AVR单片机电源管理及睡眠模式应用实例(含源代码)
AVR单片机电源管理及睡眠模式应用实例(含源代码)
/***********************************************
**** AVR 电源管理_睡眠模式范例 ***
**** ***
**** 作者: HJJourAVR ***
**** 编译器:WINAVR20050214 ***
**** ***
***********************************************/
/*
本程序简单的示范了如何令AVR ATMEGA16进入睡眠状态及唤醒
电源管理及睡眠模式的介绍
进入最低耗电的掉电模式
关闭各种模块
外部中断唤醒
M16掉电模式的耗电情况(看门狗关闭),时钟为内部RC 1MHz
0.9uA@Vcc=5.0V [手册的图表约为1.1uA]
0.3uA@Vcc=3.3V [手册的图表约为0.4uA]
//测量的数字万用表是FLUKE 15B,分辨率0.1uA
这个程序需要MCU进入休眠状态,为实现最低功耗,JTAG接口会被关闭,只能通过LED的变化来观察程序的运行。
这个实验里面,用STK500(AVRISP) ISP下载线来烧录更方便。
熔丝位设置
1 关断BOD功能 BODEN=1
2 如果用ISP方式烧录,就可以完全关闭JTAG口了 OCEEN=1,JTAGEN=1
*/
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
//时钟定为内部RC 1MHz,F_CPU=1000000 也可以采用其他时钟
#include <avr/sleep.h>
/*
sleep.h里面定义的常数,对应各种睡眠模式
#define SLEEP_MODE_IDLE 0
空闲模式
#define SLEEP_MODE_ADC _BV(SM0)
ADC 噪声抑制模式
#define SLEEP_MODE_PWR_DOWN _BV(SM1)
掉电模式
#define SLEEP_MODE_PWR_SAVE (_BV(SM0) | _BV(SM1))
省电模式
#define SLEEP_MODE_STANDBY (_BV(SM1) | _BV(SM2))
Standby 模式
#define SLEEP_MODE_EXT_STANDBY (_BV(SM0) | _BV(SM1) | _BV(SM2)) 扩展Standby模式
函数
void set_sleep_mode (uint8_t mode);
设定睡眠模式
void sleep_mode (void);
进入睡眠状态
*/
//管脚定义
#define LED 0 //PB0 驱动LED,低电平有效
#define KEY_INT2 0 //PB3 按键, 低电平有效
void delay_10ms(unsigned int t)
{
/*
由于内部函数_delay_ms() 最高延时较短
262.144mS@1MHz / 32.768ms@8MHz / 16.384ms@16MHz
故编写了这条函数,实现更长的延时,并能令程序能适应各种时钟频率 */
while(t--)
_delay_ms(10);
}
int main(void)
{
unsigned char i;
//上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
PORTA=0xFF; //不用的管脚使能内部上拉电阻。
PORTC=0xFF;
PORTD=0xFF;
PORTB=0xFF;
DDRB =(1<<LED); //PB0设为输出高电平,灯灭
/*
端口引脚
进入休眠模式时,所有的端口引脚都应该配置为只消耗最小的功耗。
最重要的是避免驱动电阻性负载。
在休眠模式下I/O 时钟clkI/O 和ADC 时钟clkADC 都被停止了,输入缓冲器也禁止了,从而保证输入电路不会消耗电流。
在某些情况下输入逻辑是使能的,用来检测唤醒条件。用于此功能的具体引脚请参见“ 数字输入使能和休眠模式” 。
如果输入缓冲器是使能的,此时输入不能悬空,信号电平也不应该接近VCC/2,否则输入缓冲器会消耗额外的电流。
IO作输出(DDR=1)时,维持状态不变
*/
/*
看门狗定时器(上电默认是关闭的)
如果系统无需利用看门狗,这个模块也可以关闭。
若使能,则在任何休眠模式下都持续工作,从而消耗电流。
在深层次的睡眠模式下,这个电流将占总电流的很大比重。
假设看门狗定时器使能了,关闭程式如下
1. 在同一个指令内对WDTOE 和WDE 写"1“,即使WDE 已经为"1“
2. 在紧接的4 个时钟周期之内对WDE 写"0”
*/
WDTCR=(1<<WDTOE)|(1<<WDE);
WDTCR=(0<<WDE);
//或使用wdt.h里面的wdt_disable()函数
/*
模数转换器(上电默认是关闭的)
使能时, ADC在睡眠模式下继续工作。
为了降低功耗,在进入睡眠模式之前需要禁止ADC。
重新启动后的第一次转换为扩展的转换。
假设模数转换器使能了,关闭程式如下
*/
ADCSRA=(0<<ADEN);
/*
模拟比较器(上电默认是打开的,需要手工关闭)
在空闲模式时,如果没有使用模拟比较器,可以将其关闭。在ADC 噪声抑制模式下也是如此。
在其他睡眠模式模拟比较器是自动关闭的。
如果模拟比较器使用了内部电压基准源,则不论在什么睡眠模式下都需要关闭它。否则内部电压基准源将一直使能。
关闭程式如下
*/
ACSR=(1<<ACD);
/*
掉电检测BOD (由熔丝位BODEN控制)
如果系统没有利用掉电检测器BOD,这个模块也可以关闭。
如果熔丝位BODEN 被编程,从而使能了BOD 功能,它将在各种休眠模式下继续工作。 在深层次的休眠模式下,这个电流将占总电流的很大比重。
设置熔丝位BODEN=1 关断BOD功能
*/
/*
片内基准电压
使用BOD、模拟比较器和ADC 时可能需要内部电压基准源。
若这些模块都禁止了,则基准源也可以禁止。
重新使能后用户必须等待基准源稳定之后才可以使用它。
如果基准源在休眠过程中是使能的,其输出立即可以使用。
当BOD、模拟比较器和ADC都禁止了,则基准源也自动禁止了。
*/
/*
JTAG 接口与片上调试系统
如果通过熔丝位OCDEN使能了片上调试系统,当芯片进入掉电或省电模式时主时钟保持运行。
在休眠模式中这个电流占总电流的很大比重。
下面有三种替代方法:
1 不编程OCDEN
2 不编程JTAGEN
3 置位MCUCSR 的JTD
当JTAG 接口使能而JTAG TAP 控制器没有进行数据交换时,引脚TDO 将悬空。 如果与TDO 引脚连接的硬件电路没有上拉电阻,功耗将增加。
器件的引脚TDI 包含一个上拉电阻,因此在扫描链中无需为下一个芯片的TDO 引脚设置上拉电阻。
通过置位MCUCSR寄存器的JTD 或不对JTAG 熔丝位编程可以禁止JTAG 接口。
JTD: 禁止JTAG 接口(MCU控制与状态寄存器MCUCSR Bit7)
此位为0 时,如果JTAGEN熔丝位被编程则JTAG 接口使能。
如果这位为1, JTAG接口禁止。
为了避免无意的禁止或使能JTAG接口,必须通过一个时间序列来改变JTD 位。 应用软件必须在四个时钟周期内将期望的数值两次写入JTD。
如果JTAG 接口没有与其他JTAG电路连接, JTD应该置位。这样做的原因是为了避免JTAG接口TDO引脚的静态电流。
在软件中关闭JTAG接口的方法
*/
MCUCSR=(1<<JTD);
MCUCSR=(1<<JTD);
/*
掉电模式
当SM2..0 为010 时, SLEEP 指令将使MCU 进入掉电模式。
在此模式下,外部晶体停振,而外部中断、两线接口地址匹配及看门狗(如果使能的话)继续工作。
只有外部复位、看门狗复位、BOD 复位、两线接口地址匹配中断、外部电平中断INT0 或INT1,或外部中断INT2 可以使MCU 脱离掉电模式。
这个睡眠模式停止了所有的时钟,只有异步模块可以继续工作。
当使用外部电平中断方式将MCU 从掉电模式唤醒时,必须保持外部电平一定的时间。 从施加掉电唤醒条件到真正唤醒有一个延迟时间,此时间用于时钟重新启动并稳定下来。 唤醒周期与由熔丝位CKSEL 定义的复位周期是一样的。
如果在睡眠过程中发生了复位,则MCU 唤醒后从中断向量开始执行
使能的中断可以将进入睡眠模式的MCU 唤醒, 经过启动时间,外加4个时钟周期后,MCU就可以运行中断例程了。然后返回到SLEEP 的下一条指令。
*/
MCUCSR=(0<<ISC2); //INT2 的下降沿激活中断(默认的,这句话可以不写)
GICR=(1<<INT2); //使能外部中断INT2
sei(); //使能全局中断
while(1)
{
for (i=0;i<10;i++) //LED闪动10次后进入掉电模式的睡眠状态
{
delay_10ms(30);
PORTB&=~(1<<LED); //点亮LED
delay_10ms(30);
PORTB|=(1<<LED); //熄灭LED
}
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //设定为掉电模式
sleep_mode(); //进入睡眠状态
/*
也可以自行编写
MCUCR=(0<<SM2)|(1<<SM1)|(0<<SM0); //设定为掉电模式
asm volatile(“sleep” : : ); //进入睡眠状态
*/
}
}
SIGNAL(SIG_INTERRUPT2) //外部中断2服务程序 唤醒源
{
PORTB&=~(1<<LED); //点亮LED
delay_10ms(500);
PORTB|=(1<<LED); //熄灭LED
delay_10ms(100);
/*LED长亮5秒钟,熄灭1秒钟后,退出中断服务程序,然后返回到SLEEP 的下一条指令*/ }
/*
程序运行效果
万用表打到直流电流的最小档位(uA分辨率),接到开关的两头
烧录后要把STK500拔出,否则无法测得正确的电流数据。
上电后LED闪动10次后进入掉电模式的睡眠状态
此时可断开开关
看看万用表的读数
然后接通开关
按下 INT2按键,将会发现LED长亮5秒钟,熄灭1秒钟后,退回主程序,LED闪动10次后进入掉电模式的睡眠状态
如果按下复位按键,马上复位。
网友可以编写其他睡眠模式/开关各种模块/其他时钟源/其他电源电压来测试电流消耗情况 */
/*
电源管理及睡眠模式
睡眠模式可以使应用程序关闭MCU中没有使用的模块,从而降低功耗。
AVR 具有不同的睡眠模式,允许用户根据自己的应用要求实施剪裁。
进入睡眠模式的条件是置位寄存器MCUCR的SE,然后执行SLEEP 指令。
具体哪一种模式( 空闲模式、ADC 噪声抑制模式、掉电模式、省电模式、Standby 模式和扩展Standby模式) 由MCUCR 的SM2、SM1 和SM0 决定。
使能的中断可以将进入睡眠模式的MCU 唤醒。
经过启动时间,外加4个时钟周期后,MCU就可以运行中断例程了。然后返回到SLEEP 的下一条指令。
唤醒时不会改变寄存器文件和SRAM的内容。
如果在睡眠过程中发生了复位,则MCU 唤醒后从中断向量开始执行
需要了解AVR芯片内部不同的时钟系统及其分布,在选择合适的睡眠模式时非常有用。
MCU控制寄存器-MCUCR
MCU控制寄存器包含了电源管理的控制位。
Bits 7, 5, 4 – SM2..0: 休眠模式选择位 2、1 和0
这些位用于选择具体的休眠模式。
SM2 SM1 SM0 休眠模式
0 0 0 空闲模式
0 0 1 ADC 噪声抑制模式
0 1 0 掉电模式
0 1 1 省电模式
1 0 0 保留
1 0 1 保留
1 1 0 Standby模式(1)
1 1 1 扩展Standby模式(1)
Note:1 仅在使用外部晶体或谐振器时Standby 模式与扩展Standby 模式才可用。
Bit 6 – SE: 休眠使能
为了使MCU 在执行SLEEP 指令后进入休眠模式, SE必须置位。
为了确保进入休眠模式是程序员的有意行为,建议仅在SLEEP 指令的前一条指令置位SE。 MCU 一旦唤醒立即清除SE。
关于各种睡眠模式的特点与唤醒要求,内容繁多,请参考数据手册
*/
【进入博客】【进入论坛】 更新时间:
20xx年07月18日 浏览次数: 279 作者: 来
源:
摘要:AVR单片机是Atmel公司推出的一个单片机系列。由于该系列单片机的集成度高,因此,其软/硬件设计都变得更加简洁。文中介绍通过AVR单片机的捕获中断来实现红外线通讯解码的一种方法。
关键词:AVR;红外线解码;输入捕获中断
1 红外线编码
红外线编码是数据传输和家用电器遥控常用的一种通讯方法,其实质是一种脉
宽调制的串行通讯。家电遥控中常用的红外线编码电路有μPD6121G型HT622型和7461型等。本文就以这些电路的编码格式来讨论怎样使用AVR单片机的捕获中断功能来实现其解码。
红外线通讯的发送部分主要是把待发送的数据转换成一定格式的脉冲,然后驱
动红外发光管向外发送数据。接收部分则是完成红外线的接收、放大、解调,还原成同步发射格式相同(但高、低电位刚好相反的脉冲信号。这些工作通常由一体化的接收头来完成,主要输出TTL兼容电平。最后通过解码把脉冲信号转换成数据,从而实现数据的传输。图1是一个红外线遥控制系统的原理框图。
图2示出该红外遥控系统的编码格式。
图中,μPD6121G遥控器的二进制“0”由0.56ms的间隔加0.565ms的脉冲表示;二进制“1”由0.56ms的间隔加1.685ms的脉冲表示。每次发送的32二进制码可分成两部分,其中前16位是遥控器辨识码,主要用于区别不同遥控器,后16位是操作码。这两个部分的后8位都是前8位的反码,用作数据校验。每帧数据以9ms的间隔加4.5ms的脉冲作为数据头。 2 AVR单片机
AVR系列单片机是Atmel公司于19xx年推出的一款全新配置的、采用精简指令(RISC-Redued Instruction Set CPU)结构的新型8位单片机。由于AVR单片机采用单指令操作,所以,在相同时钟的情况下,AVR的指令周期只有8051型机的1/2,而且AVR采用两极指令流水线,可以在执行当前指令的同时获取下一条指令,所以具备1MI/s/MHz的调整处理能力。不同于8051型机的是AVR采用32个通用工作寄存器,克有了单一累加器数据处理带来的瓶劲现象,从而使得指令代码更加灵活,编码更容易。此外,AVR中还集成了A/D、PWM、EEPROM、FLASH、SPI、WTD、IIC、T/C等功能,使外围电路变得很简单。
3 基于AVR单片机的硬件实现
本文以AVR系列中高性价比的Atmage8为例,利用16位时钟单元T/C1的捕获中断来实现红外线解码。T/C1内部的输入捕获单元可以应用于精确捕获外部发生的事情,亦即事件发生的时间印记(time-stamp)。当一个输入捕获事件发生在外部引脚ICPI上的逻辑电平也随之发生变化时,T/C1的计数值将被拷贝到捕获寄存器ICR1并设置捕获中断标志,如果捕获中断允许并且总中断IE打开,系统则进入中断服务程序。这种捕获中断通常用于频率和周期的精确测量,如电机转速和转向的测量。本文介绍怎样利用这一功能测量红外线脉冲的脉宽以实现红外传输的解码。捕获中断的触发可以是ICP1引脚上电平变化的上升沿,也可以是下降沿。根据前述脉冲调制规则,现以下降沿为触发事件来进行讨论。
图3是该系统的工作时序图,图中,一个下降沿到下一个下降沿之间刚好是一个脉冲加一个间隙的时间,这样,根据编码规则,这个时间长度所对应的信号关系如下:
数据头的时间:Th=9+4.5=13.5ms
数据“0”的时间:T0=0.565+0.56=1.125ms
数据“1”的时间:T1=1.685+0.56=2.245ms
4 软件编程
基于AVR单片机的捕获中断来实现红外编码的软件程序流程如图4所示。下面是其
C语言程序代码:
C程序代码:
*pragma interrupt_handler IceInt:6 //中断程序说明
void ICEInit(void) //T/C1初始化
{
TIMSK=0X20; //使能捕获中断
TCCR1A=0X00; //T/C1时钟与系统相同,本文使用系统AVR内部自带1MHz振荡源.T/C1时钟周期为1μs
TCCR1B=0X81; //使能噪音抑制,下降沿触发中断
}
void IceInt(void)
{
static nint oldFall;
uint temp,newFall;
newFall=ICR1;
temp=newFall-oldFall; //计算脉冲加间隔的时间
oldFall=newFall;
if(temp>1024 && temp<1225) // "0"信号
{
temp=0;
}
else if(temp>2145 && temp<2345) //“1”信号
{
temp=1;
}
else if(temp>13400 && temp<13600) //header信号 {
bitcnt=0;
data0=0;
data1=0'
return; //返回,等待下次开始接收
}
else ///干扰信号
{
return;
}
bitcnt++;
if(bitcnt<16) //开始接收前16位
{
data0=data0|(uint)temp;
data0=data0<<1;
}
else if(bitcnt==16)
{
data0=data0|(uint)temp;
}
else if(bitcnt<32) //开始接收后16位
{
data1=data1|(uint)temp;
data1=data1<<1;
}
else if(bitcnt==32) //接收完最后一位
}
data1=data1|(uint)temp;
}
}
该单片机的主循环程序中含有一些代码检测bitcnt,当bitcnt=32时,表明一帧数据已接收完成,并将bitcnt设置为0xff,然后对接收的数据进行相应的处理。程序调试最好配合串行通讯来进行,这样可以在PC上显示数据或者画出波形以方便验证。If(temp>1025 && temp<1225)这条语句是辨识“0”的代码,它是以
T0-100<T0<T0+100为范围确害的“0”,该范围的大小决定着接收的准确度与灵敏度,单位为μs,后面两个两条语句含义相同。
需要补充的是当键盘按下长达108ms时,发射端开始发送连续信号,与单次发送一样,只是header信号是由9ms的间隔加2.5ms的脉冲组成的。
AVR单片机设计DS1302实时时钟程序 【进入博客】【进入论坛】 更新时间:20xx年07月13日 浏览次数: 518 作者: 来
源:
主程序文件rtc.c
/********************************
AVR单片机I/O口操作DS1302接口程序
文件名:rtc.c
编译:WinAVR-20070525
硬件:ATMEGA169PV 时钟:7372800 Hz
SCLK <---- PE5
CE <---- PE3
IO <---> PE4
此程序在硬件上调试通过!
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include<avr/io.h>
#include<util/delay.h>
//#define BURST_MODE
#define DS1302_DDR DDRE
#define DS1302_PORT PORTE
#define DS1302_PIN PINE
#define DS1302_CK PE5
#define DS1302_IO PE4
#define DS1302_CE PE3
#define delay_bus(x) _delay_loop_1(x*2)
static inline void ds1302_select(void) {
DS1302_PORT|=_BV(DS1302_CE);
delay_bus(2);
}
static inline void ds1302_unselect(void) {
DS1302_PORT&=~_BV(DS1302_CE);
delay_bus(2);
}
//实现读一字节时序
static uint8_t ds1302_read_byte(void) {
uint8_t i,ret=0;
for(i=0;i<8;i++)
{
ret>>=1;
if(DS1302_PIN & _BV(DS1302_IO)) ret|=0x80;
//给一脉冲
DS1302_PORT|=_BV(DS1302_CK); delay_bus(1);
DS1302_PORT&=~_BV(DS1302_CK);
delay_bus(1);
}
return ret;
}
//实现写一字节时序
static void ds1302_write_byte(uint8_t dat) {
uint8_t i;
//IO口设置为输出
DS1302_DDR|=_BV(DS1302_IO); DS1302_PORT&=~_BV(DS1302_IO);
for(i=0;i<8;i++)
{
//设置好数据口
if(dat& 0x01)
DS1302_PORT|=_BV(DS1302_IO); else
DS1302_PORT&=~_BV(DS1302_IO);
delay_bus(1);
//给一脉冲
DS1302_PORT|=_BV(DS1302_CK); delay_bus(1);
DS1302_PORT&=~_BV(DS1302_CK);
dat>>=1;
}
//IO口恢复到输入状态
DS1302_PORT&=~_BV(DS1302_IO); DS1302_DDR&=~_BV(DS1302_IO);
delay_bus(1);
}
//读寄存器 addr: 0~8
uint8_t rtc_read_reg(uint8_t addr) {
uint8_t ret;
addr<<=1;
addr|=0x81;
ds1302_select();
ds1302_write_byte(addr);
ret=ds1302_read_byte();
ds1302_unselect();
return ret;
}
//写寄存器
void rtc_write_reg(uint8_t addr,uint8_t dat)
{
addr<<=1;
addr|=0x80;
ds1302_select();
ds1302_write_byte(addr);
ds1302_write_byte(dat);
ds1302_unselect();
}
//读RAM
//在多字节(BURST)模式下读取 x 长度的数据到buf指向的存储区 //在单字节模式下从地址为x(0~31)的RAM中读一字节存储到buf处 void rtc_read_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
uint8_t i;
ds1302_select();
ds1302_write_byte(0xff); //read burts ram
for(i=0;i<x;i++)
buf[i]=ds1302_read_byte();
ds1302_unselect();
#else
x<<=1;
x|=0xc1;
ds1302_select();
ds1302_write_byte(x);
*buf=ds1302_read_byte();
ds1302_unselect();
#endif
}
//写RAM
//在多字节(BURST)模式下写 x 长度的数据到DS1302内部RAM,从0地址开始写 //在单字节模式下在地址为x(0~31)的RAM片写入一字节数据
void rtc_write_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
uint8_t i;
#endif
rtc_write_reg(7,0);//写使能
#ifdef BURST_MODE
ds1302_select();
ds1302_write_byte(0xfe); //write burts ram
for(i=0;i<x;i++)
ds1302_write_byte(buf[i]);
ds1302_unselect();
#else
x<<=1;
x|=0xc0;
ds1302_select();
ds1302_write_byte(x);
ds1302_write_byte(*buf);
ds1302_unselect();
#endif
rtc_write_reg(7,0x80);//写禁止
}
//读取时钟 将读取的时钟以小时,分钟,秒的顺序存入time指向的缓冲区 //读出的数据为二进制格式,而非BCD码
void rtc_get_time(uint8_t *time)
{
uint8_t s,m=0,h;
h=rtc_read_reg(2);
if(h & 0x80)//12小时制
{
if(h& _BV(4))
m=10;
else
m=0;
if(h&_BV(5))
m+=12;
h=m+(h&0x0f);
}
else //24小时制
{
h&=0x3f;
h= (h>>4)*10 + (h & 0x0f);
}
s=rtc_read_reg(0) & 0x7f;
m=rtc_read_reg(1) & 0x7f;
time[2]= (s>>4)*10 + (s & 0x0f);
time[1]= (m>>4)*10 + (m & 0x0f);
time[0]= h;
}
//设置时钟,time中的时钟格式为:时,分,秒二进制式 void rtc_set_time(uint8_t *time)
{
uint8_t h,m,s;
h=time[0]%24;
m=time[1]%60;
s=time[2]%60;
//二进制转BCD
h=((h/10)<<4) + (h%10);
m=((m/10)<<4) + (m%10);
s=((s/10)<<4) + (s%10);
rtc_write_reg(7,0);//write enable
rtc_write_reg(0,s);
rtc_write_reg(1,m);
rtc_write_reg(2,h);
rtc_write_reg(7,0x80);//write disable
}
//读日历 将读取的日历以年,月,日的顺序存入date指向的缓冲区 //读出的数据为二进制格式,而非BCD码
void rtc_get_date(uint8_t *date)
{
uint8_t y,m,d;
y=rtc_read_reg(6);
m=rtc_read_reg(4)& 0x1f;
d=rtc_read_reg(3)& 0x3f;
//BCD转二进制
date[0]=(y>>4)*10 + (y&0x0f);
date[1]=(m>>4)*10 + (m&0x0f);
date[2]=(d>>4)*10 + (d&0x0f);
}
//设置日历
void rtc_set_date(uint8_t *date)
{
uint8_t y,m,d;
//二进制转BCD码
y=((date[0]/10)<<4) + (date[0]%10);
m=((date[1]/10)<<4) + (date[1]%10);
d=((date[2]/10)<<4) + (date[2]%10);
//写入DS1302
rtc_write_reg(7,0);
rtc_write_reg(6,y);
rtc_write_reg(4,m);
rtc_write_reg(3,d);
rtc_write_reg(7,0x80);
}
//读周
void rtc_get_day(uint8_t *day)
{
*day=rtc_read_reg(5) & 0x07;
}
//设置周
void rtc_set_day(uint8_t *day)
{
rtc_write_reg(7,0);
rtc_write_reg(5,(*day)&0x07);
rtc_write_reg(7,0x80);
}
//初始化接口和各寄存器
void rtc_init(void)
{
//CE,CK口设置为输出,IO口设置为输入
DS1302_DDR|=_BV(DS1302_CE)|_BV(DS1302_CK);
DS1302_PORT&=~(_BV(DS1302_CE)|_BV(DS1302_CK)|_BV(DS1302_IO)); DS1302_DDR&=~_BV(DS1302_IO);
if(rtc_read_reg(0) & 0x80)//如果处于暂停状态
{
rtc_write_reg(7,0);//写允许
rtc_write_reg(0,0);//运行
rtc_write_reg(7,0x80);//写保护
}
//... ... 这里你可以检查涓流充电配置并设置寄存器
}
包含头文件 rtc.h
//rtc.h
#ifndef RTC_H
#define RTC_H
uint8_t rtc_read_reg(uint8_t addr);
void rtc_write_reg(uint8_t addr,uint8_t dat);
void rtc_read_ram(uint8_t x,uint8_t *buf);
void rtc_write_ram(uint8_t x,uint8_t *buf);
void rtc_get_time(uint8_t *time);
void rtc_set_time(uint8_t *time);
void rtc_get_date(uint8_t *date);
void rtc_set_date(uint8_t *date);
void rtc_get_day(uint8_t *day);
void rtc_set_day(uint8_t *day);
void rtc_init(void);
//注:此套接口函数中表示寄存器或RAM的地址为偏移地址而不是数据手册中的命令字
//如秒寄存器的地址为0 而不是0x80或0x81
#endif
DS18B20测温程序
文件名:main.c
编译:WinAVR-20070122
硬件环境:CA-M8X 打开的开关如下
S6(1,2,5,6,7) - 外部4MHz晶振和595接口
J8(EN-SEG) - 数码管显示允许
S7(4) - 连接PC1 与DS18B20数据口
(在CA-M8X 上DS18B20为非总线供电)
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include "seg.h" //声明数码管显示接口函数
#define CLR_1WIRE_BUS DDRC|=_BV(PC1) //设置为输出,此时由于PORTC1是低所以输出低
#define SET_1WIRE_BUS DDRC&=~_BV(PC1)//设置为输入,此时由于PORTC1是低所以程高阻,又因为外部有上拉电阻所以相当于设置总线为高
#define GET_1WIRE_BUS PINC&_BV(PC1)
#define DS18B20_READ_ROM 0x33
#define DS18B20_MATCH_ROM 0X55
#define DS18B20_SKIP_ROM 0XCC
#define DS18B20_SEARCH_ROM 0XF0
#define DS18B20_ALARM_SEARCH_ROM 0XEC
#define DS18B20_WRITE_RAM 0X40
#define DS18B20_READ_RAM 0XBE
#define DS18B20_COPY_RAM 0X48
#define DS18B20_CONVERT_TEM 0X44
#define DS18B20_EECALL_EEPROM 0XB8
#define DS18B20_READ_POWER_SUPPLY 0XB4
//总线端口初始化
void BusInit(void)
{
PORTC&=~_BV(PC1);//此口总保持低
DDRC&=~_BV(PC1); //初始化为输入,用外部上拉电阻保持总线的高电平
}
//由于系统时钟为4MHz,一个_delay_loop_2正好延时一us #define DelayUs(x) _delay_loop_2(x)
void DelayMs(uint16_t t)
{
uint16_t i;
for(i=0;i<t;i++)
_delay_loop_2(250 * 4);
}
//单总线复位
uint8_t ds18b20_reset(void)
{
uint8_t ret=0;
CLR_1WIRE_BUS;
DelayUs(500); //拉低总线至少480us
SET_1WIRE_BUS;
DelayUs(100);//释放总线后等待15-60us
if((GET_1WIRE_BUS)==0)//检测到DS18B20把总线拉低 ret=1; //复位成功
DelayUs(1000);//等待器件释放总线
return ret;
}
//单总线读一字节
uint8_t ds18b20_read(void)
{
uint8_t data=0;
uint8_t i=0;
for(i=0;i<8;i++)
{
data>>=1;
CLR_1WIRE_BUS;
DelayUs(2);//此时>1us
SET_1WIRE_BUS;
DelayUs(4);//此时<15us
if(GET_1WIRE_BUS)
data|=0x80;
DelayUs(60);//此时>60us
}
return(data);
}
//单总线写一字节
void ds18b20_write(uint8_t data)
{
uint8_t i=0;
for(i=0;i<8;i++)
{
if(data&0x01)
{
CLR_1WIRE_BUS;
DelayUs(8);//8us
SET_1WIRE_BUS;
DelayUs(55);//55us
}
else
{
CLR_1WIRE_BUS;
DelayUs(55);//55us
SET_1WIRE_BUS;
DelayUs(20);//8us
}
data>>=1;
}
}
//执行转换
uint8_t Ds18b20Convert(uint8_t *t)
{
//发送转换命令
if(ds18b20_reset()==0)
return 0;
ds18b20_write(DS18B20_SKIP_ROM); //忽略地址匹配,总线上只有一个器件时,或对总线所有器件操作
ds18b20_write(DS18B20_CONVERT_TEM);//开始转换命令
//等待转换完成,ds18b20默认转换精度为12位,此时最大转换时间为750ms DelayMs(1000);
//读温度字节
if(ds18b20_reset()==0)
return 0;
ds18b20_write(DS18B20_SKIP_ROM); //忽略地址匹配
ds18b20_write(DS18B20_READ_RAM); //读RAM命令
t[0]=ds18b20_read();
t[1]=ds18b20_read();
return 1;
}
//根据DS18B20中读的温度字节,计算实际温度值
int8_t GetTemperature(uint8_t *t)
{
int8_t ret;
uint32_t val;
uint16_t tmp=(t[1]*256)+t[0];
uint8_t sflag=0;
if((t[1]&0xf8)==0xf8) //若负温度,从补码转换(取反加一)
{
sflag=1;
tmp=~tmp;
tmp++;
}
tmp&=0x07ff; //确保前5位为0
//乘0.0625操作,为此本函数只适用于DS18B20 12位转换(默认)时 val=((uint32_t)tmp)*625;
val/=10000;
ret=(int8_t)val;
if(sflag)
ret|=0x80;//变负数
return ret;
}
//测试主函数
int main(void)
{
uint8_t tmp[2];//保存温度字节
int8_t tval; //保存温度值
SegInit();//数码管初始化
SegNumberOut(0,0);//显示 0
BusInit(); //单总线I/O口初始化
while(1)
{
if(Ds18b20Convert(tmp))//如果转换成功
{
tval=GetTemperature(tmp);//计算实际温度值 if(tval>=0)
SegNumberOut(tval,0);//十进制显示温度值 else
SegNumberOut(0,0);//数码管无法显示负数,只能显示0 }
}
return 0;
}
seg.c文件:
/********************************
74HC95驱动的数码管显示模块
文件名:seg.c
编译:WinAVR-20070122
硬件环境:CA-M8X 打开的开关如下
S6(1,2,5,6,7) - 外部4MHz晶振和595接口
J8(EN-SEG) - 数码管显示允许
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include "seg.h"
#define SER_PORT PORTD
#define SER_DAT PD4
#define SER_RCK PD5
#define SER_SCK PD6
//显示码(可从chipart.cn下载生成工具)
static uint8_t g_aDisplayBuf[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//向595 写一字节
static void ser_out(uint8_t dat)
{
uint8_t i;
for(i=0;i<8;i++)
{
if(dat&0x80)
SER_PORT|=_BV(SER_DAT);
else
SER_PORT&=~_BV(SER_DAT);
//产生移位脉冲
SER_PORT|=_BV(SER_SCK);
SER_PORT&=~_BV(SER_SCK);
dat<<=1;
}
}
//数码管显示数(0~255)
//num :显示的数 hex:是否用十六进制显示 void SegNumberOut(uint8_t num,uint8_t hex) {
uint8_t buf[2];//发送显示码缓冲区
uint8_t temp;
if(hex) //十六进制
{
buf[0]=g_aDisplayBuf[num>>4];//高位
buf[1]=g_aDisplayBuf[num&0x0f];//低位 }
else//十进制
{
buf[1]=g_aDisplayBuf[num%10];
temp=num%100;
buf[0]=g_aDisplayBuf[temp/10];
temp=num/100;
if(temp>0)
buf[1]|=0x80; //第一个数码管小数点表示百位1 if(temp>1)
buf[0]|=0x80;//两个数码管小数点表示百位2 }
//串行发送数据
ser_out(buf[0]);
ser_out(buf[1]);
//产生锁存脉冲
SER_PORT|=_BV(SER_RCK);
SER_PORT&=~_BV(SER_RCK);
}
void SegInit(void)
{
//595控制I/O初始化
DDRD=_BV(SER_DAT)|_BV(SER_SCK)|_BV(SER_RCK);
SER_PORT&=~_BV(SER_SCK);
SER_PORT&=~_BV(SER_RCK);
}
利用AVR单片机TWI模块操作AT24CXX的通用程序 【进入博客】【进入论坛】 更新时间:20xx年07月13日 浏览次数: 406 作者: 来
源:
以下为TWI模块的接口函数实现:
twi.h:
//twi.h
#ifndef TWI_H
#define TWI_H
void TwiInit(void);
uint8_t TwiStart(void);
void TwiStop(void);
uint8_t TwiWriteByte(uint8_t c);
//读一字节 ack: 1时发TW_ACK,0时发TW_NOACK
uint8_t TwiReadByte(uint8_t *c, uint8_t ack);
//当I/O口模拟时此值为1
//当硬件 TWI接口时此值为0x18,即发送SLA+W后接收到ACK状态
#define NO_BUSY 0x18
#endif
twi.c:
/********************************
AVR单片机硬件TWI模块操作接口程序 文件名:twi.c
编译:WinAVR-20070122
硬件:CA-M8X
配置:外部4MHz
打开:S7(1,2,3) - EEPROM连接
S6(1,2) - 4MHz晶振连接 S5(5,6) - UART连接
注:PC3连接写保护引脚
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <util/twi.h>
#include <stdint.h>
#define TWI_BAUD 10000 //波特率(10k)
void TwiInit(void)
{
TWSR=0XF8;//不分频
//设置波特率,请确保TWBR不小于10
TWBR=((F_CPU/TWI_BAUD)-16)/8;
PORTC|=_BV(PC4)|_BV(PC5);
}
uint8_t TwiStart(void)
{
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); while ((TWCR & _BV(TWINT)) == 0) ;
return TW_STATUS;
}
//产生一停止信号
void TwiStop(void)
{
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); while(TWCR&_BV(TWSTO));/*等等停止信号完成*/ }
//写一字节
uint8_t TwiWriteByte(uint8_t c)
{
TWDR = c;
TWCR = _BV(TWINT) | _BV(TWEN);
while ((TWCR & _BV(TWINT)) == 0);
return TW_STATUS;
}
//读一字节 ack: 1时应答,0时不应答
uint8_t TwiReadByte(uint8_t *c, uint8_t ack) {
uint8_t tmp=_BV(TWINT)|_BV(TWEN);
if(ack)
tmp|=_BV(TWEA);
TWCR=tmp;
while ((TWCR & _BV(TWINT)) == 0) ;
*c=TWDR;
return TW_STATUS;
}
以下为AT24CXX的操作函数实现:
at24cxx.h:
//AT24CXX.H
#ifndef AT24CXX_H
#define AT24CXX-H
void At24cxxWaitBusy(void);
void At24cxxConfig(uint8_t device_addr,uint8_t page_size); void At24cxxWriteByte(uint16_t addr,uint8_t dat); uint8_t At24cxxReadByte(uint16_t addr);
void At24cxxWritePage(uint16_t page_index,uint8_t *buf); void At24cxxReadPage(uint16_t page_index,uint8_t *buf);
#endif
at24cxx.c:
/********************************
通用AT24CXX操作接口程序
本程序适合于AT24C32/64,AT24C128/256等器件
文件名:at.c
编译:WinAVR-20070122
硬件:CA-M8X
注:本程序需要I/O模拟或硬件实现的I2C总线接口函数
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <stdint.h>
#include "twi.h" //i2c接口函数声明处
#define TW_WRITE 0
#define TW_READ 1
#define TW_ACK 1
#define TW_NOACK 0
/*以下两个宏控制AT24CXX的WP引脚,如未连接可定义为空:
#define EEPROM_WRITE_ENABLE
#define EEPROM_WRITE_DISABLE
AT24CXX中WP引脚接地时写允许,接电源(高)时写保护,
如不接,器件内部有接地电阻,即写允许. */
//在CA-M8X板上该引脚通过S7(3)连接MEGA8的PC3
#define EEPROM_WRITE_ENABLE PORTC&=~_BV(PC3),DDRC|=_BV(PC3) #define EEPROM_WRITE_DISABLE PORTC|=_BV(PC3),DDRC|=_BV(PC3)
static uint8_t g_PageSize=0;//页大小(按字节)
static uint8_t g_DeviceAddr=0;//器件地址
static uint8_t g_PageBitCount;//一页所占用的位数(如一页为64字节,则6)
/*器件忙检测,原理:器件忙时不会对主机的写操作应答*/
//忙检测接口函数,只有一种情况才需要调用这个函数
//即:当刚写完成,要读数据时
//而连续的读或者写操作之间不需要调用这个函数
void At24cxxWaitBusy(void)
{
uint8_t i;
//检测EEPROM是否忙
while(1)
{
TwiStart();
i=TwiWriteByte(g_DeviceAddr);
TwiStop();
if(i==NO_BUSY)
break;
}
return ;
}
/* 设置当前操作器件的地址和页大小
device_addr最低位必须为0
只有在使用页访问器件时page_size有用
不使用页访问时可指定page_size为0 */
void At24cxxConfig(uint8_t device_addr,uint8_t page_size)
{
uint8_t i;
g_DeviceAddr=device_addr;
g_PageSize=page_size;
g_PageBitCount=0;
if(page_size==0)
return ;
//计算一页所占用位数
for(i=1;i<10;i++)//不能大于9次
{
if(page_size==(1<<i))
{
g_PageBitCount=i;
break;
}//if
}//for
}
//AT24CXX通用随机写一字节函数
void At24cxxWriteByte(uint16_t addr,uint8_t dat) {
At24cxxWaitBusy();
EEPROM_WRITE_ENABLE;
TwiStart();
TwiWriteByte(g_DeviceAddr );//= |TW_WRITE TwiWriteByte(addr>>8);//写地址高字节
TwiWriteByte(addr);//写地址低字节
TwiWriteByte(dat);//写数据字节
TwiStop();
EEPROM_WRITE_DISABLE;
}
//AT24CXX通用随机读一字节函数
uint8_t At24cxxReadByte(uint16_t addr)
{
uint8_t ret;
TwiStart();
TwiWriteByte(g_DeviceAddr);//写地址
TwiWriteByte(addr>>8);
TwiWriteByte(addr);
TwiStart();
TwiWriteByte(g_DeviceAddr | TW_READ);
TwiReadByte(&ret,TW_NOACK);//NO ACK
TwiStop();
return ret;
}
//AT24CXX通用写页函数,page_index为页地址,即表示第几页 void At24cxxWritePage(uint16_t page_index,uint8_t *buf) {
uint8_t i;
//页索引调整到绝对地址
page_index<<=g_PageBitCount;
//检测EEPROM是否忙
At24cxxWaitBusy();
//写一页
EEPROM_WRITE_ENABLE;
TwiStart();
TwiWriteByte(g_DeviceAddr );//= |TW_WRITE
TwiWriteByte(page_index>>8);//写地址高字节
TwiWriteByte(page_index);//写地址低字节
for(i=0;i<g_PageSize;i++)
TwiWriteByte(buf[i]);
TwiStop();
EEPROM_WRITE_DISABLE;
}
//AT24CXX通用读页函数,page_index为页地址,即表示第几页
void At24cxxReadPage(uint16_t page_index,uint8_t *buf)
{
uint8_t i;
//页索引调整到绝对地址
page_index<<=g_PageBitCount;
TwiStart();
TwiWriteByte(g_DeviceAddr);//写地址
TwiWriteByte(page_index>>8);
TwiWriteByte(page_index);
TwiStart();
TwiWriteByte(g_DeviceAddr | TW_READ);
for(i=0;i<g_PageSize-1;i++)
TwiReadByte(&buf[i],TW_ACK);
TwiReadByte(&buf[i],TW_NOACK);//最后一字节不应答
TwiStop();
}
测试部分:
uart.c:
/****************************************
文件名:uart.c
****************************************/
#include <avr/io.h>
#include <stdio.h>
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
static int uart_putchar(char c, FILE *stream)
{
if (c == '\n')
uart_putchar('\r', stream);
loop_until_bit_is_set(UCSRA, UDRE); UDR = c;
return 0;
}
void StdIoInit(void)
{
UCSRB=0;
UBRRH=0;
UBRRL=25; //9600
UCSRB=_BV(TXEN);
stdout = &mystdout;
stderr = &mystdout;
printf("Uart初始化完成!\n");
}
test.c:
/********************************
AVR单片机TWI模块操作AT24CXX的通用程序 文件名:main.c
编译:WinAVR-20070122
硬件:CA-M8X
配置:外部4MHz
打开:S7(1,2,3) - EEPROM连接 S6(1,2) - 4MHz晶振连接 S5(5,6) - UART连接 注:PC3连接写保护引脚
芯艺设计室 2004-2007 版权所有 转载请保留本注释在内的全部内容 WEB:
Email: changfutong@sina.com
*******************************/
#include<avr/io.h>
#include<stdint.h>
#include<stdio.h>
#include "twi.h"
#include "at24cxx.h"
#define AT24C256_PAGE_SIZE 64 //AT24C256页大小
#define AT24C32_PAGE_SIZE 32 //AT24C32页大小
#define MAX_PAGE_SIZE 64 //最大可能用到的缓冲,在定义缓冲时使用
#define AT24C256A_ADDR 0xA0 //CAM8X第一片AT24C256芯片地址
#define AT24C256B_ADDR 0xA2 //CAM8X第二片AT24C256芯片地址(CAM8X标准配置没有焊接这块存储器芯片)
#define AT24C32_ADDR 0xA4 //AT24C32芯片地址
static uint8_t g_PageBuffer[MAX_PAGE_SIZE];//页数据的缓冲
void StdIoInit(void);//uart.c中实现,调试用
//测试AT24C256
void test256(void)
{
uint8_t i;
printf("test at24c256:\n");
At24cxxConfig(AT24C256A_ADDR,AT24C256_PAGE_SIZE);
//测试随机读/写字节
At24cxxWriteByte(333,33);
At24cxxWaitBusy();
i=At24cxxReadByte(333);
printf("ReadByte:%d\n",i);
//测试随机读/写页
for(i=0;i<AT24C256_PAGE_SIZE;i++)
g_PageBuffer[i]=i+2;
At24cxxWritePage(3,g_PageBuffer);
At24cxxWaitBusy();
At24cxxReadPage(3,g_PageBuffer);
printf("Page:\n");
for(i=0;i<AT24C256_PAGE_SIZE;i++)
{
if((i+1)%10==0)
printf("%d\n",g_PageBuffer[i]);
else
printf("%d ",g_PageBuffer[i]);
}
printf("\n");
}
//测试at24c32
void test32(void)
{
uint8_t i;
printf("test at24c32:\n");
At24cxxConfig(AT24C32_ADDR,AT24C32_PAGE_SIZE);
//测试随机读/写字节
At24cxxWriteByte(200,170);
At24cxxWaitBusy();
i=At24cxxReadByte(200);
printf("ReadByte:%d\n",i);
//测试随机读/写页
for(i=0;i<AT24C32_PAGE_SIZE;i++)
g_PageBuffer[i]=i+33;
At24cxxWritePage(6,g_PageBuffer);
At24cxxWaitBusy();
At24cxxReadPage(6,g_PageBuffer);
printf("Page:\n");
for(i=0;i<AT24C32_PAGE_SIZE;i++) {
if((i+1)%10==0)
printf("%d\n",g_PageBuffer[i]); else
printf("%d ",g_PageBuffer[i]); }
printf("\n");
}
int main(void)
{
StdIoInit();//uart打印输出初始化 TwiInit(); //TWI口初始化
test256();
test32();
while(1);
}
测试结果:
基于AVR单片机队列的UART通信模块 【进入博客】【进入论坛】 更新时间:20xx年07月13日 浏览次数: 339 作者: 来
源:
uart.c:
/********************************
基于队列的Mega8 UART通信驱动程序
文件名:uart.c
编译:WinAVR-20070122
硬件:CA-M8X
时钟:外部4MHz
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "queue.h"
#define UART_BUF_SIZE 16 //发送和接收缓冲长度
HQUEUE g_SendQueue; //发送队列句柄
HQUEUE g_RecvQueue;//接收队列句柄
uint8_t g_SendBuffer[UART_BUF_SIZE];//发送缓冲
uint8_t g_RecvBuffer[UART_BUF_SIZE];//接收缓冲
//接收中断SIG_UART_RECV
ISR(USART_RXC_vect )
{
uint8_t c=UDR;
QueueInput(&g_RecvQueue,c);
}
//发送寄存器空中断
ISR (USART_UDRE_vect)
{
if(QueueGetDataCount(&g_SendQueue)>0)//如果发送缓冲队列不空 {
UDR=QueueOutput(&g_SendQueue); //发送一字节
}
else //否则关闭发送中断
{
UCSRB&=~_BV(UDRIE);//关闭数据空中断
}
}
////////////以下为本模块三个接口函数///////////////////////////
//初始化
void UartInit(void)
{
//UART硬件初始化
UCSRB=0;
UBRRH=0;
UBRRL=25; //9600 4MHz
UCSRB=(1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
//创建发送/接收队列
QueueCreate(&g_SendQueue,g_SendBuffer,UART_BUF_SIZE);
QueueCreate(&g_RecvQueue,g_RecvBuffer,UART_BUF_SIZE);
}
//读接收缓冲内的数据,buf为读取缓冲,size为buf能接收的最大长度,返回实际接收的长度
uint8_t UartRecv(uint8_t *buf,uint8_t size)
{
uint8_t i;
for(i=0;i<size;i++)
{
if(QueueGetDataCount(&g_RecvQueue)>0)
{
cli();//以下的队列操作不可被中断
buf[i]=QueueOutput(&g_RecvQueue);
sei();//中断重新允许
}
else
{
break;
}//if else
}//for
return i;//返回读到的数据字节数
}
//发送数据 ,buf为发送数据缓冲器,size为要发送的长度 void UartSend(uint8_t *buf,uint8_t size)
{
uint8_t i;
cli(); //以下的队列操作不可被中断
for(i=0;i<size;i++)
QueueInput(&g_SendQueue,buf[i]);
sei(); //中断重新允许
UCSRB|=_BV(UDRIE);//数据空中断允许
}
//////////////////////////////////////////////////////////
uart.h:
//uart.h
#ifndef UART_H
#define UART_H
void UartInit(void);
uint8_t UartRecv(uint8_t *buf,uint8_t size);
void UartSend(uint8_t *buf,uint8_t size);
#endif
queue.c:
/********************************
队列管理模块
文件名:queue.c
编译:WinAVR-20070122
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB:
Email: changfutong@sina.com
*******************************/
#include <stdint.h>
#include "queue.h"
//向队列插入一字节
void QueueInput(PHQUEUE Q,uint8_t dat)
{
if(Q->data_count < Q->buf_size)
{
Q->pBuffer[Q->in_index]=dat; //写入数据
Q->in_index=(Q->in_index+1) % (Q->buf_size);//调整入口地址 Q->data_count++; //调整数据个数(此操作不可被中断) }
else
{
if(Q->error<255)
Q->error++;
}
}
//从队列读出一字节
uint8_t QueueOutput(PHQUEUE Q)
{
uint8_t Ret=0;
if(Q->data_count > 0)
{
Ret=Q->pBuffer[Q->out_index]; //读数据
Q->out_index=(Q->out_index+1) % (Q->buf_size); //调整出口地址 Q->data_count--;
}
return Ret;
}
//获得队列中数据个数
uint8_t QueueGetDataCount(PHQUEUE Q)
{
return Q->data_count;
}
//清空队列,执行时不可被中断
void QueueClear(PHQUEUE Q)
{
Q->in_index=0;
Q->out_index=0;
Q->data_count=0;
Q->error=0;
}
//初始化一队列
void QueueCreate(PHQUEUE Q,uint8_t *buffer,uint8_t buf_size) {
Q->pBuffer=buffer;
Q->buf_size=buf_size;
QueueClear(Q);
}
queue.h:
//queue.h
#ifndef QUEUE_H_
#define QUEUE_H_
//队列数据结构
typedef struct QUEUE_S
{
uint8_t in_index;//入队地址
uint8_t out_index;//出队地址
uint8_t buf_size; //缓冲区长度
uint8_t *pBuffer;//缓冲
volatile uint8_t data_count; //队列内数据个数
uint8_t error;
}HQUEUE,*PHQUEUE;
void QueueInput(PHQUEUE Q,uint8_t dat);
uint8_t QueueOutput(PHQUEUE Q);
uint8_t QueueGetDataCount(PHQUEUE Q);
void QueueClear(PHQUEUE Q);
void QueueCreate(PHQUEUE Q,uint8_t *buffer,uint8_t buf_size);
#endif