1.2.2 简单的C语言程序
以一个简单的流水灯程序为例,了解一下AVR单片机C语言。
1.硬件电路
首先来看一个实例,这个例子的功能十分简单,就是让单片机的PA口的发光二极管(LED)灯按流水灯的形式进行闪烁,硬件电路如图1-4所示。
图1-4 点亮P0口LED灯电路
5V电源分别连接8只LED灯的正极,8只LED灯的负极分别连接8个1 kΩ限流电阻,然后再接到AVR单片机PB口。这样,当单片机的I/O口输出高电平时,LED两端都是高电平,LED不会导通,当I/O口输出低电平时,LED正向导通,同时也就发出光亮了。
这里解释一下LED灯上串联电阻大小的选择问题。我们知道,LED灯的工作电压为1.6~2.8V(一般为2V),工作电流为2~30mA(一般控制在4~10mA),如果系统供电VCC=5V,LED灯上串联的电阻为1kΩ,并取LED灯上电压为2V,那么,此时通过LED灯的电流则为(5V−2V)/1000Ω=3mA。如果需要提高亮度,需要增大LED灯的工作电流,当工作电流为10mA时,则此时电阻应该选择(5V−2V)/10mA=300Ω,所以,LED灯的串联电阻一般在0.3~1kΩ之间进行选择。
2.程序实现
8位流水灯源程序如下:
#include <ioavr.h> #define uint unsigned int /***************************************************************************** 函数功能:延时函数 *****************************************************************************/ void Delay_ms(uint xms) { int i,j; for(i=0;i<xms;i++) { for(j=0;j<1140;j++) ; } } /***************************************************************************** 函数功能:端口初始化函数 *****************************************************************************/ void port_init (void) { DDRA=0XFF; PORTA=0XFF; } /***************************************************************************** 函数功能:主函数 *****************************************************************************/ void main (void) { port_init(); while(1) { PORTA=0xFE; //点亮第1个LED灯 Delay_ms(500); //延时 PORTA=0xFD; //点亮第2个LED灯 Delay_ms(500); PORTA=0xFB; //点亮第3个LED灯 Delay_ms(500); PORTA=0xF7; //点亮第4个LED灯 Delay_ms(500); PORTA=0xEF; //点亮第5个LED灯 Delay_ms(500); PORTA=0xDF; //点亮第6个LED灯 Delay_ms(500); PORTA=0xBF; //点亮第7个LED灯 Delay_ms(500); PORTA=0x7F; //点亮第8个LED灯 Delay_ms(500); } }
下面对这个程序进行简要的分析:
① 程序的第1行“#include <ioavr.h>”是“文件包含”,“文件包含”是指一个文件将另外一个文件的内容全部包含进来。所以,这里的程序虽然只有几行,但C编译器(如IAR软件)在处理的时候却要处理几十行或几百行。
② Delay_ms(500)的用途是延时,由于单片机执行指令的速度很快,如果不进行延时,灯亮之后马上就灭,灭了之后马上就亮,速度太快,人眼根本无法分辨。所以,需要进行适当的延时,这里采用自定义函数Delay_ms(500)实现延时,函数前面的void表示该延时函数没有返回值。
Delay_ms(500)函数是一个自定义函数,它不是由AVR编译器提供的,即不能在任何情况下编写这样一行程序以实现延时。如果在编写其他程序时写上这么一行,会发现编译通不过。注意观察本程序会发现,在使用Delay_ms(500)之前,已对Delay_ms(int k)函数进行了事先定义,因此,在主程序中才能采用Delay_ms(500)进行使用。
注意:在延时函数Delay_ms(uint xms)定义中,参数xms称为“形式参数”(简称形参);而在调用延时函数Delay_ms(500)中,小括号里的数据“500”,这个“500”称为“实际参数”(简称实参)。参数的传递是单向的,即只能把实参的值传给形参,而不能把形参的值传给实参。另外,实参可以在一定范围内调整,这里的“500”表示延时时间为0.5s,若为“1000”,则延时时间是1000ms,即1s。
③ AVR单片机的I/O口是标准的I/O口,I/O口的功能是负责实现CPU通过系统总线把I/O电路和外围设备联系在一起,标准的I/O口具有输入、输出、高阻3种状态,AVR单片机通过3个寄存器来控制I/O口的状态:输入和输出方向寄存器DDRx(x表示端口号,如DDRA表示端口A的方向寄存器)、输出寄存器PORTx、输入寄存器PINx。
在程序中,void port_init(void)是端口初始化函数,有两个语句:
· DDRB = 0xFF;的含义就是设置端口A为输出口。
· PORTA= 0xFF;的含义就是将端口A的输出寄存器设置为0xFF,即让端口A的8个I/O口全部输出高电平。
④ 在单片机程序中,首先在main主函数里对端口进行初始化,然后让程序进入一个while(1){}死循环中,这样保证程序一直运行。程序都是一步一步向下执行的,执行到程序的结尾就会停止,这时即使外界再有什么动作,单片机也不再响应了,加上死循环,那么程序就会一直在这个循环体中运行。如果在这个循环体中进行相应操作,程序就会很快检测到并给出响应。
在本例中,while(1){}死循环的功能轮流点亮PORTA口LED灯,使PORTA口的LED灯流动显示。