深圳市华胄科技有限公司 >> MCU专题 >> 单片机教程


AVR 单片机与GCC 编程----之三



提交者 阳光总在风雨后  在  2008-6-12 13:33:56 

上一篇 下一篇
第三章 功能模块编程示例


3.1 中断服务程序


avr-gcc 为中断提供缺省的入口例程,这些例程的名字已固定,用户可通过重载这些例程来处理中断。如果中断没有被用户重载,说明正常情况下不会产生该中断,缺省的中断例程将程序引导到0 地址处(既复位)。


Avr-gcc 为重载中断例程提供两个宏来解决细节的问题,它们是 SIGNAL(signame)和INTERRUPT(signame)。参数signame 为中断名称,它的定义在io.h 中包含。表3-1 列出了ATMega8 的signame 定义,其它器件的signame 定义可查阅相应的ioxxxx.h 文件。


表3-1 ATMega8 中断名称定义


signame 中 断 类 型
SIG_INTERRUPT0 外部中断INT0
SIG_INTERRUPT1 外部中断INT1
SIG_OUTPUT_COMPARE2 定时器/计数器比较匹配中断
SIG_OVERFLOW2 定时器/计数器2 溢出中断
SIG_INPUT_CAPTURE1 定时器/计数器2 输入捕获中断
SIG_OUTPUT_COMPARE1A 定时器/计数器1 比较匹配A
SIG_OUTPUT_COMPARE1B 定时器/计数器1 比较匹配B
SIG_OVERFLOW1 定时器/计数器1 溢出中断
SIG_OVERFLOW0 定时器/计数器0 溢出中断
SIG_SPI SPI 操作完成中断
SIG_UART_RECV USART 接收完成
SIG_UART_DATA USART 寄存器空
SIG_UART_TRANS USART 发送完成
SIG_ADC ADC转换完成
SIG_EEPROM_READY E2PROM 准备就绪
SIG_COMPARATOR 模拟比较器中断
SIG_2WIRE_SERIAL TWI 中断
SIG_SPM_READY 写程序存储器准备好


以下是个外部中断0 的重载示例:
#include
#include
#include
SIGNAL(SIG_INTERRUPT0)
{
//中断处理程序
}
宏INTERRUPT 的用法与SIGNAL 类似,区别在于SIGNAL 执行时全局中断触发位被清除、其他中断被禁止,INTERRUPT 执行时全局中断触发位被置位、其他中断可嵌套执行。


另外avr-libc 提供两个API 函数用于置位和清零全局中断触发位,它们分别是:
void sei(void) 和 void cli(void)。


3.2 定时器/计数器应用


下面以定时器/计数器0 为例,说明定时器计数器的两种操作模式
定时器/计数器0 相关寄存器:
TCCR0 :定时器/计数器0 控制寄存器
计数使能,时钟源选择和CPU 时钟预分频设置
TCNT0 :定时器/计数器0 计数值寄存器
包含计数值 (0~255)
TIFR :定时器中断标志寄存器(Timer Interrupt Flag Register)
TOV0 位 为定时器/寄存器0 溢出标志
TIMSK :定时器中断屏蔽寄存器(Timer Interrupt Mask Register)
TOIE0 位为定时器/寄存器0 中断使能/禁止控制位
查询模式举例:
/* mcu:AT90S2313 时钟:4MHz */
#include
#define uchar unsigned char
#define SET_LED PORTD&=0XEF //PD4 接发光管
#define CLR_LED PORTD|=0X10
int main(void)
{
uchar i,j=0;
DDRD=0X10;
PORTD=0X10;
TCNT0=0; // T/C0 开始值
TCCR0=5; // 预分频 ck/1024 ,计数允许
while(1)
{
//查询定时器方式等待一秒
//4000000 /1024 /256 /15 ≈ 1Hz
for(i=0;i<15;i++)
{
loop_until_bit_is_set(TIFR,TOV0);
sbi(TIFR,TOV0);//写入逻辑1 清零TOV0 位
}
if(j) //反向LED 控制脚
SET_LED,j=0;
else
CLR_LED,j=1;
}
}
中断模式举例:
/* mcu:AT90S2313 时钟:4MHz */
#include
#include
#include
#define uchar unsigned char
#define SET_LED PORTD&=0XEF //PD4 接发光管
#define CLR_LED PORTD|=0X10
static uchar g_bCount=0; //中断计数器
static uchar g_bDirection=0;
//T/C0 中断例程
SIGNAL(SIG_OVERFLOW0)
{
// 产生中断周期 T = 256 * 1024 / 4MHz
if(++g_bCount >14) //中断15 次约一秒
{
if(g_bDirection) //反向LED 控制脚
SET_LED,g_bDirection=0;
else
CLR_LED,g_bDirection=1;
g_bCount=0;
}
}
int main(void)
{
DDRD=0X10;
PORTD=0X10;
TCNT0=0; // T/C0 开始值
TCCR0=5; // 预分频 ck/1024 ,计数允许
TIMSK=_BV(TOIE0);
sei();
while(1);
}


