LED流水灯的开发在51单片机中很常见,主要是让大家掌握IO的操作是单片机控制的最基本要求。 根据开发流程,查看选定的单片机资源和控制寄存器后,在软件上实现控制。
这里芯片采用STC15W404AS作为开发的硬件平台,这里用该单片机的P1引脚驱动LED实现流水灯的功能。
图中显示的是连接到LED的硬件电路,8个LED的阳极通过限流电阻共同连接到VCC。 也就是说,LED阴极为高电平时LED熄灭,相反,LED阴极为低电平时LED点亮。 另外,由于限流电阻为510欧姆,电源VCC的电压为5V,红色LED的电压为1.9V左右,所以LED点亮。有了上述参数,我们可以有目的地查看单片机文档。
从文档中可以看到,P1端口可以设置为I/O (输入/输出)模式、输入模式和输出模式。 另外,每个模式所能承受的电流大小也不同。 这里根据电路的连接方法不同,需要输入模式,可以看出输入电流最大为3mA左右。 因此,根据手册的说明,将P1端口设定为准双向端口即可。 当然,在想将单片机的端口用作Leet端口的情况下,将对应于P1端口的控制寄存器P1M1和P1M0的位(对应端口)设定为0即可。 也就是说,P1M1=0x00。 P1M0=0x00;
设定P1的控制位后,可以通过直接代入P1来控制LED的点亮和熄灭。 其对应关系为P1=~0x01,最初点亮; ~0x02第二亮度……由此可见,实现流水功能有各种各样的方法。
1 .排列方法
根据以前的结果类推,可以创建表{0x01、0x02、0x04、0x08、0x10、0x20、0x40、0x80}。 这样,只需选择表中的1位并反向代入P1即可控制对应灯的闪烁,因此将该表排列成循环,实现LED的流水功能。 其步骤如下
#包含
unsignedcharcodeled [ ]={0x 01,0x 02,0x 04,0x 08,0x 10,0x 20 };
void delay () )
{
无符号输入I;
for(I=0; I
}
void IO_Config (
{
P1M0=0x00; 设定//p1端口的类型
P1M1=0x00; //可以通过电路进行修正
}
Void主() )
{
无符号char j;
IO_Config (;
while(1)。
{
for(j=0; Jj
{
P1=~LED[j];
延迟(;
}
}
}
复制代码
我们选择的这个芯片P1引脚只有6个(P1.0~P1.5 ),所以我们只设定了6个值。 对于8个东西,将数组扩展到8位即可,编译后可以下载到单片机进行调试
2 .班次的方法
c语言具有“左移”功能。 这也是单片机中经常使用的。 值得注意的是,数据移动时会被0填充。 请充分注意这个。 因此,上述程序可以简化为以下结构:
#包含
//unsignedcharcodeled [ ]={0x 01,0x 02,0x 04,0x 08,0x 10,0x 20 };
void delay () )
{
无符号输入I;
for(I=0; I
}
void IO_Config (
{
P1M0=0x00; 设定//p1端口的类型
P1M1=0x00; //可以通过电路进行修正
}
Void主() )
{
无符号char j,temp;
IO_Config (;
while(1)。
{
temp=0xfe;
for(j=0; Jj
{
//P1=~LED[j];
P1=temp;
temp=(temp
延迟(;
}
}
}
复制代码
同样,可以修改和编译程序,将生成的HEX文件写入单片机,然后进行调试
3 .循环移位法
从上面可以看出,用c语言的移位方法移动时会自动用0填充。 这样,必须以后或的方法补充原始数据。 我们知道汇编语言有RR和rl(rotateright和rotateleft )移位方法。 这种想法的优点是,移动中的数据是圆形位移,所以不需要填充,但这种想法只是将ACC位移,我们可以
实现这个函数怎么办呢,第一种方法就是在C语言中嵌入汇编通过# pragma asm和# pragma endasm来实现嵌入汇编,格式如下:……
#pragma asm
RL A
……
#pragma end asm
……
当 然在这里这不是我们要讲的重点,我们要说的是第二种方法,那么我们是不是能够编写一个和RL,RR功能一样的函数呢?其实完全没有必要,这些函数都在 intrins头文件中,根据前面几章介绍的知识,我们只需要在文件中加入这个头文件就可以用了,那么这个函数是怎么个形式呢?我们就看看这个函数吧。
externvoid _nop_ (void);
externbit _testbit_ (bit);
externunsigned char _cror_ (unsigned char,unsigned char);
externunsigned int _iror_ (unsigned int, unsigned char);
externunsigned long _lror_ (unsigned long,unsigned char);
externunsigned char _crol_ (unsigned char,unsigned char);
externunsigned int _irol_ (unsigned int, unsigned char);
externunsigned long _lrol_ (unsigned long,unsigned char);
externunsigned char _chkfloat_(float);
externvoid _push_ (unsigned char _sfr);
externvoid _pop_ (unsigned char _sfr);
以上界函数是在intrins头文件中的函数,这些函数的源码keil封装了起来,只能看到函数的接口,其具体用法在我的附件中有说明,在这里我们需要的 就是_crol_字符循环左移的指令,这个函数有两个入口参数分别是要移动的数据和移动的位数着这里我们移动的数据是0xfe,移动的位数为j(和所在的 位有关)位,因此我们可以将上面的程序改写:
#include
#include
//unsigned char code LED[]={0x01,0x02,0x04,0x08,0x10,0x20};
void delay()
{
unsigned int i;
for(i=0;i
}
void IO_Config()
{
P1M0=0x00;//设置P1端口的类型
P1M1=0x00;//可以根据不同的电路来修改
}
void main()
{
unsigned char j;
IO_Config();
while(1)
{
for(j=0;j
{
//P1=~LED[j];//第一种方法
//P1=temp; //第二种方法
//temp=(temp
P1=_crol_(0xfe,j);//第三种方法
delay();
}
}
}
可以看到其时序和前面的都一样。
最后总结一下,在这个小设计的开发过程中我们主要根据前面的开发流程加深了对单片机的开发,首先设计硬件电路根据硬件电路来将参数提取出来,再根据所选单片 机手册来选择相应的功能,最后根据要求来编写程序,再编程过程中善于利用已经编写好的源码来是实现自己的功能,如果现成的源码不能满足自己的要求就根据自 己的需求来编写相应的程序。