全自动可遥控旗帜升降系统的设计与制作
摘要:
全自动可遥控旗帜升降系统以AVR单片机为核心,由PWM对电机进行变频调速,高精度光电编码器测速,闭环式电路控制。能够利用按键输入相应的指令控制旗帜的升降,还可以实现在特定位置停留的功能。利用LCD可以显示旗帜的实时高度和相应的控制命令,用LED指示旗帜是否处于半旗状态,具有语音提示报警功能,并可利用无线模块实现遥控功能,准确均匀地实现旗帜的升降。
关键词:PWM调速无线数据传输MP3解码码盘闭环控制STA013
(一)系统特性
利用L298和PWM配合可以对电机实现高精度的速度调节。
具有最小7.5度的角度精度。系统精度高达0.05MM.
利用PID算法构成的闭环式控制电路具有精度高,实时反馈等优点。
以MEGA8为核心,使用cc1000芯片,速度高达38400bps。
利用STA013芯片对MP3进行解码,实现音乐的播放与控制。
系统采用高达1G的SD卡存储MP3及各种音频数据,容量大。
(二)系统框图
PWM
M
L298
脉冲
74LS04
整形放大
电动机
码盘
主机
MCU
MEGA32
INT
电源:
LCD:128*32
LED:显示半旗状态
KEYBOARD
TWI
C
U
SD卡
STA013
扬声器
上限位
下限位
上下限位开关
鼓风机
无线模块
CC1000芯片
LM386
(三)方案比较与论证
(1)电机的选择
方案一:采用步进电机
步进电机是一种将电脉冲转化为角位移的执行机构,具有精度高易调控的特点。但是步进电机的力矩会随转速的升高而下降,调速潜力不大。并且低速时可以正常运转,但若高于一定速度就无法启动,并伴有啸叫声。所以放弃使用步进电机。
方案二:采用减速电机+闭环控制
减速电机是减速器与电机联体,相对于步进电机具有高效率、传递力矩大、可靠性高等显著优点。但是如果采用开环的控制方式精度不高。为了提高它的精度,我们采用减速电机+闭环控制的方案。闭环控制利用PID算法通过检测电机速度对电机进行实时的调节,从而提高系统的调节精度。
(2)电机升旗方案
方案一:采用恒速上升的方案使用步进电机
如果采用恒速上升的方法则只需要测定启动初期的速度即可,上升过程中不进行测速。但是系统处在外界,难免会受到各种因素的影响,如果旗帜在上升的过程中受到影响速度改变的话就会使升旗出现误差。所以这种方式虽然简单但是非常不稳定。
方案二:闭环控制PID算法
闭环控制是从输出量变化取出控制信号作为比较量反馈给输入端控制输入量,一般这个取出量和输入量相位相反,所以也叫负反馈控制。
PID是比例,积分,微分的缩写。它是本系统实现闭环控制的核心。
比例调节作用:是按比例反应系统的偏差,系统一旦出现了偏差,比例调节立即产生调节作用用以减少偏差。比例作用大,可以加快调节,减少误差,但是过大的比例,使系统的稳定性下降,甚至造成系统的不稳定。
本系统采用PID算法,适当的调节比例,积分,微分的份额,实现闭环控制电动机的速度以保证旗帜准时升到顶。
(3)速度测量方式
方案一:单脉冲计数
单脉冲计数的方式是电动机每转一周,系统就获得一个脉冲。这种计数方式虽然比较简单,但是它的精度不够高。
方案二:透过式高精度光电编码器(码盘)
原理图如图所式:
方案三:反射式码盘开关
反射式码盘开关的原理与透过式码盘开关的原理类似,但是它必须采取静态固定的方式,对固定的位置、牢固性等的要求较高。而透过式码盘开关则不同,对固定的要求没有那么高。所以从这方面考虑不采用反射式码盘开关。
(4)调速方式
方案一:调压
通过调压的方式进行电机调速的原理是将一部分电压转化成热,这样就浪费了能源,与建设节约型社会的宗旨相背离,况且对于移动式的设备来说节能也至关重要。调压方式下转速与外界的负载有关,另外,当电压较低时,电机力矩小,输出不稳定。
方案二:PWM调速
PWM调速电路图:
L298:
L298是内含两个H桥的高电压大电流双全桥式驱动器,是性能优越的小型直流电机驱动芯片之一。接受标准TTL逻辑电平信号,可驱动电压46V、每相2.5A及以下的减速电机或者是步进电机,在4--46V的电压下,可以提供2A的驱动电流。它具有两个使能输入端,在不受输入信号影响的情况下允许或禁止器件工作,每个桥对的下部三极管的发射极接在一起并引出,用以外接检测电阻,它设置了一附加电源输入端使逻辑部分在低电压下工作。L298还有过热自动关断功能,并有反馈电流检测功能,符合电机驱动的需要。
L298的图解:
L298引脚图示:
(5)音乐同步播放
方案一:利用系统控制录音机
方案二:语音存储芯片
语音存储芯片同样存在无法精确巧妙跳跃式地控制音乐播放的缺点。
方案三:模拟MP3按键控制MP3
这种方式只是将MP3的按键控制移植到系统中。利用系统模拟按键的功能实现对MP3的控制。但是系统要求可以跳跃式地任意播放曲目,MP3尚不能满足这种要求。
方案四:MP3音频解码芯片STA013+1G的SD卡
采用STA013芯片+1G的SD卡的音频处理模式,利用LM386进行音频的放大,利用TWI或者是I2C进行通信,能够对音乐播放进行较好的控制。可以实现只要输入音乐的名称就可以播放的功能,刚好满足了系统语音报错和音乐播放的功能要求,另外超大容量的存储芯片可以使系统功能具有较大的扩展空间。
STA013芯片:
STA013芯片是一颗标准的MP3音讯译码器(audiodecoder)。若不考虑体积大小、耗电量、额外的复杂功能、生产成本,它是能以单一的技术整合方案,来实现一台可用的(workable)MP3播放机。STA013具有高可设定性(configurable)的功能,不同的设定组合可以满足不同的应用需求。另外STA013会忽略掉不属于MP3格式的数据—它们不会产生声音。所以,不需要另外设计程序,以求事先将MP3档案的ID3标签(tag)去除掉。可以直接将一个完整的MP3档案,传送给STA013处理。万一将毁损的MP3数据串流(例如:由于下载中断,造成档案被截断)输入给STA013,它会忽略掉大部分已经毁损的数据;只有一部分毁损的数据,会产生一个短暂的吱喳声(通常这视紧接在毁损部位后面的数据而定),但是STA013会自动立即与毁损部位后面的正常数据同步。
STA013芯片MP3解码原理图如下:
STA013引脚连接图:
MP3音频解码器的原理图:
通信
解码
判断是否读取数据
读取数据
送数据
是
否
无动作
播放音乐
(6)限位开关
方案一:行程开关
该开关的关键之处就是在旗子到达最顶端或者最底端时对行程开关造成碰撞从而使开关接触,这一动作会即时反馈到单片机,单片机对此做出相应的反应。但是,小型行程开关弹簧触片太短太小以至于使开关起作用需要的力较大,有可能在使用过程中出现意外,大型行程开关装在该设备上极不美观。所以放弃该方案。
方案二:霍尔传感器
霍尔元件是基于霍尔效应来测量电位差的一种器件,霍尔元件与磁铁配合是一种很好的可实现非接触式信号传递的组合,也称霍尔传感器。霍尔传感器利用霍尔元件中的磁通量使电子在霍尔元件的一侧聚集,在霍尔元件两侧产生电压差,并由霍尔器件检测出霍尔电压信号,经过放大器放大,该电压信号精确地反映原边电流,或者是磁通量的变化。从而根据事先预定好的情况做出相应的反应。但是这又存在另外一个问题,如果旗杆使用铁制的则将会被磁铁磁化从而对霍尔传感器产生不良影响。
这套仪器采用合金材料制作旗杆,不会或极少被磁化,所以综合考虑以后选择方案二。
霍尔元件连接电路如图所示:
(7)显示方式
系统具有的显示功能是体现人机交互的重要方式。通过系统的显示信息,人们可以知道它在干什么,从而决定自己应该干什么。
方案一:采用LED(数码管)显示
LED(数码管)是light-emittingdiode的缩写,它经过合理的设置可以完成显示旗帜位置信息的任务,并且经济耐用。同时LED具有高亮度,高刷新率的优点,能提供宽达160°的视角,可以在较远的距离上看清楚。但是它的显示存在信息量少,显示不直观,不易理解的缺点。
方案二:采用LCD(液晶屏)显示
LCD(液晶屏)是LiquidCrystalDisplay的缩写,它具有汉字显示的功能,不但可以指示旗帜的位置,还可以显示相应的控制命令,如升旗、降旗等,信息量丰富且直观易懂。另外,液晶显示有功耗低,体积小,质量轻,寿命长,不产生电磁辐射污染等优点。
综合二者的优缺点,半旗的指示用LED显示,旗帜位置和控制命令的显示用LCD显示。
(8)掉电检测
系统要求在任意时刻掉电后系统的数据信息不丢失,但是又不可能每一时刻的数据都写入EEPROM,因为EEPROM擦写次数有限而单片机数据变化迅速。因此必须进行掉电检测,只有判断掉电以后才可以将数据写入EEPROM。
电路连接状况如下:
VCC输入一个12V的电压使稳压电容保持4.7V的电压。当系统掉电时会破坏稳压电容的电压,反馈到系统使系统将数据写入EEPROM。此时系统CPU靠4700uF的大电容供电,为了防止其它耗电元件耗电,在电容之前接一个二极管,保证单向连通。
(9)掉电数据不丢失的实现
EEPROM是电子可擦写可编程只读存储器(electricallyerasableprogrammableread-onlymemory)的缩写。它是一种非挥发性记忆体(Memory),与抹除式唯读记忆体(EPROM)相似,电源消失后,储存的资料(Data)依然存在,要消除储存在其中的内容,不是用紫外线照射方式,而是以电子讯号直接消除即可。一般消费者常用的电视遥控器,内部所用的一、二千位元(bit)的非挥发性记忆体(NVRAM),通常就是EEPROM。
DRAM断电后存在其中的数据会丢失,而EEPROM断电后存在其中的数据不会丢失。另外,EEPROM可以清除存储数据和再编程。正是由于EEPROM具有以上特点,该器件可广泛应用于对数据存储安全性及可靠性要求高的应用场合,如门禁考勤系统,测量和医疗仪表,非接触式智能卡,税控收款机,预付费电度表或复费率电度表、水表、煤气表以及家电遥控器等应用场合。该类型存储器在可靠数据存储领域会获得越来越广泛的应用。
在平常情况下,EEPROM是只读的,需要写入时,在指定的引脚加上一个高电压即可写入或擦除,而且其擦除的速度极快!它可以电改写,也可以逐字节改写。
但是,EEPROM有固定的使用寿命,这是指某一位由1写为O或由O写为1的次数。不同厂家的产品,相同厂家不同型号、系列的产品,它们的寿命也不尽相同,100万次为常见主流产品。这种寿命已经足以满足本系统的要求。
为了实现在任意时刻任意位置掉电后旗帜位置数据不丢失的功能,本系统采用EEPROM作为数据存储芯片。
(10)遥控方式
方案一:FM模拟遥控
方案二:数字遥控
这种方案采用速度高达38400bps的无线传输模块进行数据传输。采用CC1000芯片,以MEGA8为核心,充分显示了现代单片机技术在无线传输中应用的优势。具有传输距离远,速率高,不易受干扰等优点。而且可传输的信息多种多样,只要能够转化成数字信号就能传输。所以采用这种传输方式作为无线模块。
(11)出错提示音
为了防止误操作,系统需要对一些情况进行错误提示。
这种方式虽然简单但是不能体现人性化,如果操作者不知道自己的错误往往造成操作者摸不清头脑,甚至以为设备出了毛病。
方案二:嘀嘀声作为错误提示
这种方式虽然可以让操作者知道自己操作错误但不能知道为什么,功能仍然不够完善。
方案三:语音提示
这种方式更能够体现人机交互。当操作者无操作时,系统会利用音频播放设备播放相应的提示音,使人们知道式自己操作不当,并且知道哪里出了错。这样更能够体现人性化。
(12)分机设计
分机的设计相对简单,包括键盘模块,无线模块,LCD显示模块和MCU模块,在此不过多叙述。
LCD的电路连接如图所示:
分机整体电路图如图所示:
(四)系统工作原理
主机电路图:
工作原理:系统由按键控制,利用PWM驱动电机按照程序设计的转速和方向利用滑轮实现旗帜的升降。按上升键以后,旗帜按照预定速度匀速上升,并由音频设备流畅地演奏国歌。上升到最高端时,旗帜停止上升,国歌停奏。按下降键后,旗帜按照预定速度匀速下降,此时音频设备不演奏国歌。按特定的停止键可以实现在特定的位置上停留。旗帜上安装有磁铁,旗杆的顶端和底端安装有霍尔元件,当旗帜上升到顶端或下降到底端时,霍尔元件做出感应,以免误操作。当旗帜在顶端时如果按上升键或者旗帜在底端时按下降键则会听到语音报警提示音。由测量模块实时测量旗帜的高度并由LCD显示出来。
由一个半旗开关控制旗帜是否处于半旗状态,由LED指示是否处于半旗状态。半旗状态下(根据《国旗法》)升旗时,按上升键,奏国歌,国旗从最低端上升到最高端之后,国歌停奏,然后自动下降到总高度的2/3高度处停止;降旗时,按下降键,国旗先从2/3高度处上升到最高端,再自动从最高端下降到底之后自动停止,国歌停奏。
由EEPROM实时记录旗帜的位置,不论旗帜是在顶端还是在底端,关断电源之后重新合上电源,旗帜所在的高度数据不会丢失,显示不变。
用停止键可以实现任意位置的急停。
另外,利用无线模块可以实现遥控旗帜的升降及停止。
程序流程图:
在系统的框图完成以后,小组成员对自己的工作已经有了比较明确的认识,根据个人的具体情况大家进行了分工。擅长程序编写的就负责系统所需程序的编写工作,精通硬件的则负责元器件的选购和硬件的架设,而我则负责文档的整理,毕竟团结协作才算是一个团体嘛。
(六)系统功能调式与结果测量
虽然我们在制作的过程中进行过无数次的测试,但是最后还是要进行总体性能的测试。9月11号上午,我们对经过多次调式的系统进行了总体性能的测试。
测试的内容主要有升旗、降旗、半旗、语音报错、掉电数据保存、LCD、LED显示等。
(七)参考文献:
(1)《AVR单片机C语言开发入门指导》沈文Eaglelee詹卫前编著清华大学出版社
(2)《音讯放大器LM386简介》(作者:陈明周(2003-04-07))
(八)本系统部分程序源代码
**AllRightsReserved
**
**V1.2.0
/*****************************************************************************
**文件名:main.c工程主文件
******************************************************************************/
/********************************************************
//eeprom0x0a:旗帜位置0x0b:是否半旗
//0:底;244,顶:其他:中间1:y0:n
*********************************************************/
#include"config.h"
externuint16haveused_time;//已经耗时
externuint16s,get_s,T;
externuint8S;
uint8position_flag=0;//旗帜位置,0:底254:顶1~180:高度cm
uint8if_half_flag=0;//是否半旗,默认0:否1:是
uint8move_flag;//是否在移动
volatileuint8up_or_down=1;//在上升(1)还是下降(0)
volatileuint8half_up_or_down;//半旗升旗到顶(1),还是半旗降旗到顶(0)。
volatileuint8lcd_fresh_flag;//是否允许刷新LCD
volatileuint8speed_change_flag=0;//是否允许调速度
uint8SIO_buff[10];//遥控数据缓冲
uint8lcd_show[32];//LCD显示缓冲
uint8timer_i=0;//内部计时循环
voidmain(void)
{
uint8read;//readkey
//floatpass_speed,forward_speed;
init_devices();
position_flag=EepromRead(0x0a);//读旗帜位置和是否半旗
if(position_flag==255)position_flag=0;
if_half_flag=EepromRead(0x0b);
if(if_half_flag>1)if_half_flag=0;//防止读到错误数据
strcpy(lcd_show,"高度:000CM时间:000MS");
Lcd_init();
Lcd_stringdisp("程序启动",8);
music_state_ask("S");
/****************************************************************/
//半旗状态显示
if(if_half_flag==1){half_flag_show;lcd_show[15]=0x08;}
else{half_flag_unshow;lcd_show[15]=0x0A;}
while(1)
read=kbscan();//键盘操作
switch(read)
/*----------------------------------------------------------------------------*/
case'u'://up
if(move_flag==1)break;//运动中按键屏蔽
if(position_flag<254){//只有在顶端以下才有动作
if(position_flag==0)
music_state_ask("PGUOGE.MP3");//音乐
else
music_state_ask("C");
Delay1s(30);
if(if_half_flag==1){
Lcd_stringdisp("半旗模式升旗",12);
MOTOR(1,170,46);
half_up_or_down=1;//半旗升旗到顶,霍尔中断的时候判断。
}else{
Lcd_stringdisp("升旗",4);
}
break;
music_state_ask("PA05.MP3");//音乐
case'd'://down
if(position_flag!=0){//只有在底部以上才有动作
MOTOR(1,60,15);//半旗模式,上升1/3降旗
half_up_or_down=0;//半旗降旗到顶,霍尔中断的时候判断。
Lcd_stringdisp("半旗模式降旗",4);
else{
MOTOR(0,position_flag,46-haveused_time);
Lcd_stringdisp("降旗",4);
case'h'://half
if(position_flag!=0)break;//如果不在底端,不允许改变半旗模式
half_flag_unshow;//如果是,则改为不是
if_half_flag=0;
lcd_show[15]=0x0A;//非半旗标志
Lcd_stringdisp("取消半旗模式",12);
half_flag_show;
if_half_flag=1;
lcd_show[15]=0x08;//半旗标志
Lcd_stringdisp("设定半旗模式",12);
case's'://stop
if(move_flag==0)break;//停止中按键屏蔽
if((position_flag!=254)&&(position_flag!=0))
{//只有在运动时才有动作
all_stop();//停止操作
Lcd_stringdisp("停止",4);
default:break;
}//switchend
/******************************************************************************/
//遥控模块
if(Com_R_count(&RTbuf_UART0)!=0)
Com_getstring(SIO_buff,4,&RTbuf_UART0);
switch(SIO_buff[0])
//动态显示
if(lcd_fresh_flag==1)
lcd_fresh_flag=0;
lcd_show[25]=(haveused_time%100)/10+0x30;
lcd_show[26]=haveused_time%10+0x30;
lcd_show[27]=timer_i/20+0x30;//0.1s
if(up_or_down==1)
lcd_show[31]=0x1E;//向上标记
lcd_show[8]=s/100+0x30;//位置显示
lcd_show[9]=(s%100)/10+0x30;
lcd_show[10]=s%10+0x30;
position_flag=s;
elseif(up_or_down==0)//降旗
lcd_show[31]=0x1F;//向下标记
lcd_show[8]=(180-s)/100+0x30;//位置显示
lcd_show[9]=((180-s)%100)/10+0x30;
lcd_show[10]=(180-s)%10+0x30;
position_flag=180-s;
Lcd_stringdisp(lcd_show,32);
//电机调速
if(speed_change_flag==1)
speed_change_flag=0;
if((S-s)<=0)
all_stop();//距离到,停止
if((T-haveused_time)<=0)
timer_i++;
if(timer_i==200)
haveused_time++;
timer_i=0;
s=get_s/130;//每厘米130个脉冲
//forward_speed=(S*20-get_s)/(T*10-haveused_time*10-timer_i/20);
//forward_speed=(S-s)/(T-haveused_time);
//pass_speed=get_s/(haveused_time*10+timer_i/20);
//pass_speed=s/haveused_time;
if((S-s)>2)
{//剩下两厘米,不再调速度
if(((S-s)/(T-haveused_time))>(s/haveused_time))//未来速度大于过去速度,增加
//if(forward_speed>pass_speed)
if(OCR2<0x99)OCR2++;
while(ASSR&(1< elseif(((S-s)/(T-haveused_time))<(s/haveused_time)) if(OCR2>40)OCR2--; **文件名:key_driver.c键盘驱动程序 #defineport_PINPINB//键盘端口 externvoidDelay100us(uint8n); externuint8kbscan(void); //PB4--7 uint8kbscan(void) uint8key=0; if((port_PIN&0xF0)!=0xF0)//有键按下 Delay100us(200);//20ms去抖动 if((port_PIN&0xF0)!=0xF0) //键值 switch(port_PIN&0xF0)//++++分别为updownhalfstop case0xE0:key='u'; case0xD0:key='d'; case0xB0:key='h'; case0x70:key='s'; /*case0x18:key=0x03;//ud0011加速 case0x48:key=0x09;//dh1001停止 case0x60:key=0x0c;//hs1100减速 */ default:key=0; while((port_PIN&0xF0)!=0xF0);//等待按键抬起 returnkey; /************************************************************************** **文件名:lcddriver.c液晶驱动程序 ***************************************************************************/ /************************************************************************ 函数名:voidlcd_init(void) 功能:LCD初始化 输入:无 输出:无 返回:无 备注: *************************************************************************/ voidLcd_init(void){ //WDR();//喂狗 Lcd_scomm(Lcd_Set_Function_Basic);//30H--基本指令操作 Lcd_scomm(Lcd_Clear);//清除显示 Lcd_scomm(Lcd_Set_Entrance_Point+2);//指定在资料写入或读取时,光标的移动方向 Lcd_scomm(Lcd_Display_Status+4);//开显示,关光标,不闪烁 //Lcd_Status_Free=TRUE; 函数名:Lcd_busy 功能:测忙碌子程序 输入: 输出: 返回:0:不忙 1:忙 uint8Lcd_cbusy(void) uint8lcdtmp; uint16tmp=1; Lcd_DDR=0x00; Lcd_Control_DDR=Lcd_Control;//0xff; Lcd_RS_Dis; Lcd_RW_En; Lcd_E_En; while((Lcd_PIN&0x80)&&(tmp!=0))tmp++; lcdtmp=Lcd_PIN; Lcd_E_Dis; Lcd_DDR=0xff; return(lcdtmp); /*********************************************************************** 函数名:Lcd_scomm 功能:向液晶送一个命令 输入:s_scomm:送入命令 返回: ************************************************************************/ voidLcd_scomm(uint8s_comm) Lcd_cbusy(); Lcd_RW_Dis; Lcd_PORT=s_comm; 函数名:Lcd_sdata 功能:向液晶送一个数据 输入:s_date:送入数据 voidLcd_sdata(uint8s_data) Lcd_RS_En; Lcd_PORT=s_data; 函数名:Lcd_rdata 功能:从显示读入一个数据 输入: 输出:读入数据 备注:没有使用 uint8Lcd_rdata(void) uint8Rtemp; DDRC=0xff; PORTC|=0x01; PORTC|=0x02; PORTC|=0x04; DDRC=0x00; Rtemp=Lcd_PIN; PORTC&=~0x04; return(Rtemp); /********************************************************************** **函数名称:Lcd_stringdisp **功能描述:全屏显示 **输入:*p:显示缓冲指针 **displen:显示字符长度 **输出: **全局变量:无 **调用模块:无 **说明: **注意:32=8字*2行*2(字节) **0x080x188个字每行 **********************************************************************/ voidLcd_stringdisp(uint8*p,uint16displen) uint8i,tmp; uint16j=0,k; k=0; for(j=0;j<=(uint16)displen/32&&displen>0;j++) if(p[k]=='\0'||(p[k]=='\0'&&p[k+1]=='\0'))break; Lcd_scomm(Lcd_Clear); for(i=0;i<32&&i Lcd_sdata(p[k++]); tmp=Lcd_cbusy(); if((tmp>=0x08&&tmp<0x10)) Lcd_scomm(Lcd_Set_Ddram+0x10); elseif(tmp>=0x18)Lcd_scomm(Lcd_Set_Ddram); //Delay1s(2);//延时两秒 **文件名:eeprom.ceeprom驱动程序 //eepromdriver intEepromWrite(intlocation,unsignedcharbyte); unsignedcharEepromRead(intlocation); //intEepromStingWrite(intlocation,uint8str[]); //intEepromStingRead(intlocation,uint8str[]); //写一个字节 intEepromWrite(intlocation,unsignedcharbyte) unsignedcharoldSREG; EEAR=location; EEDR=byte; oldSREG=SREG; SREG&=~0x80; EECR|=0x04; EECR|=0x02; while(EECR&0x02); SREG=oldSREG; return0; //读一个字节 unsignedcharEepromRead(intlocation) EECR|=0x01; return(EEDR); /*出错:操作数为数组长度减一 //写一个数组 //输入数组指针 //输出写结果 intEepromStingWrite(intlocation,uint8str[]) uint8i; for(i=0;i<=sizeof(str);i++) EEPROMwrite(location,str[i]); location++; //读一个数组 //输入地址 intEepromStingRead(intlocation,uint8str[]) str[i]=EEPROMread(location); **文件名:unit.c延时函数 /*************************************************************************** **延时公式 **i*500-100US(8>i>2) **i*500(255>i>8) **i=1精确延时100us **i=2精确延时660US **i=6精确延时2.9ms **i=10精确延时5ms **i=2010ms **i=10050ms **i=200100ms **i=255128ms *****************************************************************************/ voidDelay100us(uint8n) for(i=36;n!=0;n--) while(--i); voidDelay1s(uint8n) n=n*10; for(;n!=0;n--){ Delay100us(200); **文件名:TWI.cTWI通讯驱动函数 //Twi标准波特率: //低速100KHz //高速400KHz //Twi_状态和地址变量 staticvolatileeTwi_StateTypeTwi_State;//用于独占TWI的资源分配的信号量机制 staticvolatileuint8Twi_DeviceAddrRW; //发送缓冲区 staticuint8Twi_SendData[TWI_SEND_DATA_BUFFER_SIZE]; CirQueueTwi_SendBuffer={0,0,0,Twi_SendData,TWI_SEND_DATA_BUFFER_SIZE,QUEUE_OK,TWI_DATA_FREE,0}; //接收缓冲区 staticuint8Twi_ReceiveData[TWI_RECEIVE_DATA_BUFFER_SIZE]; CirQueueTwi_ReceiveBuffer={0,0,0,(uint8 *)Twi_ReceiveData,TWI_RECEIVE_DATA_BUFFER_SIZE,QUEUE_OK,TWI_DATA_FREE,0}; //本地缓冲区,当为从机时,与TWI接口 staticuint8LocalBuffer[TWI_LOCAL_DATA_BUFFER_SIZE]; CirQueueTwi_LocalBuffer={0,0,0,LocalBuffer,TWI_LOCAL_DATA_BUFFER_SIZE,QUEUE_OK,TWI_DATA_FREE,0}; //错误代码 staticvolatileuint8TwiErrorCode; //指向接收处理函数的指针,当本机被选中从接收时调用函数:Twi_SlaveReceive staticvoid(*Twi_SlaveReceive)(uint8receiveDataLength,uint8*recieveData); //指向发送处理函数的指针,当本机被选中从发送时调用函数:ITwi_SlaveTransmit staticuint8(*Twi_SlaveTransmit)(uint8transmitDataLengthMax,uint8*transmitData); /******************************************************************************** **函数名称:Twi_SetBitrate **功能描述:设置TWI总线的速率 **输入:bitrateKHZ:总线速率 **输出: **全局变量: **注意: ********************************************************************************/ voidTwi_SetBitrate(uint16bitrateKHz){ uint8bitrate_div; WDR();//喂狗; //SCLfreq=F_CPU/(16+2*TWBR)) #ifdefTWPS0 //对于用速率分频的AVR(mega128) //SCLfreq=F_CPU/(16+2*TWBR*4^TWPS) //setTWPStozero TWSR&=~(1< TWSR&=~(1< #endif //计算分频 bitrate_div=((F_CPU/1000)/bitrateKHz); if(bitrate_div>=16) bitrate_div=(bitrate_div-16)/2; TWBR=bitrate_div; /**************************************************************************** **函数名称:Twi_MasterSend **功能描述:主发送 **输入:deviceAddr:从机地址 **length:数据长度 ***data:发送缓冲区指针 **输出:发送的字节数 **调用模块: uint8Twi_MasterSend(uint8deviceAddr,uint8length,uint8*data) uint8i,result=0; //等待总线准备完成 while(Twi_State);//此处为独占TWI的信号量机制 //设置状态 Twi_State=TWI_MASTER_TX; //准备数据 Twi_DeviceAddrRW=(deviceAddr&0xFE);//RW为0:写操作 if(Twi_SendBuffer.status==TWI_DATA_FREE) InitQueue(&Twi_SendBuffer);//清空上次发送的信息 Twi_SendBuffer.status=TWI_DATA_SEND; for(i=0;(i EnQueue(&Twi_SendBuffer,*data++); result=i; //发送开始条件 Twi_Start(); while(Twi_State); i=0; while(((TwiErrorCode==TW_MT_SLA_NACK)||(TwiErrorCode==TW_MT_ARB_LOST)||(TwiErrorCode==TW_MT_DATA_NAC K))&&i i++; if((TwiErrorCode==TW_MT_DATA_NACK)&&(Twi_SendBuffer.count==0)) Delay(255); Twi_SendBuffer.status=TWI_DATA_FREE; if(i>=TWI_FAIL_MAX) return(FALSE); return(result-Twi_SendBuffer.count); **函数名称:Twi_MasterReceive **功能描述:主模式接收 ***data缓冲区指针 **输出:返回接收到的字节数 ****************************************************************************/ uint8Twi_MasterReceive(uint8deviceAddr,uint8length,uint8*data) uint8i,result; Twi_State=TWI_MASTER_RX; //保存数据 Twi_DeviceAddrRW=(deviceAddr|0x01);//RW为1:读操作 if(Twi_ReceiveBuffer.status==TWI_DATA_FREE) InitQueue(&Twi_ReceiveBuffer);//清空上次发送的信息 Twi_ReceiveBuffer.status=TWI_DATA_RECEIVE; Twi_ReceiveBuffer.memo=length; //等待数据准备好 while(Twi_State);//注意有可能引起死循环 while(((TwiErrorCode==TW_MR_SLA_NACK)||(TwiErrorCode==TW_MR_ARB_LOST)||(TwiErrorCode==TW_MR_DATA_NAC if((TwiErrorCode==TW_MR_DATA_NACK)&&(Twi_ReceiveBuffer.count>=length)) //Twi_Stop(); //取数据 for(result=0;(result *data++=DeQueue(&Twi_ReceiveBuffer); Twi_ReceiveBuffer.status=TWI_DATA_FREE; return(result);//返回接收到的字节数; /********************************************************************************* **函数名称:Twi_MasterSendNI **功能描述:主模式非中断发送 ************************************************************************************/ uint8Twi_MasterSendNI(uint8deviceAddr,uint8length,uint8*data) uint8retval=TWI_OK; //关Twi_中断 TWCR&=~(1< Twi_WaitForComplete(); //发送器件写地址 Twi_SendByte(deviceAddr&0xFE); //检查器件是否可用 if(TWSR==TW_MT_SLA_ACK) //发送数据 while(length) Twi_SendByte(*data++); length--; //如未回应器件地址,停止发送,返回错误 retval=TWI_ERROR_NODEV; //发送停止条件,保持TWEA以便从接收 Twi_Stop(); while(!(TWCR&(1< //开Twi_中断 TWCR|=(1< returnretval; **函数名称:主模式非中断接收 **功能描述: uint8Twi_MasterReceiveNI(uint8deviceAddr,uint8length,uint8*data) //发送器件读地址 Twi_SendByte(deviceAddr|0x01); if(TWSR==TW_MR_SLA_ACK) //接收数据并回应 while(length>1) Twi_Ack(); *data++=TWDR; //接收数据无回应(末位信号) Twi_NoAcK(); TWCR|=TWIE; **函数名称:Twi_SlaveReceiveService **功能描述:此函数在本机被选中为从写入时运行 **输入:receiveDataLength:接收数据长度 ***receiveData:数据缓冲区指针 *********************************************************************************/ voidTwi_SlaveReceiveService() //此函数在本机被选中为从写入时运行 //接收到的数据存入本地缓冲区 if((Twi_LocalBuffer.status==TWI_DATA_RECEIVE)&&(Twi_ReceiveBuffer.status==TWI_DATA_RECEIVE)) for(i=0;!EmptyQueue(&Twi_ReceiveBuffer);i++) EnQueue(&Twi_LocalBuffer,DeQueue(&Twi_ReceiveBuffer)); Twi_LocalBuffer.status=TWI_DATA_READY; **函数名称:Twi_SlaveTransmitService **功能描述:此函数在本机被选中为从读出时运行 **输入:transmitDataLengthMax uint8Twi_SlaveTransmitService(void) uint8i=0; //此函数在本机被选中为从读出时运行 //要发送的数据存入发送缓冲区 if((Twi_LocalBuffer.status==TWI_DATA_SEND)&&Twi_SendBuffer.status==TWI_DATA_FREE) InitQueue(&Twi_SendBuffer);//清除上次发送的信息 for(i=0;(Twi_LocalBuffer.count>0)&&(Twi_SendBuffer.errorcode!=QUEUE_OVERFLOW);i++) EnQueue(&Twi_SendBuffer,DeQueue(&Twi_LocalBuffer)); Twi_LocalBuffer.status=TWI_DATA_FREE; returni; **函数名称: **功能描述:Twi_(TWI)中断服务程序 **输入: #pragmainterrupt_handlertwi_isr:iv_TWI voidtwi_isr(void) //读状态位 uint8status; status=TWSR&TWSR_STATUS_MASK; switch(status) //主方式 caseTW_START://0x08:START已发送 caseTW_REP_START://0x10:重复START已发送 //发送器件地址 Twi_SendByte(Twi_DeviceAddrRW); //主发送,主接收状态码 caseTW_MT_SLA_ACK://0x18:SLA+W已发送;接收到ACK caseTW_MT_DATA_ACK://0x28:数据已发送;接收到ACK if((Twi_SendBuffer.count>0)&&(Twi_SendBuffer.status==TWI_DATA_SEND)) Twi_SendByte(DeQueue(&Twi_SendBuffer)); Twi_State=TWI_IDLE; caseTW_MR_DATA_NACK://0x58:接收到数据;NOTACK已返回 //保存最终数据 EnQueue(&Twi_ReceiveBuffer,TWDR); //继续发送条件 caseTW_MR_SLA_NACK://0x48:SLA+R已发送,接收到NOTACK caseTW_MT_SLA_NACK://0x20:SLA+W已发送,接收到NOTACK caseTW_MT_DATA_NACK://0x30:数据已发送,接收到NOTACK caseTW_MT_ARB_LOST://0x38:SLA+W或数据的仲裁失败 //释放总线 TWCR=TWCR&TWCR_CMD_MASK|(1< caseTW_MR_DATA_ACK://0x50:接收到数据,ACK已返回 //保存接收到的数据位 //检查是否接收完 caseTW_MR_SLA_ACK://0x40:SLA+R已发送,接收到ACK if(!FullQueue(&Twi_ReceiveBuffer)&&(Twi_ReceiveBuffer.count<(Twi_ReceiveBuffer.memo-1))&&(Twi_Receiv eBuffer.status==TWI_DATA_RECEIVE)) //数据位将接收,回复ACK(传送更多字节) //数据位将接收,回复NACK(传送最后字节) //从接收状态码 caseTW_SR_SLA_ACK://0x60:自己的SLA+W已经被接收,ACK已返回 caseTW_SR_ARB_LOST_SLA_ACK://0x68:SLA+R/W作为主机的仲裁失败;自己的SLA+W已经被接收,ACK已返回 caseTW_SR_GCALL_ACK://0x70:接收到广播地址,ACK已返回 caseTW_SR_ARB_LOST_GCALL_ACK://0x78:SLA+R/W作为主机的仲裁失败;接收到广播地址,ACK已返回 //被选中为从写入(数据将从主机接收) Twi_State=TWI_SLAVE_RX; //缓冲准备 if((Twi_ReceiveBuffer.status==TWI_DATA_FREE)&&(Twi_LocalBuffer.status==TWI_DATA_RECEIVE)) InitQueue(&Twi_ReceiveBuffer); //接收数据,回应ACK Twi_Ack();//notice自己增加,为适应缓冲区独 占 //拒绝接收数据,回应NACK Twi_NoAcK();//notice 自己增加,为适应缓冲区独占 caseTW_SR_DATA_ACK://0x80:以前以自己的SLA+W被寻址;数据已经被接收,ACK已返回 caseTW_SR_GCALL_DATA_ACK://0x90:以前以广播方式被寻址;数据已经被接收,ACK已返回 //检查接收缓冲区状态 if(!FullQueue(&Twi_ReceiveBuffer)) //接收数据,回应NACK caseTW_SR_DATA_NACK://0x88:以前以自己的SLA+W被寻址;数据已经被接收,NOTACK已返回 caseTW_SR_GCALL_DATA_NACK://0x98:以前以广播方式被寻址;数据已经被接收,NOTACK已返回 caseTW_SR_STOP://0xA0:在以从机工作时接收到STOP或重复START //Twi_接收完成 Twi_SlaveReceiveService(); //从发送 caseTW_ST_SLA_ACK://0xA8:自己的SLA+R已经被接收,ACK已返回 caseTW_ST_ARB_LOST_SLA_ACK://0xB0:SLA+R/W作为主机的仲裁失败;自己的SLA+R已经被接收,ACK已返回 //被选中为从读出(数据将从传回主机) Twi_State=TWI_SLAVE_TX; //数据请求 Twi_SlaveTransmitService(); caseTW_ST_DATA_ACK://0xB8:TWDR里数据已经发送,接收到ACK //发送数据位 if(Twi_SendBuffer.count>1) //回应ACK {TWDR=DeQueue(&Twi_SendBuffer); //回应NACK caseTW_ST_DATA_NACK://0xC0:TWDR里数据已经发送接收到NOTACK caseTW_ST_LAST_DATA://0xC8:TWDR的一字节数据已经发送(TWAE=“0”);接收到ACK //全部完成 //从方式开放 //无操作 caseTW_BUS_ERROR://0x00:由于非法的START或STOP引起的总线错误 //内部硬件复位,释放总线 TWCR=TWCR&TWCR_CMD_MASK|(1< TwiErrorCode=status; **函数名称:Twi_Init **功能描述:TWI总线初始化 **调用模块:Twi_SetBitrate() voidTwi_Init(void) //设置总线上拉此处注意:如果有外部上拉,可以不使用内部弱上拉 #if(CPU_TYPE==M64)||(CPU_TYPE==M128) DDRD&=~(1<<0); DDRD&=~(1<<1); PORTD|=(1<<0);//Twi_SCLonATmega128,64 PORTD|=(1<<1);//Twi_SDAonATmega128,64 #if(CPU_TYPE==M32)||(CPU_TYPE==M16) DDRC&=~(1<<0); DDRC&=~(1<<1); PORTC|=(1<<0);//Twi_SCLonATmega163,323,16,32,等 PORTC|=(1<<1);//Twi_SDAonATmega163,323,16,32,等 #if(CPU_TYPE==M8) DDRC&=~(1<<5); DDRC&=~(1<<4); PORTC|=(1<<5);//Twi_SCLonATmega163,323,16,32,等 PORTC|=(1<<4);//Twi_SDAonATmega163,323,16,32,等 //设置Twi_波特率为100KHz Twi_SetBitrate(80); //清空从发送和从接收 Twi_SlaveReceive=0; Twi_SlaveTransmit=0; InitQueue(&Twi_SendBuffer); InitQueue(&Twi_LocalBuffer); //状态设置 //设置从接收函数句柄(此函数在被选中为从接收时执行) //Twi_SetSlaveReceiveHandler(Twi_SlaveReceiveService); //设置从发送函数句柄(此函数在被选中为从发送时执行) //Twi_SetSlaveTransmitHandler(Twi_SlaveTransmitService); Twi_SetLocalDeviceAddr(0x50,0); //开Twi_中断和回应Twi_总线使能 TWCR=(1< SEI();//开中断 /************************************************************************* 音乐准备状态查询PSUVRC:播放停止暂停音量(+0~99)查询继续 传入:命令+音乐名称.mp3 返回:成功,跳出函数,否则重发,等待 voidmusic_state_ask(uint8*music_name) uint8tmp=0; do Twi_MasterSend(0x50,12,music_name); Twi_LocalBuffer.status=TWI_DATA_RECEIVE; while(Twi_LocalBuffer.status!=TWI_DATA_READY); tmp++; if(tmp==10)break; while(!DeQueue(&Twi_LocalBuffer)==TRUE); **文件名:queue.c队列结构函数 /********************************************************************************** **函数名称:InitQueue **功能描述:初始化队列 voidInitQueue(CirQueue*Q) //CLI(); Q->front=Q->rear=0; Q->count=0; Q->errorcode=QUEUE_OK; Q->memo=0; Q->status=TWI_DATA_FREE; //SEI(); /*********************************************************************************** **函数名称:EmptyQueue(CirQueue*Q) **功能描述:判断队列是否为空 ************************************************************************************* uint8EmptyQueue(CirQueue*Q) returnQ->count==0; **函数名称:FullQueue(CirQueue*Q) **功能描述:判断队列是否为满 *********************************************************************************** uint8FullQueue(CirQueue*Q) return(Q->count==Q->queuesize); **函数名称:EnQueue **功能描述:入队 voidEnQueue(CirQueue*Q,uint8x) if(FullQueue(Q)) Q->errorcode=QUEUE_OVERFLOW; Q->count=Q->queuesize; return; Q->count++; Q->data[Q->rear]=x; Q->rear=(Q->rear+1)%Q->queuesize; **函数名称:DeQueue **功能描述:出队 uint8DeQueue(CirQueue*Q) uint8temp; if(EmptyQueue(Q)) Q->errorcode=QUEUE_UNDERFLOW; returnFALSE; Q->count--; temp=Q->data[Q->front]; Q->front=(Q->front+1)%Q->queuesize; returntemp; /******************************************************************************* **函数名称:FrontQueueData *******************************************************************************/ uint8FrontQueueData(CirQueue*Q) returnQ->data[Q->front]; **文件名:sio.c串口驱动程序 #include #ifUSEUART0 siocirqueueRTbuf_UART0; #ifUSEUART1 siocirqueueRTbuf_UART1; #if(CPU_TYPE==M32)||(CPU_TYPE==M16)||(CPU_TYPE==M8) #defineUSEUART0TRUE #defineUSEUART1FALSE #defineiv_USART0_RXCiv_USART_RXC #defineiv_USART0_TXCiv_USART_TXC #defineUDR0UDR #defineUCSR0AUCSRA #defineUCSR0BUCSRB #defineUCSR0CUCSRC #defineUBRR0HUBRRH #defineUBRR0LUBRRL #defineRXCIE0RXCIE #defineTXCIE0TXCIE /************************************************************** **函数名称:Com_init **功能描述:初始化串行口 **调用模块:Com_baudrate ******************************************************************/ voidCom_init(void){ WDR();//喂狗 //CLI();//关中断 Tbuf_init(&RTbuf_UART0);//初始化接收缓冲 Rbuf_init(&RTbuf_UART0); UCSR0B=0x00;//disablewhilesettingbaudrate UCSR0A=0x00; #ifCPU_TYPE<=M32 UCSRC=(1< UCSR0B=(1< #else UCSR0C=0x06;//8DATA,1STOP,NOPARITY UCSR0B=(1< Tbuf_init(&RTbuf_B);//初始化接收缓冲 Rbuf_init(&RTbuf_B); UCSR1B=0x00;//disablewhilesettingbaudrate UCSR1A=0x00; UCSR1C=0x06; UCSR1B=0xD8; Com_baudrate(9600);// //SEI();//开中断 /********************************************************************* **函数名称:Com_baudrate **功能描述:利用定时器T1产生由参数baudrate指定的波特率 **输入:baudrate指定的波特率 voidCom_baudrate(uint16baudrate){ uint16tmp; tmp=F_CPU/baudrate/16-1; UBRR0H=(uint8)(tmp>>8); UBRR0L=(uint8)tmp; UBRR1H=(uint8)(tmp>>8); UBRR1L=(uint8)tmp; **函数名称:Com_putchar **功能描述:从串行口输出一个字符c **输入:c:输出字符 **输出:0:失败1:成功 uint8Com_putchar(uint8c,siocirqueue*RTbuf){ if(Tbuf_full(RTbuf)) return(0); Tbuf_putchar(c,RTbuf);//将数据加入到发送缓冲区并开中断 return(1); **函数名称:Com_putstring **功能描述:从串行口输出一个字符串 **输入:p:指向输出字符串len:输出长度 voidCom_putstring(uint8*p,uint8len,siocirqueue*RTbuf){ if(len!=0){ for(i=0;i while(Com_putchar(p[i],RTbuf)==0);//WDR();//喂狗; do{ while(Com_putchar(*p,RTbuf)==0);//WDR();//喂狗; }while(*p++!='\n'); **函数名称:Com_getchar **功能描述:从串行口输入一个字符 **输入:mode:0不需等待,在调用函数前检测Com_R_count的值 **1等待数据到来 **输出:读入的字符 uint8Com_getchar(uint8mode,siocirqueue*RTbuf){ //Delay(Delay_Comget,MaxLimit_Comget_Delay); //if(mode>0)while(Com_R_count(RTbuf)==0&&!DelayOvf(Delay_Comget))//WDR();//喂狗; //if(DelayOvf(Delay_Comget)) //else while(!Com_R_count(RTbuf)); return(Rbuf_getchar(RTbuf));//串行口输入正确,返回输入的字符 voidCom_Rbuf_Clear(siocirqueue*RTbuf){ Rbuf_init(RTbuf); uint8Com_getstring(uint8*p,uint8len,siocirqueue*RTbuf){ if(len>0){ for(i=0;i *p++=Com_getchar(1,RTbuf); return(len); }while(*(p-1)!='\n'); //*p++=Com_getchar(1,RTbuf); //*(p)=0; return(i); **函数名称:Com_R_count **功能描述:返回接收缓冲区字符个数函数 **输出:字符个数 **************************************************************************/ uint8Com_R_count(siocirqueue*RTbuf){ returnRTbuf->R_count; **函数名称:Rbuf_init **功能描述:接收缓冲区初始化 voidRbuf_init(siocirqueue*RTbuf){ RTbuf->R_front=0; RTbuf->R_rear=0; RTbuf->R_count=0; RTbuf->R_overflow=0; **函数名称:Tbuf_init **功能描述:发送缓冲区初始化 voidTbuf_init(siocirqueue*RTbuf){ RTbuf->T_front=0; RTbuf->T_rear=0; RTbuf->T_count=0; RTbuf->T_disabled=1; **函数名称:Rbuf_empty **功能描述:接收缓冲区判空 **输出:TRUE空FALSE非空 uint8Rbuf_empty(siocirqueue*RTbuf){ returnRTbuf->R_count==0; **函数名称:Tbuf_empty **功能描述:发送缓冲区判空 uint8Tbuf_empty(void){ returnRTbuf->T_count==0; **函数名称:Rbuf_full **功能描述:接收缓冲区判满 **输出:TRUE满FALSE非满 uint8Rbuf_full(void){ returnRTbuf->R_count==RBUF_SIZE; **函数名称:Tbuf_full **功能描述:发送缓冲区判满 uint8Tbuf_full(siocirqueue*RTbuf){ returnRTbuf->T_count==TBUF_SIZE; **函数名称:Rbuf_putchar **功能描述:把一个字符放入接收缓冲区 ************************************************************************* voidRbuf_putchar(uint8x){ if(!Rbuf_full()){ REV_DIS(); RTbuf->R_count++; RTbuf->R_buf[RTbuf->R_rear]=x; RTbuf->R_rear=(RTbuf->R_rear+1)%RBUF_SIZE; REV_EN(); **函数名称:Tbuf_putchar **功能描述:把一个字符放入发送缓冲区 ***********************************************************************/ voidTbuf_putchar(uint8x,siocirqueue*RTbuf){ if(!Tbuf_full(RTbuf)){ TXC_DIS(); if(RTbuf->T_disabled) UDR0=x; RTbuf->T_disabled=0; RTbuf->T_count++; RTbuf->T_buf[RTbuf->T_rear]=x; RTbuf->T_rear=(RTbuf->T_rear+1)%TBUF_SIZE; TXC_EN(); **函数名称:Rbuf_getstring **功能描述:从接收缓冲区返回当前子串指针 **输出:当前子串指针 ********************************************************************** uint8*Rbuf_getstring(void){ return(RTbuf->R_buf[RTbuf->R_front]); **函数名称:Tbuf_getstring **功能描述:从发送缓冲区返回当前子串指针 *********************************************************************** uint8*Tbuf_getstring(void){ return(RTbuf->T_buf[RTbuf->T_front]); **功能描述:从接收缓冲区读一个字符 **输出:读字符 uint8Rbuf_getchar(siocirqueue*RTbuf){ uint8Btemp=0; if(!Rbuf_empty(RTbuf)){ Btemp=RTbuf->R_buf[RTbuf->R_front]; RTbuf->R_count--; RTbuf->R_front=(RTbuf->R_front+1)%RBUF_SIZE; return(Btemp); **函数名称:Tbuf_getchar **功能描述:从发送缓冲区读一个字符 ************************************************************************** uint8Tbuf_getchar(void){ uint8Btemp; if(!Tbuf_empty()){ Btemp=RTbuf->T_buf[RTbuf->T_front]; RTbuf->T_count--; RTbuf->T_front=(RTbuf->T_front+1)%TBUF_SIZE; /*******************************中断处理函数*************************** *功能:完成数据得的接收合发送 #pragmainterrupt_handleruart0_rx_isr:iv_USART0_RXC voiduart0_rx_isr(void) uint8status,data; siocirqueue*RTbuf; RTbuf=&RTbuf_UART0; status=UCSR0A; data=UDR0; if((status&(FRAMING_ERROR|DATA_OVERRUN))==0) if(RTbuf->R_count RTbuf->R_buf[RTbuf->R_rear]=data; RTbuf->R_overflow=1; #pragmainterrupt_handleruart0_tx_isr:iv_USART0_TXC voiduart0_tx_isr(void) if(RTbuf->T_count>0){ UDR0=RTbuf->T_buf[RTbuf->T_front]; elseRTbuf->T_disabled=1; #pragmainterrupt_handleruart1_rx_isr:iv_USART1_RXC voiduart1_rx_isr(void) RTbuf=&RTbuf_B; status=UCSR1A; data=UDR1; #pragmainterrupt_handleruart1_tx_isr:iv_USART1_TXC voiduart1_tx_isr(void) UDR1=RTbuf->T_buf[RTbuf->T_front]; /**************************************************************** **文件名:motor.c电机驱动函数 ****************************************************************/ //ICC-AVRapplicationbuilder:2006-9-89:37:32 //Target:M32 //Crystal:7.3728Mhz //pd45电机方向控制 //Pd7PWM信号 //使用T/C0计数 //使用T/C2产生pwm externvoidDelay1s(uint8n); volatileuint16counter=0; externuint16INT2_count; externuint8up_or_down; externuint8lcd_show[32]; externuint8position_flag; volatileuint8timer0_i=0; volatileuint16get_s;//计数用 externuint8lcd_fresh_flag;//允许刷新LCD externuint8speed_change_flag; externuint8move_flag;//是否在移动 uint16S=0; uint16T=0; uint8juli;//要运行的距离 volatileuint16s;//已经走过的路程cm //uint8speed; //电机主控函数 //externintMOTOR(uint8fangxiang,uint8juli,uint16time); //电机正转 externvoidfor_ward(uint8speed); //电机反转 externvoidback_ward(uint8speed); //电机急停 externvoidmotor_stop(void); //停止电机,外部中断,定时器1 externvoidall_stop(void); //PD4,PD5电机方向控制 #definemoto_en1PORTD|=0x10; #definemoto_en2PORTD|=0x20; #definemoto_uen1PORTD&=~0x10; #definemoto_uen2PORTD&=~0x20; intMOTOR(uint8fangxiang,uint8juli,uint16time) INT2_count=0; if(juli<0)juli=juli+(254-180);//解决顶端状态为254的兼容问题 S=juli;//每厘米20个脉冲 T=time;//一秒有10个100毫秒 s=0; haveused_time=0; INT2_EN; move_flag=1;//标记电机已运动 if(fangxiang==1) for_ward(40); up_or_down=1;//在上升 elseif(fangxiang==0) back_ward(40); up_or_down=0;//下降 T1_start; T1EN; T0_start; T0EN; return1; #pragmainterrupt_handlertimer0_comp_isr:11 voidtimer0_comp_isr(void) //TCNT0=0x4C;//setcount timer0_i++; if(timer0_i==20)//1s timer0_i=0; lcd_fresh_flag=1;//允许刷新LCD #pragmainterrupt_handlertimer1_ovf_isr:10 voidtimer1_ovf_isr(void) {//100ms //TIMER1hasoverflowed TCNT1H=0x70;//setup TCNT1L=0x01; INT2_UN;//关中断取数 get_s=INT2_count; INT2_EN;//迅速返回开中断 speed_change_flag=1;//开调速允许 //前,速度 voidfor_ward(uint8speed) if(speed!=0) OCR2=speed; TIMSK|=(1< moto_en1; moto_uen2; //后,速度 voidback_ward(uint8speed) TIMSK|=(1< moto_uen1; moto_en2; //停止 voidmotor_stop(void) //OCR2=0x55; moto_en1;//同时置一停止 TIMSK&=~(1< voidall_stop(void) motor_stop(); T1UEN;//T1定时停止 T1_stop; INT2_UN;//关闭中断 T0UEN; T0_stop;//关中断 music_state_ask("U"); move_flag=0;//标记电机停止运动 **文件名:init_device.c系统参数初始化 //ICC-AVRapplicationbuilder:2006-9-100:27:10 volatileuint16INT2_count=0; //externuint16haveused_time; externuint8if_half_flag; externuint8half_up_or_down;//半旗升旗到顶,还是半旗降旗到顶。 externvoidLcd_stringdisp(uint8*p,uint16displen);//lcd显示 externintEepromWrite(intlocation,unsignedcharbyte); externintMOTOR(uint8fangxiang,uint8juli,uint16time); externuint16s; voidport_init(void) PORTA=0x00; DDRA=0xFF;//LCD_DATA PORTB=0xF4; DDRB=0x0B;//LCD_CONTROLKEY_BOARD PORTC|=0xC0; DDRC&=~0xc0; PORTD=0x30; DDRD=0xC0;//D7PWMD6半旗状态 //TIMER0initialize-prescale:1024 //desiredvalue:25mSec //actualvalue:25.000mSec(0.0%) voidtimer0_init(void) TCCR0=0x00;//stop TCNT0=0x00;//setcount OCR0=0x3A;//setcompare TCCR0=0x0D;//starttimer //TIMER1initialize-prescale:256 //desiredvalue:100mSec //actualvalue:99.965mSec(0.0%) /*voidtimer1_init(void) TCCR1B=0x00;//stop TCNT1H=0xF4;//setup TCNT1L=0xC1; TCCR1A=0x00; //TCCR1B=0x04;//startTimer }*/ voidtimer1_init(void)//20MS TCNT1L=0xC0; //TCCR1B=0x01;//startTimer /***********************因为计数器冲突,采用timer2产生波形***********************/ //PWM频率=系统时钟频率/(分频系数*2*计数器上限值)) voidtimer2_init(void) TCCR2=0x00;//stop TCNT2=0x01;//TCNT2=0x01;//setcountc7 OCR2=0x66;//OCR2=0x66;//setcompare TCCR2=(1< 频 //高比低为:(OCR2-0X55)/(0XFF-OCR2)OX55++++++(0X77)__________OXFF //即OCR2越大,输出越大 /* //UART0initialize //desiredbaudrate:9600 //actual:baudrate:9600(0.0%) //charsize:8bit //parity:Disabled voiduart0_init(void) UCSRB=0x00;//disablewhilesettingbaudrate UCSRA=0x00; UCSRC=BIT(URSEL)|0x06; UBRRL=0x2F;//setbaudratelo UBRRH=0x00;//setbaudratehi UCSRB=0xD8; //SPIinitialize //clockrate:230398hz voidspi_init(void) SPCR=0xFE;//setupSPI SPSR=0x01;//setupSPI #pragmainterrupt_handlerspi_stc_isr:13 voidspi_stc_isr(void) //byteinSPDRhasbeensent/received #pragmainterrupt_handlerint0_isr:2 voidint0_isr(void) {//下降延触发,掉电检测 //externalinteruptonINT0 EepromWrite(0x0b,if_half_flag);//是否半旗 EepromWrite(0x0a,position_flag);//写入eeprom #pragmainterrupt_handlerint1_isr:3 voidint1_isr(void) {//行程控制//externalinteruptonINT1 GICR&=~(1< if((PINC&0xC0)==0x80) {//上 position_flag=254; all_stop();//停止电机,外部中断,定时器1 if(if_half_flag==0)Lcd_stringdisp("升旗到顶",8); elseif((if_half_flag==1)&&(half_up_or_down==1)) Lcd_stringdisp("半旗模式升旗到顶",16); Delay1s(1); MOTOR(0,60,15);//下降1/3 elseif((if_half_flag==1)&&(half_up_or_down==0)) Lcd_stringdisp("半旗模式降旗先升到顶",20); MOTOR(0,180,43);//下降 elseif((PINC&0xC0)==0x40) {//下 position_flag=0; all_stop(); Lcd_stringdisp("降旗结束",8); GICR|=(1< #pragmainterrupt_handlerint2_isr:4 voidint2_isr(void) {//脉冲计数中断 //externalinteruptonINT2 INT2_count++; //callthisroutinetoinitializeallperipherals voidinit_devices(void) //stoperrantinterruptsuntilsetup CLI();//disableallinterrupts port_init(); timer2_init(); timer1_init(); timer0_init(); //uart0_init();//串口 //spi_init(); //twi_init(); Twi_Init(); Com_init();//初始化串行口 MCUCR|=(1< //MCUCSR|=(1< MCUCSR&=~(1< GICR|=(1< TIMSK=0x00;//timerinterruptsources SEI();//re-enableinterrupts //allperipheralsarenowinitialized //itsinmotor.c //compareoccuredTCNT0=OCR0 //不需要用,但是必须允许中断,在中断时有信号输出变化 #pragmainterrupt_handlertimer2_comp_isr:5 voidtimer2_comp_isr(void) //定时器二用于pwm输出 #pragmainterrupt_handlertimer2_ovf_isr:6 voidtimer2_ovf_isr(void) TCNT0=0x01;//reloadcountervalue ///TCNT2=0x24; **文件名:config.h工程配置文件 #ifndef__config_H__ #define__config_H__ /*********************************************/ #defineM81 #defineM162 #defineM323 #defineM644 #defineM1285 #defineCPU_TYPEM32 //定义MCU时钟频率 //#defineF_CPU14745600 #defineF_CPU7372800 //************************************************** //包含系统头文件,请根据实际需要进行裁减 //#pragmaREGPARMS #ifCPU_TYPE==M128 #include #ifCPU_TYPE==M64 #include #ifCPU_TYPE==M32 #include #ifCPU_TYPE==M16 #include #ifCPU_TYPE==M8 #include //#include //#include #include //#include #include //#include //#defineconstcode //系统数据类型定义 #ifndefTRUE #defineTRUE1 #ifndefFALSE #defineFALSE0 #ifndefNULL #defineNULL0 #defineMIN(a,b)((a #defineMAX(a,b)((a>b)(a):(b)) #defineABS(x)((x>0)(x):(-x)) typedefunsignedcharuint8;/*定义可移植的无符号8位整数关键字 typedefsignedcharint8;/*定义可移植的有符号8位整数关键字 typedefunsignedintuint16;/*定义可移植的无符号16位整数关键字 typedefsignedintint16;/*定义可移植的有符号16位整数关键字 typedefunsignedlonguint32;/*定义可移植的无符号32位整数关键字 typedefsignedlongint32;/*定义可移植的有符号32位整数关键字 //包含工程头文件,请根据需要进行裁减 #include"main.h" #include"lcddrive.h" #include"queue.h" #include"TWI.h" #include"sio.h" /********************************/ /*"以下为工程配置"*/ //计数器0比较计数中断允许 #defineT0ENTIMSK|=(1< //0x03 #defineT0UENTIMSK&=~(1< #defineT0_startTCCR0=0x0d #defineT0_stopTCCR0=0x0d //计数器1定时中断允许//OVERflow #defineT1ENTIMSK|=(1< #defineT1UENTIMSK&=~(1< #defineT1_startTCCR1B=0x01 #defineT1_stopTCCR1B=0x00 //定时器2允许,PWM输出 #defineINT2_ENGICR|=(1< #defineINT2_UNGICR&=~(1< (九)制作过程中硬件方案改动记录 //vesion1.2 //修改5.portalcd显示,旧的思路占用太多io口 //修改12.因为pd2与int0在一个口上,添加pa7 //修改6.因为C/T2没有外部输入计数引脚,改用C/T0,而C/T2用于pwm输出 //修改7.电机方向控制PBO与T0冲突,改为PD4,PD5 //删除9.淘汰本机控制STA013的方案,因为太耗资源。 //删除10.淘汰9之后,10—SD卡无需再用。 //修改1.3.C/T0和C/T2功能互换 //添加15.TWIPC0,PC1 1:c/t2pwm电机控制 2.C/T11s精确定时 3.C/T0外部计数,判断速度INT2输入 PB2(INT2)下降沿触发,外部计数 4.EEROM保存当前旗帜位置和状态 // 6.PD_7OC2pwm输出 7.PD4,5电机方向控制 8.PD6LED显示半旗状态/低电平驱动 /////////////////////////////////////////9.PB_5~7;PC0,PC1;PD_4~5STAO13 /////////////////////////////////////////10.PD_4~6SD卡 11.PD0,PD1(RXD,PXD)无线模块 12.PD2(INT0),PD3PA7限位开关(内部上拉电阻) 13.PB_4~7键盘 14.PORTB(1,2,4)控制并口LCD显示PORTA:LCD_DATA 15.PC0,PC1TWI /****************************theend**************************************/