3.3 看门狗应用


avr-libc 提供三个API 支持对器件内部Watchdog 的操作,它们分别是:
wdt_reset() // Watchdog 复位
wdt_enable(timeout) // Watchdog 使能
wdt_disable() // Watchdog 禁止


调用上述函数前要包含头文件 wdt.h ,wdt.h 中还定义Watchdog 定时器超时符号常量,它们用于为wdt_enable 函数提供timeout 值。符号常量分别如下:


符号常量 值含意
WDTO_15MS Watchdog 定时器15 毫秒超时
WDTO_30MS Watchdog 定时器30 毫秒超时
WDTO_60MS Watchdog 定时器60 毫秒超时
WDTO_120MS Watchdog 定时器120 毫秒超时
WDTO_250MS Watchdog 定时器250 毫秒超时
WDTO_500MS Watchdog 定时器500 毫秒超时
WDTO_1S Watchdog 定时器1 秒超时
WDTO_2S Watchdog 定时器2 秒超时


Watchdog 测试程序:
/* mcu:AT90S2313 时钟:4MHz */
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define CLR_LED PORTD&=0XEF //PD4 接发光管
#define SET_LED PORTD|=0X10
//误差不会太大的延时1ms 函数
void DelayMs(uint ms)
{
uint i;
for(i=0;i_delay_loop_2(4 *250);
}
int main(void)
{
DDRD=0X10;
PORTD=0X10; //SET_LED
wdt_enable(WDTO_1S);
wdt_reset();
DelayMs(500);
CLR_LED;
DelayMs(5000);//等待WDT 复位
SET_LED;
while(1)
wdt_reset();
}
执行结果:
接在PD4 脚下的LED 不断的闪烁,证明了Watchdog 使mcu 不断的复位。


3.4 UART 应用


查询方式:
/* mcu:AT90S2313 时钟:4MHz */
#include
#define uchar unsigned char
#define uint unsigned int
//uart 发送一字节数据
void putc(uchar c)
{
loop_until_bit_is_set(UCR,UDRE);
UDR=c;
}
//uart 等待并接收一字节数据
uchar getc(void)
{
loop_until_bit_is_set(UCR,RXC);
return UDR;
}
int main(void)
{
//uart 初始化
UCR=(1<UBRR=25; //baud=9600 UBRR=CK/(baud*16) -1
while(1)
{
putc(getc());
}
}


程序从UART 等待接收一字节,接收到数据后立即将数据又从UART 发送回去。
中断方式:
/* mcu:AT90S2313 时钟:4MHz */
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
uchar g_bTxdPos=0; //发送定位计数器
uchar g_bTxdLen=0; //等待发送字节数
uchar g_bRxdPos=0; //接收定位计数器
uchar g_bRxdLen=0; //等待接收字节数
uchar g_aSendBuf[16]; //发送数据绶冲区
uchar g_aRecvBuf[16]; //接收数据缓冲区
//接收中断
SIGNAL(SIG_UART_RECV)
{
uchar c=UDR;
if(g_bRxdLen>0)
{
g_aRecvBuf[g_bRxdPos++]=c;
g_bRxdLen--;
}
}
//发送中断
SIGNAL (SIG_UART_TRANS)
{
if(--g_bTxdLen>0)
UDR=g_aSendBuf[++g_bTxdPos];
}
//是否接收完成
uchar IsRecvComplete(void)
{
return g_bRxdLen==0;
}
//从发送缓冲区发送指定长度数据
void SendToUart(uchar size)
{
g_bTxdPos=0;
g_bTxdLen=size;
UDR=g_aSendBuf[0];
while(g_bTxdLen>0);
}
//接收指定长度数据到接收缓冲区
void RecvFromUart(uchar size,uchar bwait)
{
g_bRxdPos=0;
g_bRxdLen=size;
if(bwait)
while(g_bRxdLen>0);
}
int main( void )
{
uchar i;
//uart 初始化
//接收使能、发送使能、接收中断允许、发送中断允许
UCR=(1<UBRR=25; // baud=9600 UBRR=CK/(baud*16) -1
sei();//总中断允许
while(1)
{
//异步接收16 字节数据
RecvFromUart(16,0);
//等待接收完成
while(!IsRecvComplete());
//将接收到的数据复制到发送缓冲区
for(i=0;i<16;i++)
g_aSendBuf[i]=g_aRecvBuf[i];
//发送回接收到的数据
SendToUart(16);
}
}


利用中断可实现数据的异步发送和接收,正如上面程序所示,调用RecvFromUart 后主程序可处理其它任务,在执行其它任务时可调用IsRecvComplete 检测是否接收完成。


3.5 PWM 功能编程


/*
avr-libc PWM 测试程序
mcu:at90S2313
时钟:4MHz
*/
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define FREQ 4
//延时
void DelayMs(uint ms)
{
uint i;
for(i=0;i_delay_loop_2(FREQ * 250);
}
int main (void)
{
uchar direction=1;
uchar pwm=0;
// 8 位PWM 模式 , 向上计数时匹配清除OC1
TCCR1A = _BV (PWM10) | _BV (COM1A1);
//PWM 引脚PB3 方向设置为输出
DDRB= _BV (PB3);
//启动PWM 时钟源:CK/8 PWM 频率为 4MHz/8/512=976Hz
TCCR1B = _BV (CS11);
//循环改变PWM 输出脉宽,使接在OC1 引脚上的发光管亮度发生变化
while(1)
{
if(direction)
{
if(++pwm==254)
direction=0;
}
else
{
if(--pwm==0)
direction=1;
}
OCR1=pwm;
DelayMs(10);
}
return 0;
}


3.6 模拟比较器


/*
模拟比较器测试程序
mcu:ATMega8
时钟:内部4MHz RC 振荡器
*/
#include
#include
#include
#define uchar unsigned char
#define SET_RED_LED PORTB&=0XFD //PB1 接红色发光管
#define CLR_RED_LED PORTB|=0X02
#define SET_YEL_LED PORTB&=0XFE //PB0 接黄色发光管
#define CLR_YEL_LED PORTB|=0X01
//模拟比较器中断函数
SIGNAL(SIG_COMPARATOR)
{
if(ACSR & _BV(ACO))
{
SET_YEL_LED;
CLR_RED_LED;
}
else
{
CLR_YEL_LED;
SET_RED_LED;
}
}
int main(void)
{
DDRB=0X03;
PORTB=0X03;
//模拟比较器上下均触发中断 ACIS1=ACIS0=0
//中断允许 ACIE=1
ACSR=_BV(ACIE);
sei();
//AIN0:正极 AIN1:负极 AIN0 脚上的电压高于AIN1 上电压时AC0=1
if(ACSR & _BV(ACO))
{
SET_YEL_LED;
CLR_RED_LED;
}
else
{
CLR_YEL_LED;
SET_RED_LED;
}
while(1);
}
以上程序实现了用LED 指示ATMega8 比较输入引脚 AIN0 和AIN1 上的电压的高低状态。


3.7 A/D 转换模块编程


查询方式:
/* 查询方式 A/D 转换测试程序 mcu:atmega8 时钟:4MHz 外部晶振 */
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
static uint g_aAdValue[8]; //A/D 转换缓冲区
void IoInit(void);
uint AdcConvert(void)
{
uchar i;
uint ret;
uchar max_id,min_id,max_value,min_value;
ADMUX=0Xc0;//内部2.56V 参考电压,0 通道
ADCSRA=_BV(ADEN);//使能ADC,单次转换模式
//连续转换8 次
for(i=0;i<8;i++)
{
ADCSRA|=_BV(ADSC);
_delay_loop_1(60);
while(ADCSRA&_BV(ADSC))
_delay_loop_1(60);
ret=ADCL;
ret|=(uint)(ADCH<<8);
g_aAdValue[i]=ret;
}
ret=0;
for(i=1;i<8;i++)
ret+=g_aAdValue[i];
//找到最大和最小值索引
ret/=7;
max_id=min_id=1;
max_value=min_value=0;
for(i=1;i<8;i++)
{
if(g_aAdValue[i]>ret)
{
if(g_aAdValue[i]-ret>max_value)
{
max_value=g_aAdValue[i]-ret;
max_id=i;
}
}
else
{
if(ret-g_aAdValue[i]>min_value)
{
min_value=ret-g_aAdValue[i];
min_id=i;
}
}
}
//去掉第一个和最大最小值后的平均值
ret=0;
for(i=1;i<8;i++)
{
if((i!=min_id)&&(i!=max_id))
ret+=g_aAdValue[i];
}
if(min_id!=max_id)
ret/=5;
else
ret/=6;
ADCSRA=0;//关闭ADC
return ret;
}
int main(void)
{
uchar i;
IoInit();
while(1)
{
scanf("%c",&i);
if(i=='c')
printf("%d\n",AdcConvert());
}
}


中断方式:
/* 中断方式 A/D 转换测试程序 mcu:atmega8 时钟:4MHz 外部晶振 */
#include
#include
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
static uint g_nAdValue=0;
void IoInit(void);
// A/D 转换完成中断
SIGNAL(SIG_ADC)
{
g_nAdValue=ADCL;
g_nAdValue|=(uint)(ADCH<<8);
}
int main(void)
{
uchar i;
//内部2.56V 参考电压,0 通道
ADMUX=0Xc0;
//使能ADC,中断允许,自由模式, 时钟:ck/8
ADCSRA=_BV(ADEN)|_BV(ADIE)|_BV(ADFR)|_BV(ADPS1)|_BV(ADPS0);
IoInit();//标准输入/输出初始化
ADCSRA|=_BV(ADSC);//自由模式开始转换
while(1)
{
//延时
for(i=0;i<100;i++)
_delay_loop_2(4 * 250 * 10);//10ms
cli();
printf("%d\n",g_nAdValue);
sei();
}
}


以上是ATMega8 A/D 转换程序的两种方式,在第一种查询方式中ADC 按单次转换模式工作,每次转换均由置位ADSC 触发。在中断方式示例中ADC 按自由模式工作,自第一次置位ADSC 起ADC 就连续不断的进行采样转换、进行数据更新。


这两段程序的执行结果可由串行口监测工具PrintMonitor (第四章 祥细介绍) 观测。



单片机教程,五系列(55讲)电子书全集下载

论坛精选:
单片机教程,MCS51单片机从零开始 第一讲:初识单片机
http://bbs.huazhoucn.com/Topic.aspx?id=2539
单片机教程,MCS51单片机从零开始 第二讲:MCS-51单片机简述
http://bbs.huazhoucn.com/Topic.aspx?id=2540
单片机教程,MCS51单片机从零开始 第三讲:单片机相关常用名词解释
http://bbs.huazhoucn.com/Topic.aspx?id=2541
单片机教程,MCS51单片机从零开始 第四讲:计算机中数的表示及运算
http://bbs.huazhoucn.com/Topic.aspx?id=2542
单片机教程,MCS51单片机从零开始 第五讲:常用逻辑电路
http://bbs.huazhoucn.com/Topic.aspx?id=2544
单片机教程,MCS51单片机从零开始 第六讲:51单片机的结构及其组成
http://bbs.huazhoucn.com/Topic.aspx?id=2545
单片机教程,MCS51单片机从零开始 第七讲:51单片机的引脚
http://bbs.huazhoucn.com/Topic.aspx?id=2546
单片机教程,MCS51单片机从零开始 第八讲:8051单片机I/O引脚工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2547
单片机教程,MCS51单片机从零开始 第九讲:8051单片机的存储器结构
http://bbs.huazhoucn.com/Topic.aspx?id=2548
单片机教程,MCS51单片机从零开始 第十讲:编码及译码器工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2549
单片机教程,MCS51单片机从零开始 第十一讲:存储器的存储原理
http://bbs.huazhoucn.com/Topic.aspx?id=2550
单片机教程,MCS51单片机从零开始 第十二讲:51单片机的特殊功能寄存器
http://bbs.huazhoucn.com/Topic.aspx?id=2551
单片机教程,MCS51单片机从零开始 第十三讲:51单片机CPU的内部结构
http://bbs.huazhoucn.com/Topic.aspx?id=2552
单片机教程,MCS51单片机从零开始 第十四讲:定时器/计数器的基本结构及工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2553
单片机教程,MCS51单片机从零开始 第十五讲:51单片机的中断系统
http://bbs.huazhoucn.com/Topic.aspx?id=2554
单片机教程,MCS51单片机从零开始 第十六讲:51单片机的复位
http://bbs.huazhoucn.com/Topic.aspx?id=2555
单片机教程,MCS51单片机从零开始 第十七讲:51单片机执行指令的过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第十八讲:51单片机的延时及时序分析
http://bbs.huazhoucn.com/Topic.aspx?id=2557
单片机教程,MCS51单片机从零开始 第十九讲:汇编语言基础
http://bbs.huazhoucn.com/Topic.aspx?id=2558
单片机教程,MCS51单片机从零开始 第二十讲:汇编语言及汇编过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第二十一讲:汇编程序的基本结构
http://bbs.huazhoucn.com/Topic.aspx?id=2560
单片机教程,MCS51单片机从零开始 第二十二讲:51单片机的寻址方式
http://bbs.huazhoucn.com/Topic.aspx?id=2561
单片机教程,MCS51单片机从零开始 第二十三讲:数据传送类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2562
单片机教程,MCS51单片机从零开始 第二十四讲:算术运算类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2563
单片机教程,MCS51单片机从零开始 第二十五讲:逻辑运算及移位指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2564
单片机教程,MCS51单片机从零开始 第二十六讲:控制转移类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2565
单片机教程,MCS51单片机从零开始 第二十七讲:布尔变量操作指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2566
单片机教程,MCS51单片机从零开始 第二十八讲:伪指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2567

1 楼  提交者:耕牛 在 2008-6-13 9:22:35
2 楼  提交者:Guest 在 2008-7-10 20:41:11
ding 


3 楼  提交者:Guest 在 2008-7-12 10:09:00
lookvlookvlook
4 楼  提交者:Guest 在 2008-7-13 14:16:46
look
5 楼  提交者:Guest 在 2008-7-17 11:41:54
vb
6 楼  提交者:Guest 在 2008-7-17 20:52:09
kankan
7 楼  提交者:Guest 在 2008-7-21 15:57:58
二天
8 楼  提交者:Guest 在 2008-7-24 15:26:38
support
9 楼  提交者:Guest 在 2008-7-26 9:21:53
10 楼  提交者:Guest 在 2008-7-28 13:50:09
hehe
11 楼  提交者:Guest 在 2008-7-29 11:50:34
jj
12 楼  提交者:fightsqlee 在 2008-7-30 8:55:36
看了以前的,很好
13 楼  提交者:Guest 在 2008-8-4 16:07:56
look
14 楼  提交者:Guest 在 2008-8-6 8:50:38
_BV
15 楼  提交者:Guest 在 2008-8-7 10:59:11
谢谢
16 楼  提交者:qsjit 在 2008-8-7 19:59:36
17 楼  提交者:Guest 在 2008-8-14 23:24:56
fgfdgfdgdfgdfgdfgdrt
18 楼  提交者:Guest 在 2008-8-21 15:59:50
学习
19 楼  提交者:要飞的鸟 在 2008-8-31 8:11:33
可以的
20 楼  提交者:Guest 在 2008-9-2 10:08:44
;jfkldshmdf,./smhjs
21 楼  提交者:Guest 在 2008-9-5 8:40:51
咨询问答:

sss

22 楼  提交者:wgx1976 在 2008-9-5 15:48:00
无内容
23 楼  提交者:Guest 在 2008-9-7 14:27:58
o
24 楼  提交者:Guest 在 2008-9-9 10:18:45
很好!
25 楼  提交者:Guest 在 2008-9-20 14:07:44
~~~~~~~~~~~~~~~~~~~~
26 楼  提交者:Guest 在 2008-9-24 8:23:23
try it
27 楼  提交者:ztkclzty 在 2008-10-5 18:34:50
dd
28 楼  提交者:Guest 在 2008-10-7 9:28:09
我想看看
29 楼  提交者:Guest 在 2008-10-10 21:01:33
无内容
30 楼  提交者:luozhongchao 在 2008-10-14 21:36:09
lai 
上一篇 下一篇
当前第〖1〗页 共有4页 转到第 1 2 3 4