LED闪烁,说白了就是点亮之后,延时,熄灭,点亮,然后一直反复就行。
但是有一个需要注意的点,就是延时的时间需要把握好。如果延时时间太短,我们就看不到LED的闪烁,只是看到一个微弱是小灯,如果延时时间太长,我们看到灯熄灭时候,再点亮的间隙太长。
实现LED闪烁
利用while语句实现延时
代码
我们学习单片机,基本都是从一个自己写的延时函数开始。代码如下
void main(void) { //注意,因为MSP430是16位的编译环境,所以这里的 unsigned int只有2个字节,16个bit,又是无符号的,所以范围是0-65535 volatile unsigned int uint16_delay; WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer //P4.7为输出 P4DIR=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出,P4其他脚为输入 while(1) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 //延时 uint16_delay =50000; while(uint16_delay--); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 uint16_delay =50000; while(uint16_delay--); } }
这里只有一个注意的点,就是必须写volatile(不明白的点链接),这个防止编译器优化。如果被编译器优化了之后,延时将会不准确,甚至可能没有现象。
关于编译器优化
如果我们觉得写volatile很麻烦,总是忘记写了。怎么办呢?
我们可以自己设置,鼠标靠近工程——>右键——>properties——>build——>MSP430 Compiler——>Optimizition——>Optimizition level(--opt_level,-O)——>选择off——>Apply and Close。
利用for语句实现延时
代码
这个也是官方的视频教程里面的代码,他代码里面没有volatile,可能是for循环里面步骤,CCS编译器不会进行优化。
void main(void) { //注意,因为MSP430是16位的编译环境,所以这里的 unsigned int只有2个字节,16个bit,又是无符号的,所以范围是0-65535 unsigned int uint16_delay; WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer //P4.7为输出 P4DIR=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出,P4其他脚为输入 while(1) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 for(uint16_delay=0;uint16_delay<50000;uint16_delay++); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 for(uint16_delay=0;uint16_delay<50000;uint16_delay++); } }
精准延时
代码
#include <msp430.h> #define CPU_F ((double)1000000) #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0)) /** * main.c */ void main(void) { WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer //P4.7为输出 P4DIR=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出,P4其他脚为输入 while(1) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 delay_ms(1000); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 delay_ms(1000); } }
如上为MSP430F5529的精准延时。这个里面我们只需要修改#define CPU_F ((double)1000000)这一行即可。如果系统主时钟(MCLK)是多少,(double)后写多少。因为默认情况下,MSP430F5529的主时钟为1Mhz。所以此处写成1000000。
简单方法获取开发板主频
如果不知道我们当前板子的系统时钟是多少,可以尝试写入如上代码。(1)然后将 delay_ms(1000);改为 delay_ms(5000);。(2)然后打开手机计时器,和下载程序到开发板中。(3)在运行程序的瞬间,按下手机计时器开始计时。(4)如果手机是5S左右,那么表示当前开发板时钟是1MHZ。如果是5MHZ,那么手机的时间应该是1S左右。计算方法如下
实际主频= (程序定时时间 / 手机计时器时间 )* 当前程序设置主频
将手机计时器时间5s,程序定时时间5s,当前程序设置1MHZ(1MHZ=1000000HZ)带入。可以得到实际主频为1MHZ。
将手机计时器时间1s,程序定时时间5s,当前程序设置1MHZ(1MHZ=1000000HZ)带入。可以得到实际主频为5MHZ。
修改主频
假设我们现在有一个板子的主频是5MHZ,那么这样写
//主频为1MHZ的板子 #define CPU_F ((double)1000000) #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0)) //主频为5MHZ的板子 #define CPU_F ((double)5000000) #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
使用示范
delay_us(1); //延时1微秒 delay_ms(1); //延时1毫秒 delay_us(4.5); //延时4.5微秒 delay_ms(4.5); //延时4.5毫秒 delay_us(1000000); //延时1秒 delay_ms(1000); //延时1秒
实现呼吸灯
采用精准定时的方法实现呼吸灯
因为我们这个for和while的延时多多少少还是有点不太准确,所以一开始我采用精准延时。
void main(void) { long delay_time; WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer //P4.7为输出 P4DIR=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出,P4其他脚为输入 while(1) { for(delay_time=0;delay_time<500;delay_time++) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 delay_ms(delay_time); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 delay_ms(1000-delay_time); } } }
后面我一编译就开始报错,expected an integer constant。
后面查找了相关资料发现,delay_ms();的延时只能是传入常量,像delay_time这种变量是无法编译通过。
delay_ms(1000); //传入的是1000,这是个常量。可以编译成功 delay_ms(delay_time); //传入的是delay_time,这是个变量,编译失败
采用for语句实现呼吸灯
因为for语句整体写起来是在同一行,更加容易理解。所以我采用for语句进行延时
#include <msp430.h> #define LED_all_time 200 //这里是为了方便修改呼吸灯的周期 /** * main.c */ void delay(volatile int delay_time) { volatile unsigned int uint16_delay; while(delay_time--) { for(uint16_delay=0;uint16_delay<5;uint16_delay++); } } void main(void) { volatile long delay_time; WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer //P4.7为输出 P4DIR=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出,P4其他脚为输入 while(1) { for(delay_time=0;delay_time<LED_all_time;delay_time++) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 delay(delay_time); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 delay(LED_all_time-delay_time); } for(delay_time=LED_all_time;delay_time>0;delay_time--) { //P4.7输出高电平 P4OUT=0x80; //0x80=1000 0000,查看数据手册可知,P4.7为输出高电平,P4其他脚为下拉输入 delay(delay_time); P4OUT=0x00; //0x80=0000 0000,查看数据手册可知,P4.7为输出低电平,P4其他脚为下拉输入 delay(LED_all_time-delay_time); } } }