六、控制第一个外设--LED灯
1、单片机编程的一般步骤
a、目标分析:我们的目标是带你两开发板上的LED灯。
b、原理图分析
(1)目标器件(LED)工作原理
(2)相关模块电路连接
(3)控制线路分析:相关IO端口是那些?
c、代码编写、编译
d、下载与调试
2、原理图与控制方法分析
a、原理图概况浏览
(1)目的1:把原理图中相关部分摘录出来重点看
(2)目的2:把相关部分的原理图关键部分(74HC573芯片)重点看
b、74HC573芯片介绍
(1)要知道这个芯片怎么工作就要看它的数据手册
(2)第一步:找到这个芯片的数据手册
(3)OE(output enable)、IE(input enable)
(4)74HC573是一个锁存器,简单来说就是由输入引脚Dx来控制输出引脚Qx(x = 0~7)。OE和IE影响Dx控制Qx
的结果。
(5)当OE为L,LE为L的时候74HC573工作在锁存模式,此时Qx的输出被锁存(进入锁存模式前的状态是高电平就一
直是高电平,是低电平就一直是低电平,这就是锁存),这就是锁存器的作用。
(6)当OE为L,LE为H时74HC573工作在跟随模式。此时Qx的输出跟随Dx的输入变化而变化。此时逻辑上可以认为
74HC573是透明的。
c、硬件原理图分析结论
(1)原理图开发板默认跳线帽的方式为:OE = L,LE = H,此时74HC573工作在跟随模式下,相当于是透明的。当
我们接线(接线方式为P0端口和J12用杜邦线连接起来,当然其实也可以和P1或者别的其他端口连接)后,其实
相当于P0端口和LED正极直接相连了,而LED的负极接地了。
(2)结论是:P0端口的8个引脚输出高电平时LED则亮;输出低电平时则LED灭。
3、第一个实验:点亮LED
a、创建工程和文件
(1)创建空工程
(2)创建main.c并关联到工程
(3)模式化书写:头文件包含与main函数外壳
b、如何用C语言进行IO端口控制
(1)P0这个符号在C语言代码中的特殊含义
(2)十六进制数
(3)位、字节、IO端口、IO口
(4)硬件连接方式对编程的影响
c、点亮LED
(1)点亮八颗LED
(2)点亮四颗LED
(3)隔一颗点亮一颗LED
(4)全部熄灭八颗LED
(5)实现以上四个功能的代码:
#include <REGX51.H> //头文件包含
void main(void) //main函数
{
while(1)
{
//P0 = 0xff; //P0端口的八个引脚都输出高电平
//P0 = 0xf0; //P0端口的前四个引脚都输出高电平
P0 = 0xaa; //P0端口每隔一个引脚是高电平
//P0 = 0x00; //P0端口的八个引脚都输出低电平
}
}
4、如何让LED闪烁
a、闪烁的本质含义:亮-延时-灭-延时-亮
(1)由于人眼睛有视觉暂留,所以亮和灭之间的延时时间选择非常重要
(2)如果延时时间太短(譬如20ms),这时候上一次灭眼睛还没有反应过来就到了下一次亮,此时你会觉得灯是一直
亮,此时你会觉得等是一直亮着的。和一直亮着的区别是亮度低一些。但是看不到闪烁。
(3)延时时间逐渐增大慢慢就能看到闪烁,大到一定程度就可以看到明显的一两一灭了。
b、单片机如何实现延时
(1)单片机执行一条语句要一定的时间
(2)单片机开机后不能休息
(3)单片机可以“空转”来“浪费时间”
c、C语言是实现延时
(1)笨办法:放多条指令实现延时
(2)正确方法:使用C语言循环语句实现延时
要求:
(1)能够用C语言编写一个delay函数,实现一段时间的延时,从而实现LED闪烁即可。
(2)改变i和j的数值,去实际观察效果然后思考为什么。
d、LED闪烁的实现代码:
#include <REGX51.H>
void delay(unsigned int t) //delay(1);延时1毫秒,晶振:12.000MHz
{
while(t--)
{ //执行一次循环体为1毫秒,传入的参数为几就循环几毫秒
unsigned char i = 2;
unsigned char j = 239;
do
{
while (--j);
} while (--i);
}
}
void main(void)
{
P0 = 0xff; //全亮
//延时
delay(100);
P0 = 0x00; //全灭
//延时
delay(100);
}
5、C语言循环语句学习
a、while循环
(1)格式:
while (条件)
{
//循环体, 若干条代码
}
(2)例程:
void delay(unsigned int t)
{
while (t--)
{
unsigned char i = 2;
unsigned char j = 239;
while (i--)
{
while (j--);
}
}
}
b、for循环
(1)格式:
for (循环初始化;循环条件;循环增量)
{
循环体
}
(2)例程:
void delay(void)
{
int i;
for (i=100; i<0; i--)
{
for (i=100; j>0; j--)
{
;
}
}
}
c、do-while循环
6、流水灯效果实现1
a、什么是流水灯:D1~D8LED灯依次亮起,循环这个过程。
b、流水灯本质分析
(1)流水灯的本质其实就是不同的亮的状态相互间隔一定时间
(2)流水灯的第一个状态:0x80(1000 0000);
第二个状态:0x40(0100 0000);
第三个状态:0x20(0010 0000);
第四个状态:0x10(0001 0000);
第五个状态:0x08(0000 1000);
第六个状态:0x04(0000 0100);
第七个状态:0x02(0000 0010);
第八个状态:0x01(0000 0001);
(3)延时时间越长流水灯就流的越慢,越短就流水越快。
(4)八个状态依次执行完之后,从头开始,其实就是循环。
c、编程实现流水灯
(1)分析发现流水灯和闪烁没有什么区别。
(2)实现代码:
#include <REGX51.H>
//依次流水灯
//unsigned char num[15]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x10,0x20,0x40};
//两边靠拢分开流水灯
//unsigned char num1[7]={0x81,0x42,0x24,0x18,0x24,0x42,0x81};
void delay(unsigned int t)
{
while(t--)
{
unsigned char i = 2;
unsigned char j = 239;
do
{
while(--j);
}while(--i);
}
}
void main(void)
{
int i;
while(1)
{
for(i=0; i<7; i++)
{
P0 = num1[i];
delay(500);
}
}
}
7、流水灯效果实现2
a、上节课程序的缺陷分析(老师上节课演示的是将每一个状态都用P0去输出,本人用数组相对来说简便一些)
(1)功能实现了,但是写的太笨了,如果状态很多程序会很长编写程序就会困难。
(2)如果要修改流水灯的效果会很繁琐。
(3)用数组来实现时,一次数组遍历玩进入下一轮遍历的时候会有一次卡顿,观感不好,有待优化。
b、改进思路:通过变量运算来得到合适的端口输出值
(1)首先定义变量unsigned char a,初值为0x80(1000 0000)
(2)每个状态就是a依次右移0~7位
(3)我们这样就相当于用C语言让单片机自己运算得到每个状态的端口值,而不用自己事先去计算好。这样就简化了
程序。
c、C语言位移运算介绍
(1)C语言中有位移运算符,>>可以右移,<<可以左移。(无符号数移位:移出去的位丢掉了,空出来的位用0补)
d、编程实现:
#include <REGX51.H>
void delay(unsigned int t)
{
while(t--)
{
unsigned char i = 2;
unsigned char j = 239;
do
{
while(--j);
}while(--i);
}
}
//通过位移实现左右跑流水灯
void main(void)
{
unsigned char LED = 0x01;
int i;
while(1)
{
for(i=0; i<7; i++)
{
P0 = LED ;
LED = LED << 1;
delay(100);
}
for(i=0; i<7; i++)
{
P0 = LED;
LED = LED >> 1;
delay(100);
}
}
}
8、跑马灯效果实现
a、什么是跑马灯
跑马灯是和流水灯非常像:流水灯是七颗LED灭一颗亮,亮的在移动;而跑马灯是七颗亮,一颗灭,灭的再移动
b、实现思路分析
第一种状态:0111 1111 0x7f
第二种状态:1011 1111 0xbf
第三种状态:1101 1111 0xdf
第四种状态:1110 1111 0xef
第五种状态:1111 0111 0xf7
第六种状态:1111 1011 0xfb
第七种状态:1111 1101 0xfd
第八种状态:1111 1110 0xfe
c、编程实现
(1)编程实现跑马灯要用两种C语言技巧:左移(右移)、按位取反
(2)C语言中“~”代表对一个数字按位取反,譬如0111 1111按位取反后变成1000 0000。而1101 1111按位取反
后变成0010 0000。