上回提到,通过与或非的组合使用可以控制特定寄存器的特定某个位,接下来玩点花样。
让灯闪烁咋整?
闪烁就是:亮灯、灭灯、亮灯、灭灯……
一直闪的话,放在while(1) 循环里面就好了。
比如让PC7的灯闪烁:
while(1)
{
PORTC &= ~(1 << PC7);//亮灯
PORTC |= (1 << PC7);//灭灯
}
编译下载运行,发现啥事都没发生,程序没运行?不是,程序正在运行,而且是以一种快到肉眼看不见的速度在闪烁。
因为亮灯跟灭灯之间要加延时。
普遍的延时函数就是两层循环语句在狂跑,这里介绍一个 Atmel Studio 自带的延时函数。
首先你要包含头文件
#include <util/delay.h>
然后跟着我做一个小小的设置:
菜单栏 Project > 你的项目名字 Properties > Toolchain > AVR/GCC C Compiler
在 Defined symbols 新增一行
F_CPU=7372800UL
这里的7372800UL指的是你MCU的晶振频率,UL表示该数据是无符号长整型常量,假如你用的是8M的晶振,相应要写成
F_CPU=8000000UL
这样你就可以用自带的官方延时函数:
_delay_ms()
和
_delay_us()
顾名思义,一个是毫秒延时,一个是微秒延时。
把延时1秒(即1000毫秒)加到程序上:
while(1)
{
_delay_ms(1000);
PORTC &= ~(1 << PC7);//亮灯
_delay_ms(1000);
PORTC |= (1 << PC7);//灭灯
}
现在你就可以看到L1以1秒的间隔在一闪一闪亮晶晶了。
但目前的代码还只是玩具级别,接下来我教你用工程的思维来组建程序。
首先,像
PORTC &= ~(1 << PC7);//亮灯
这样的语句是不应该出现在主程序里面的,不但可读性不好,可移植性也不好。
既然这个程序有点灯功能,我们给项目新增一个专门的light.c文件和与之配套的light.h。
在右侧Solution Explorer的项目名称右键菜单选择Add>New Item:
在弹出的窗口选择C File类型,文件名为light.c
再Add一遍,这次文件类型选择Include File,文件名为light.h
在main.c的#include <avr/io.h>下添加一行#include "light.h"
#include <avr/io.h>
#include "light.h"
这样,main.c跟light.c就联系起来了。
接下来的步骤是:
在light.c里边写函数,在light.h里边声明函数,在main.c里边调用函数。
首先,在light.c里添加一行
#include <avr/io.h>
接下来写个亮灯的函数。函数命名一般按照主谓或者主谓宾的结构:这是什么器件、它要操作什么、它要操作的对象是什么。
在这里,一号灯,点亮,这么写:
void light1_on(void)
{
PORTC &= ~(1 << PC7);
DDRC |= (1 << PC7);
}
灭掉,这么写:
void light1_off(void)
{
PORTC |= (1 << PC7);
DDRC |= (1 << PC7);
}
接下来在light.h里写这两个函数的声明:
#ifndef LIGHT_H_
#define LIGHT_H_
void light1_on(void);
void light1_off(void);
#endif /* LIGHT_H_ */
现在main.c里边就可以直接调用了:
while(1)
{
_delay_ms(1000);
light1_on();
_delay_ms(1000);
light1_off();
}
看,我现在注释已经去掉了,但并不影响阅读,一份写得好的程序,是可以自注释的。
可读性是有了,但还不具备可移植性。
可移植性是在特定的范围下讨论的,有跨编译器的可移植性,跨芯片的可移植性,在这里,我们讨论的是同样编译器同样芯片的前提下,电路图稍微改变,如何在尽量少改动代码的前提下移植程序。
目前的light1_on和light1_off函数都是控制PC7引脚的LED,假如换成了接在PB3呢?把程序里出现的PC7全部换成PB3?
优雅的做法是:用宏定义把具体的标识符换个名字。
在light.c开头这么写:
#define LIGHT1_PORT PORTC
#define LIGHT1_DDR DDRC
#define LIGHT1 PC7
然后重写两个函数:
void light1_on(void)
{
LIGHT1_PORT &= ~(1 << LIGHT1);
LIGHT1_DDR |= (1 << LIGHT1);
}
void light1_off(void)
{
LIGHT1_PORT |= (1 << LIGHT1);
LIGHT1_DDR |= (1 << LIGHT1);
}
把这个程序移植到另一个LED接在PB3的电路板时,只需要把
#define LIGHT1_PORT PORTC
#define LIGHT1_DDR DDRC
#define LIGHT1 PC7
改成
#define LIGHT1_PORT PORTB
#define LIGHT1_DDR DDRB
#define LIGHT1 PB3
就可以了。
最后放一下我的公众号:
网友评论