AVR 通用I/O控制:以端口A为例
(1)首先对I/O口工作方式初始化,即根据需要正确设置DDRA寄存器,确定是输出方式还是输入方式。当DDRA=0xff时,A的每个端口都设置为输出方式,当DDRA=0x00时,A的每个端口都设置为输入方式。
(2)当I/O工作在输入方式时,要读取外部引脚时,应读取PINAn的值,而不是PORTAn的值。
注:DDRA是PA口的方向寄存器,在写DDRA时,就是在设定PA口的工作方式——输入口还是输出口。在读DDRA时则是在查询PA口的工作方式。
PORTA是PA口的数据寄存器,在写PORTA时,就是在控制PA的引脚数据。
PINA只允许读操作,从PINA口读入的数据反映A口引脚逻辑状态。
程序实例代码:
/************************************************************************
File name : led.c
Chip type : ATmega32
Program type : Application
AVR Core Clock frequency : 4.000000 Mhz
Memory model : Small
External RAM size : 0
Data Stack Size : 512
****************************************************************/
#include<mega32.h> //不知道为什么错了?????????
#include<ioavr.h> //该文件是编译器自带的,这个头文件功能是根据所选择的芯片型号自动选择相应的寄存器地址定义头文件
void delay(unsigned int time); //延时函数
void main()
{
unsigned char position = 0; //led点亮位置的控制
/******初始化PORTA**********************/
PORTA = 0xff; //初始化端口状态,全是1
DDRA = 0xff; //A端口全部设置为输出状态
/******流水灯点亮**********************/
while(1)
{
PORTA = ~(1<<position);
if(++position>=8)position=0;
delay(1000);
}
}
void delay(unsigned int time)
{
for(;time>0;time--);
}
引脚配置为输入时,若PORTAn为“1”,则上拉电阻使能。如果需要关闭这个上拉电阻,则可以将PORTAn清零,或者将这个引脚配置为输出。复位时各引脚为高阻态。
引脚配置为输出时,若PORTAn为“1”,引脚输出高电平,若PORTAn为“0”,引脚输出低电平。
****************************************************************************************************************************************
****************************************************************************************************************************************
****************************************************************************************************************************************
****************************************************************************************************************************************
针对AVR的开发,在不同的软件开发环境之下,会有不同的一些开发规则,本人在IAR 下进行的开发,此下都是基于IAR开发环境的。
首先在头文件的使用时,一般使用#include<iomxx.h>其中xx表示型号,eg:使用ATmega32芯片,则头文件使用iom32.h。
下面主要说说中断的一些知识:
状态寄存器SREG:(I,T,H,S,V,N,Z,C),
I:全局中断使能位,当I为1时,开启全局中断,当I为0时,关闭所有中断。
T(位复制标志),H(半进位标志),S(符号标志),V(溢出标志),N(复数标志),Z(零标志),C(进位标志)。
控制寄存器MCUCR:(SE,SM2,SM1,SM0,ISC11,ISC10,ISC01,ISC00)
ISC11 | ISC10 | 说明 |
0 | 0 | INT1为低电平时产生中断请求 |
0 | 1 | INT1引脚上任意的逻辑电平变化都将引发中断 |
1 | 0 | INT1的下降沿产生异步中断请求 |
1 | 1 | INT1的上升沿产生异步中断请求 |
如果是边沿触发或者电平变化触发,那么持续时间必须大于一个时钟周期,才能触发中断,时间不能过短,如果选择低电平触发,则低电平必须保持到当前指令的结束。
ISC01,ISC00是对INT0的控制,同样如INT1。
中断控制寄存器GICR(INT1,INT0,INT2,-,-,-,IVSEL,ICVE),前三位分别是INT1,INT0,INT2,的外部中断使能位。
中断标志寄存器GIFR(INT1,INT0,INT2,-,-,-,-),前三位分别是INT1,INT0,INT2外部中断标志位,当中断发生时自动置位,进入中断服务程序后此标志自动清零。
下面是在IAR环境下,中断服务程序的写法,
(1)在头文件iom32.h中找到中断向量(中断入口)
Eg:#define RESET_vect (0x00)
#define INT0_vect (0x04)
#define INT1_vect (0x08)
#define INT2_vect (0x0C)
(2)根据以上介绍的各种寄存器,对要使用的中断进行初始化。
(3)#pragma vector=INT0_vect //这是一个关于外部中断0的中断服务程序
__interrupt void INT0_process() //interrupt前有两个下划线
{
************************ //要实现的功能
****************
}
Eg
#include<iom32.h>
void delay_ms(unsigned int time); //延时函数
/******中断处理函数*************************/
void main()
{
unsigned char position = 0; //LED灯位置
/*******I/O初始化*******************/
PORTA = 0xff; //初始化PA口
DDRA = 0xff; //PA工作方式为输出
/********外部中断初始化**************/
GICR = 0x40; //外部中断请求0使能
MCUCR = 0x02; //INT0的下降沿产生异步中断请求
SREG = 0x80; //全局中断使能
while(1)
{
PORTA = ~(1<<position);
if(++position>=8)position = 0;
delay_ms(1000);
}
}
void delay_ms(unsigned int time)
{
for(;time>0;time--);
}
#pragma vector=INT0_vect
__interrupt void INT0_process()
{
PORTD = PORTD | 0x20; //把PD5置1,DDRD初始值是0x00,即PD端口全为输入口
delay_ms(2000); //延时2s
PORTD = PORTD & 0xdf; //解除警报
}
本函数实验用PD5检测外部中断报警,正常情况时led流水灯点亮。