开场白:
在大部分的项目中,串口都需求“一收一应对”的握手协议,主机先发一串数据,从机收到数据后进行校验判别,假如校验正确则回来正确应对指令,假如校验过错则回来过错应对指令,主机收到应对指令后,假如发现是正确应对指令则持续发送其它的新数据,假如发现是过错应对指令,或许超时没有接纳到任何应对指令,则持续重发,假如接连重发三次都是过错应对或许无应对,主机就进行报错处理。
上一节现已讲了从机,这节就讲主机的收发端程序实例。要教会咱们四个知识点:
上一节现已讲了从机,这节就讲主机的收发端程序实例。要教会咱们四个知识点:
榜首个:为了确保串口中止接纳的数据不丢掉,在初始化时有必要设置IP= 0x10,相当于把串口中止设置为最高优先级,这个时分,串口中止能够打断任何其他的中止服务函数,完结中止嵌套。
第二个:主机端的收发端程序结构。包含重发,超时检测等等。
第三个:主机的状况指示程序结构。能够指示待机,通讯中,超时犯错三种状况。
第四个:其实上一节的LED灯闪耀的时刻里,我忘了加原子锁,不加原子锁的结果是,闪耀的时刻有时分会不共同,所以这节多添加一个原子锁变量ucLedLock,再次感谢“红金龙吸味”关于原子锁的主张,真的很好用。
第二个:主机端的收发端程序结构。包含重发,超时检测等等。
第三个:主机的状况指示程序结构。能够指示待机,通讯中,超时犯错三种状况。
第四个:其实上一节的LED灯闪耀的时刻里,我忘了加原子锁,不加原子锁的结果是,闪耀的时刻有时分会不共同,所以这节多添加一个原子锁变量ucLedLock,再次感谢“红金龙吸味”关于原子锁的主张,真的很好用。
具体内容,请看源代码解说。
(1)硬件渠道:
依据朱兆祺51单片机学习板。
(2)完结功用:
显现和独立按键部分依据第29节的程序来改编,用朱兆祺51单片机学习板中的S1,S5,S9,S13作为独立按键。
一共有4个窗口。每个窗口显现一个参数。串口能够把当时设置的4个数据发送给从机。从机端能够用电脑的串口帮手来模仿。
榜首:按键更改参数:
第8,7,6,5位数码管显现当时窗口,P-1代表第1个窗口,P-2代表第2个窗口,P-3代表第3个窗口,P-4代表第1个窗口。
第4,3,2,1位数码管显现当时窗口被设置的参数。规模是从0到9999。S1是加按键,按下此按键会顺次添加当时窗口的参数。S5是减按键,按下此按键会顺次削减当时窗口的参数。S9是切换窗口按键,按下此按键会顺次循环切换不同的窗口。S13是发动发送数据和复位按键,当体系处于待机状况时,按下此按键会发动发送数据;当通讯超时蜂鸣器报警时,能够按下此键铲除报警,回来到待机的状况。
第二:通过串口把更改的参数发送给从机。
波特率是:9600.
通讯协议:EB00 55GG 00 02 XX XXCY
其间第1,2,3位EB00 55便是数据头
其间第4位GG便是数据类型。01代表更改参数1,02代表更改参数2,03代表更改参数3,04代表更改参数4,
其间第5,6位0002便是有用数据长度。高位在左,低位在右。
其间从第7,8位XXXX是被更改的参数。高位在左,低位在右。
第9位CY是累加和,前面一切字节的累加。
一个完好的通讯有必要发送完4串数据,每串数据之间的间隔时刻不能超越10秒钟,不然以为通讯超时主时机重发数据,假如接连三次都没有回来,则引发蜂鸣器报警。假如接纳到得数据校验正确,主机持续发送新的一串数据,直到把4串数据发送结束中止。
通讯协议:EB00 55GG 00 02 XX XXCY
其间第1,2,3位EB00 55便是数据头
其间第4位GG便是数据类型。01代表更改参数1,02代表更改参数2,03代表更改参数3,04代表更改参数4,
其间第5,6位0002便是有用数据长度。高位在左,低位在右。
其间从第7,8位XXXX是被更改的参数。高位在左,低位在右。
第9位CY是累加和,前面一切字节的累加。
一个完好的通讯有必要发送完4串数据,每串数据之间的间隔时刻不能超越10秒钟,不然以为通讯超时主时机重发数据,假如接连三次都没有回来,则引发蜂鸣器报警。假如接纳到得数据校验正确,主机持续发送新的一串数据,直到把4串数据发送结束中止。
体系处于待机状况时,LED灯一向亮,
体系处于非待机状况时,LED灯闪耀,
体系处于犯错状况时,LED灯闪耀,而且蜂鸣器间歇鸣叫报警。
体系处于非待机状况时,LED灯闪耀,
体系处于犯错状况时,LED灯闪耀,而且蜂鸣器间歇鸣叫报警。
通过电脑的串口帮手来模仿从机,回来不同的应对
从机回来校验正确应对:eb 00 55 f5 00 00 35
从机回来校验犯错应对:eb00 55 fa 00 00 3a
从机回来校验犯错应对:eb00 55 fa 00 00 3a
(3)源代码解说如下:
- #include “REG52.H”
- #define const_voice_short40 //蜂鸣器短叫的持续时刻
- #define const_key_time120 //按键去颤动延时的时刻
- #define const_key_time220 //按键去颤动延时的时刻
- #define const_key_time320 //按键去颤动延时的时刻
- #define const_key_time420 //按键去颤动延时的时刻
- #define const_led_0_5s200 //大约0.5秒的时刻
- #define const_led_1s 400 //大约1秒的时刻
- #define const_send_time_out 4000//通讯超时犯错的时刻 大约10秒
- #define const_rc_size20//接纳串口中止数据的缓冲区数组巨细
- #define const_receive_time5//假如超越这个时刻没有串口数据过来,就以为一串数据现已悉数接纳完,这个时刻依据实践情况来调整巨细
- #define const_send_size10//串口发送数据的缓冲区数组巨细
- void initial_myself(void);
- void initial_peripheral(void);
- void delay_short(unsigned int uiDelayShort);
- void delay_long(unsigned int uiDelaylong);
- //驱动数码管的74HC595
- void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
- void display_drive(void); //显现数码管字模的驱动函数
- void display_service(void); //显现的窗口菜单服务程序
- //驱动LED的74HC595
- void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
- void T0_time(void);//守时中止函数
- void usart_receive(void); //串口接纳中止函数
- void usart_service(void);//串口接纳服务程序,在main函数里
- void communication_service(void); //一发一收的通讯服务程序
- void eusart_send(unsigned char ucSendData); //发送一个字节,内部自带每个字节之间的delay延时
- void key_service(void); //按键服务的应用程序
- void key_scan(void);//按键扫描函数 放在守时中止里
- void status_service(void);//状况显现的应用程序
- sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
- sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
- sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
- sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
- sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
- sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
- sbit led_dr=P3^5;//作为状况指示灯 亮的时分表明待机状况.闪耀表明非待机状况,处于正在发送数据或许犯错的状况
- sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
- sbit dig_hc595_st_dr=P2^1;
- sbit dig_hc595_ds_dr=P2^2;
- sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
- sbit hc595_st_dr=P2^4;
- sbit hc595_ds_dr=P2^5;
- unsigned char ucSendregBuf[const_send_size]; //发送的缓冲区数组
- unsigned intuiSendCnt=0; //用来辨认串口是否接纳完一串数据的计时器
- unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接纳完一串数据只处理一次
- unsigned intuiRcregTotal=0;//代表当时缓冲区现已接纳了多少个数据
- unsigned char ucRcregBuf[const_rc_size]; //接纳串口中止数据的缓冲区数组
- unsigned intuiRcMoveIndex=0;//用来解析数据协议的中心变量
- unsigned charucSendCntLock=0; //串口计时器的原子锁
- unsigned char ucRcType=0;//数据类型
- unsigned intuiRcSize=0;//数据长度
- unsigned char ucRcCy=0;//校验累加和
- unsigned char ucLedLock=0; //原子锁
- unsigned intuiLedCnt=0;//操控Led闪耀的延时计时器
- unsigned intuiSendTimeOutCnt=0; //用来辨认接纳数据超时的计时器
- unsigned char ucSendTimeOutLock=0; //原子锁
- unsigned char ucStatus=0; //当时状况变量 0代表待机 1代表正在通讯进程 2代表发送犯错
- unsigned char ucSendStep=0; //发送数据的进程进程
- unsigned char ucErrorCnt=0; //累计过错总数
- unsigned char ucSendTotal=0; //记载当时现已发送了多少串数据
- unsigned char ucReceiveStatus=0; //回来的数据状况 0代表待机 1代表校验正确 2代表校验犯错
- unsigned char ucKeySec=0; //被触发的按键编号
- unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
- unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
- unsigned intuiKeyTimeCnt2=0; //按键去颤动延时计数器
- unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
- unsigned intuiKeyTimeCnt3=0; //按键去颤动延时计数器
- unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
- unsigned intuiKeyTimeCnt4=0; //按键去颤动延时计数器
- unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
- unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
- unsigned charucVoiceLock=0;//蜂鸣器鸣叫的原子锁
- unsigned char ucDigShow8;//第8位数码管要显现的内容
- unsigned char ucDigShow7;//第7位数码管要显现的内容
- unsigned char ucDigShow6;//第6位数码管要显现的内容
- unsigned char ucDigShow5;//第5位数码管要显现的内容
- unsigned char ucDigShow4;//第4位数码管要显现的内容
- unsigned char ucDigShow3;//第3位数码管要显现的内容
- unsigned char ucDigShow2;//第2位数码管要显现的内容
- unsigned char ucDigShow1;//第1位数码管要显现的内容
- unsigned char ucDigDot8;//数码管8的小数点是否显现的标志
- unsigned char ucDigDot7;//数码管7的小数点是否显现的标志
- unsigned char ucDigDot6;//数码管6的小数点是否显现的标志
- unsigned char ucDigDot5;//数码管5的小数点是否显现的标志
- unsigned char ucDigDot4;//数码管4的小数点是否显现的标志
- unsigned char ucDigDot3;//数码管3的小数点是否显现的标志
- unsigned char ucDigDot2;//数码管2的小数点是否显现的标志
- unsigned char ucDigDot1;//数码管1的小数点是否显现的标志
- unsigned char ucDigShowTemp=0; //暂时中心变量
- unsigned char ucDisplayDriveStep=1;//动态扫描数码管的进程变量
- unsigned char ucWd1Update=1; //窗口1更新显现标志
- unsigned char ucWd2Update=0; //窗口2更新显现标志
- unsigned char ucWd3Update=0; //窗口3更新显现标志
- unsigned char ucWd4Update=0; //窗口4更新显现标志
- unsigned char ucWd=1;//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
- unsigned intuiSetData1=0;//本程序中需求被设置的参数1
- unsigned intuiSetData2=0;//本程序中需求被设置的参数2
- unsigned intuiSetData3=0;//本程序中需求被设置的参数3
- unsigned intuiSetData4=0;//本程序中需求被设置的参数4
- unsigned char ucTemp1=0;//中心过渡变量
- unsigned char ucTemp2=0;//中心过渡变量
- unsigned char ucTemp3=0;//中心过渡变量
- unsigned char ucTemp4=0;//中心过渡变量
- //依据原理图得出的共阴数码管字模表
- code unsigned char dig_table[]=
- {
- 0x3f,//0 序号0
- 0x06,//1 序号1
- 0x5b,//2 序号2
- 0x4f,//3 序号3
- 0x66,//4 序号4
- 0x6d,//5 序号5
- 0x7d,//6 序号6
- 0x07,//7 序号7
- 0x7f,//8 序号8
- 0x6f,//9 序号9
- 0x00,//无 序号10
- 0x40,//- 序号11
- 0x73,//P 序号12
- };
- void main()
- {
- initial_myself();
- delay_long(100);
- initial_peripheral();
- while(1)
- {
- key_service(); //按键服务的应用程序
- usart_service();//串口接纳服务程序
- communication_service(); //一发一收的通讯服务程序
- display_service(); //显现的窗口菜单服务程序
- status_service();//状况显现的应用程序
- }
- }
- void communication_service(void) //一发一收的通讯服务程序
- {
- unsigned int i;
- if(ucStatus==1)//处于正在通讯的进程中
- {
- switch(ucSendStep)
- {
- case 0: //通讯进程0发送一串数据
- switch(ucSendTotal)//依据当时现已发送到第几条数据来决议发送哪些参数
- {
- case 0: //发送参数1
- ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
- ucSendregBuf[1]=0x00;
- ucSendregBuf[2]=0x55;
- ucSendregBuf[3]=0x01; //代表发送参数1
- ucSendregBuf[4]=0x00;
- ucSendregBuf[5]=0x02; //代表发送2个字节的有用数据
- ucSendregBuf[6]=uiSetData1>>8;//把int类型的参数分化成两个字节的数据
- ucSendregBuf[7]=uiSetData1;
- break;
- case 1://发送参数2
- ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
- ucSendregBuf[1]=0x00;
- ucSendregBuf[2]=0x55;
- ucSendregBuf[3]=0x02; //代表发送参数2
- ucSendregBuf[4]=0x00;
- ucSendregBuf[5]=0x02; //代表发送2个字节的有用数据
- ucSendregBuf[6]=uiSetData2>>8;//把int类型的参数分化成两个字节的数据
- ucSendregBuf[7]=uiSetData2;
- break;
- case 2://发送参数3
- ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
- ucSendregBuf[1]=0x00;
- ucSendregBuf[2]=0x55;
- ucSendregBuf[3]=0x03; //代表发送参数3
- ucSendregBuf[4]=0x00;
- ucSendregBuf[5]=0x02; //代表发送2个字节的有用数据
- ucSendregBuf[6]=uiSetData3>>8;//把int类型的参数分化成两个字节的数据
- ucSendregBuf[7]=uiSetData3;
- break;
- case 3://发送参数4
- ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
- ucSendregBuf[1]=0x00;
- ucSendregBuf[2]=0x55;
- ucSendregBuf[3]=0x04; //代表发送参数4
- ucSendregBuf[4]=0x00;
- ucSendregBuf[5]=0x02; //代表发送2个字节的有用数据
- ucSendregBuf[6]=uiSetData4>>8;//把int类型的参数分化成两个字节的数据
- ucSendregBuf[7]=uiSetData4;
- break;
- }
- ucSendregBuf[8]=0x00;
- for(i=0;i<8;i++)//最终一个字节是校验和,是前面一切字节累加,溢出部分不必咱们管,体系会有规则的主动处理
- {
- ucSendregBuf[8]=ucSendregBuf[8]+ucSendregBuf[i];
- }
- for(i=0;i<9;i++)
- {
- eusart_send(ucSendregBuf[i]);//把一串完好的数据发送给下位机
- }
- ucSendTimeOutLock=1; //原子锁加锁
- uiSendTimeOutCnt=0;//超时计时器计时清零
- ucSendTimeOutLock=0; //原子锁解锁
- ucReceiveStatus=0;//回来的数据状况清零
- ucSendStep=1;//切换到下一个进程,等候回来的数据
- break;
- case 1: //通讯进程1判别回来的指令
- if(ucReceiveStatus==1)//校验正确
- {
- ucErrorCnt=0; //累计校验过错总数清零
- ucSendTotal++;//累加当时发送了多少串数据
- if(ucSendTotal>=4) //现已发送完悉数4串数据,结束
- {
- ucStatus=0;//切换到结束时的待机状况
- }
- else//还没发送完4串数据,则持续发送下一串新数据
- {
- ucSendStep=0;//回来上一个进程,持续发送新数据
- }
- }
- else if(ucReceiveStatus==2||uiSendTimeOutCnt>const_send_time_out)//校验犯错或许超时犯错
- {
- ucErrorCnt++; //累计过错总数
- if(ucErrorCnt>=3)//累加重发次数3次以上,则报错
- {
- ucStatus=2;//切换到犯错报警状况
- }
- else//重发还没超越3次,持续回来重发
- {
- ucSendStep=0;//回来上一个进程,重发一次数据
- }
- }
- break;
- }
- }
- }
- void status_service(void)//状况显现的应用程序
- {
- if(ucStatus!=0) //处于非待机的状况,Led闪耀
- {
- if(uiLedCnt
- {
- led_dr=1;//前半秒亮
- if(ucStatus==2)//处于发送数据犯错的状况,则蜂鸣器间歇鸣叫报警
- {
- ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
- }
- }
- else if(uiLedCnt
- {
- led_dr=0; //前半秒灭
- }
- else
- {
- ucLedLock=1; //原子锁加锁
- uiLedCnt=0; //延时计时器清零,让Led灯处于闪耀的重复循环中
- ucLedLock=0; //原子锁解锁
- }
- }
- else//处于待机状况,Led一向亮
- {
- led_dr=1;
- }
- }
- void usart_service(void)//串口接纳服务程序,在main函数里
- {
- unsigned int i;
- if(uiSendCnt>=const_receive_time&&ucSendLock==1) //阐明超越了必定的时刻内,再也没有新数据从串口来
- {
- ucSendLock=0; //处理一次就锁起来,不必每次都进来,除非有新接纳的数据
- //下面的代码进入数据协议解析和数据处理的阶段
- uiRcMoveIndex=0; //由所以判别数据头,所以下标移动变量从数组的0开端向最尾端移动
- while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))
- {
- if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55)//数据头eb 00 55的判别
- {
- ucRcType=ucRcregBuf[uiRcMoveIndex+3]; //数据类型一个字节
- uiRcSize=ucRcregBuf[uiRcMoveIndex+4]; //数据长度两个字节
- uiRcSize=uiRcSize<<8;
- uiRcSize=uiRcSize+ucRcregBuf[uiRcMoveIndex+5];
- ucRcCy=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]; //记载最终一个字节的校验
- ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=0;//清零最终一个字节的累加和变量
- for(i=0;i<(3+1+2+uiRcSize);i++) //核算校验累加和
- {
- ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]+ucRcregBuf[uiRcMoveIndex+i];
- }
- if(ucRcCy==ucRcregBuf[uiRcMoveIndex+6+uiRcSize])//假如一串数据校验正确,则进入以下数据指令的判别
- {
- switch(ucRcType) //依据不同的数据类型来做不同的数据处理
- {
- case 0xf5: //回来的是正确的校验指令
- ucReceiveStatus=1;//代表校验正确
- break;
- case 0xfa: //回来的是过错的校验指令
- ucReceiveStatus=2;//代表校验过错
- break;
- }
- }
- break; //退出循环
- }
- uiRcMoveIndex++; //由于是判别数据头,游标向着数组最尾端的方向移动
- }
- uiRcregTotal=0;//清空缓冲的下标,便利下次从头从0下标开端承受新数据
- }
- }
- void eusart_send(unsigned char ucSendData) //发送一个字节,内部自带每个字节之间的delay延时
- {
- ES = 0; //关串口中止
- TI = 0; //清零串口发送完结中止请求标志
- SBUF =ucSendData; //发送一个字节
- delay_short(400);//每个字节之间的延时,这儿十分要害,也是最简单犯错的当地。延时的巨细请依据实践项目来调整
- TI = 0; //清零串口发送完结中止请求标志
- ES = 1; //答应串口中止
- }
- void display_service(void) //显现的窗口菜单服务程序
- {
- switch(ucWd)//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
- {
- case 1: //显现P–1窗口的数据
- if(ucWd1Update==1)//窗口1要悉数更新显现
- {
- ucWd1Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow8=12;//第8位数码管显现P
- ucDigShow7=11;//第7位数码管显现-
- ucDigShow6=1; //第6位数码管显现1
- ucDigShow5=10;//第5位数码管显现无
- //先分化数据
- ucTemp4=uiSetData1/1000;
- ucTemp3=uiSetData1%1000/100;
- ucTemp2=uiSetData1%100/10;
- ucTemp1=uiSetData1%10;
- //再过渡需求显现的数据到缓冲变量里,让过渡的时刻越短越好
- if(uiSetData1<1000)
- {
- ucDigShow4=10;//假如小于1000,千位显现无
- }
- else
- {
- ucDigShow4=ucTemp4;//第4位数码管要显现的内容
- }
- if(uiSetData1<100)
- {
- ucDigShow3=10;//假如小于100,百位显现无
- }
- else
- {
- ucDigShow3=ucTemp3;//第3位数码管要显现的内容
- }
- if(uiSetData1<10)
- {
- ucDigShow2=10;//假如小于10,十位显现无
- }
- else
- {
- ucDigShow2=ucTemp2;//第2位数码管要显现的内容
- }
- ucDigShow1=ucTemp1;//第1位数码管要显现的内容
- }
- break;
- case 2://显现P–2窗口的数据
- if(ucWd2Update==1)//窗口2要悉数更新显现
- {
- ucWd2Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow8=12;//第8位数码管显现P
- ucDigShow7=11;//第7位数码管显现-
- ucDigShow6=2;//第6位数码管显现2
- ucDigShow5=10; //第5位数码管显现无
- ucTemp4=uiSetData2/1000; //分化数据
- ucTemp3=uiSetData2%1000/100;
- ucTemp2=uiSetData2%100/10;
- ucTemp1=uiSetData2%10;
- if(uiSetData2<1000)
- {
- ucDigShow4=10;//假如小于1000,千位显现无
- }
- else
- {
- ucDigShow4=ucTemp4;//第4位数码管要显现的内容
- }
- if(uiSetData2<100)
- {
- ucDigShow3=10;//假如小于100,百位显现无
- }
- else
- {
- ucDigShow3=ucTemp3;//第3位数码管要显现的内容
- }
- if(uiSetData2<10)
- {
- ucDigShow2=10;//假如小于10,十位显现无
- }
- else
- {
- ucDigShow2=ucTemp2;//第2位数码管要显现的内容
- }
- ucDigShow1=ucTemp1;//第1位数码管要显现的内容
- }
- break;
- case 3://显现P–3窗口的数据
- if(ucWd3Update==1)//窗口3要悉数更新显现
- {
- ucWd3Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow8=12;//第8位数码管显现P
- ucDigShow7=11;//第7位数码管显现-
- ucDigShow6=3;//第6位数码管显现3
- ucDigShow5=10; //第5位数码管显现无
- ucTemp4=uiSetData3/1000; //分化数据
- ucTemp3=uiSetData3%1000/100;
- ucTemp2=uiSetData3%100/10;
- ucTemp1=uiSetData3%10;
- if(uiSetData3<1000)
- {
- ucDigShow4=10;//假如小于1000,千位显现无
- }
- else
- {
- ucDigShow4=ucTemp4;//第4位数码管要显现的内容
- }
- if(uiSetData3<100)
- {
- ucDigShow3=10;//假如小于100,百位显现无
- }
- else
- {
- ucDigShow3=ucTemp3;//第3位数码管要显现的内容
- }
- if(uiSetData3<10)
- {
- ucDigShow2=10;//假如小于10,十位显现无
- }
- else
- {
- ucDigShow2=ucTemp2;//第2位数码管要显现的内容
- }
- ucDigShow1=ucTemp1;//第1位数码管要显现的内容
- }
- break;
- case 4://显现P–4窗口的数据
- if(ucWd4Update==1)//窗口4要悉数更新显现
- {
- ucWd4Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow8=12;//第8位数码管显现P
- ucDigShow7=11;//第7位数码管显现-
- ucDigShow6=4;//第6位数码管显现4
- ucDigShow5=10; //第5位数码管显现无
- ucTemp4=uiSetData4/1000; //分化数据
- ucTemp3=uiSetData4%1000/100;
- ucTemp2=uiSetData4%100/10;
- ucTemp1=uiSetData4%10;
- if(uiSetData4<1000)
- {
- ucDigShow4=10;//假如小于1000,千位显现无
- }
- else
- {
- ucDigShow4=ucTemp4;//第4位数码管要显现的内容
- }
- if(uiSetData4<100)
- {
- ucDigShow3=10;//假如小于100,百位显现无
- }
- else
- {
- ucDigShow3=ucTemp3;//第3位数码管要显现的内容
- }
- if(uiSetData4<10)
- {
- ucDigShow2=10;//假如小于10,十位显现无
- }
- else
- {
- ucDigShow2=ucTemp2;//第2位数码管要显现的内容
- }
- ucDigShow1=ucTemp1;//第1位数码管要显现的内容
- }
- break;
- }
- }
- void key_scan(void)//按键扫描函数 放在守时中止里
- {
- if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock1=0; //按键自锁标志清零
- uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt1++; //累加守时中止次数
- if(uiKeyTimeCnt1>const_key_time1)
- {
- uiKeyTimeCnt1=0;
- ucKeyLock1=1;//自锁按键置位,防止一向触发
- ucKeySec=1; //触发1号键
- }
- }
- if(key_sr2==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock2=0; //按键自锁标志清零
- uiKeyTimeCnt2=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock2==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt2++; //累加守时中止次数
- if(uiKeyTimeCnt2>const_key_time2)
- {
- uiKeyTimeCnt2=0;
- ucKeyLock2=1;//自锁按键置位,防止一向触发
- ucKeySec=2; //触发2号键
- }
- }
- if(key_sr3==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock3=0; //按键自锁标志清零
- uiKeyTimeCnt3=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock3==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt3++; //累加守时中止次数
- if(uiKeyTimeCnt3>const_key_time3)
- {
- uiKeyTimeCnt3=0;
- ucKeyLock3=1;//自锁按键置位,防止一向触发
- ucKeySec=3; //触发3号键
- }
- }
- if(key_sr4==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock4=0; //按键自锁标志清零
- uiKeyTimeCnt4=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock4==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt4++; //累加守时中止次数
- if(uiKeyTimeCnt4>const_key_time4)
- {
- uiKeyTimeCnt4=0;
- ucKeyLock4=1;//自锁按键置位,防止一向触发
- ucKeySec=4; //触发4号键
- }
- }
- }
- void key_service(void) //按键服务的应用程序
- {
- switch(ucKeySec) //按键服务状况切换
- {
- case 1:// 加按键 对应朱兆祺学习板的S1键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 1:
- uiSetData1++;
- if(uiSetData1>9999) //最大值是9999
- {
- uiSetData1=9999;
- }
- ucWd1Update=1;//窗口1更新显现
- break;
- case 2:
- uiSetData2++;
- if(uiSetData2>9999) //最大值是9999
- {
- uiSetData2=9999;
- }
- ucWd2Update=1;//窗口2更新显现
- break;
- case 3:
- uiSetData3++;
- if(uiSetData3>9999) //最大值是9999
- {
- uiSetData3=9999;
- }
- ucWd3Update=1;//窗口3更新显现
- break;
- case 4:
- uiSetData4++;
- if(uiSetData4>9999) //最大值是9999
- {
- uiSetData4=9999;
- }
- ucWd4Update=1;//窗口4更新显现
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 2:// 减按键 对应朱兆祺学习板的S5键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 1:
- uiSetData1–;
- if(uiSetData1>9999)
- {
- uiSetData1=0;//最小值是0
- }
- ucWd1Update=1;//窗口1更新显现
- break;
- case 2:
- uiSetData2–;
- if(uiSetData2>9999)
- {
- uiSetData2=0;//最小值是0
- }
- ucWd2Update=1;//窗口2更新显现
- break;
- case 3:
- uiSetData3–;
- if(uiSetData3>9999)
- {
- uiSetData3=0;//最小值是0
- }
- ucWd3Update=1;//窗口3更新显现
- break;
- case 4:
- uiSetData4–;
- if(uiSetData4>9999)
- {
- uiSetData4=0;//最小值是0
- }
- ucWd4Update=1;//窗口4更新显现
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 3:// 切换窗口按键 对应朱兆祺学习板的S9键
- ucWd++;//切换窗口
- if(ucWd>4)
- {
- ucWd=1;
- }
- switch(ucWd)//在不同的窗口下,在不同的窗口下,更新显现不同的窗口
- {
- case 1:
- ucWd1Update=1;//窗口1更新显现
- break;
- case 2:
- ucWd2Update=1;//窗口2更新显现
- break;
- case 3:
- ucWd3Update=1;//窗口3更新显现
- break;
- case 4:
- ucWd4Update=1;//窗口4更新显现
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 4:// 发动发送数据和复位按键 对应朱兆祺学习板的S13键
- switch(ucStatus)//在不同的状况下,进行不同的操作
- {
- case 0://处于待机状况,则发动发送数据
- ucErrorCnt=0; //累计过错总数清零
- ucSendTotal=0; //现已发送串数据总数清零
- ucSendStep=0; //发送数据的进程进程清零,回来开端的进程待命
- ucStatus=1; //发动发送数据,1代表正在通讯进程
- break;
- case 1://处于正在通讯的进程
- break;
- case 2: //发送数据犯错,比方中心超时没有接纳到数据
- ucStatus=0; //切换回待机的状况
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- }
- }
- void display_drive(void)
- {
- //以下程序,假如加一些数组和移位的元素,还能够紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
- switch(ucDisplayDriveStep)
- {
- case 1://显现第1位
- ucDigShowTemp=dig_table[ucDigShow1];
- if(ucDigDot1==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfe);
- break;
- case 2://显现第2位
- ucDigShowTemp=dig_table[ucDigShow2];
- if(ucDigDot2==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfd);
- break;
- case 3://显现第3位
- ucDigShowTemp=dig_table[ucDigShow3];
- if(ucDigDot3==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfb);
- break;
- case 4://显现第4位
- ucDigShowTemp=dig_table[ucDigShow4];
- if(ucDigDot4==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xf7);
- break;
- case 5://显现第5位
- ucDigShowTemp=dig_table[ucDigShow5];
- if(ucDigDot5==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xef);
- break;
- case 6://显现第6位
- ucDigShowTemp=dig_table[ucDigShow6];
- if(ucDigDot6==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xdf);
- break;
- case 7://显现第7位
- ucDigShowTemp=dig_table[ucDigShow7];
- if(ucDigDot7==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xbf);
- break;
- case 8://显现第8位
- ucDigShowTemp=dig_table[ucDigShow8];
- if(ucDigDot8==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0x7f);
- break;
- }
- ucDisplayDriveStep++;
- if(ucDisplayDriveStep>8)//扫描完8个数码管后,从头从榜首个开端扫描
- {
- ucDisplayDriveStep=1;
- }
- }
- //数码管的74HC595驱动函数
- void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
- {
- unsigned char i;
- unsigned char ucTempData;
- dig_hc595_sh_dr=0;
- dig_hc595_st_dr=0;
- ucTempData=ucDigStatusTemp16_09;//先送高8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)dig_hc595_ds_dr=1;
- else dig_hc595_ds_dr=0;
- dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- dig_hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- ucTempData=ucDigStatusTemp08_01;//再先送低8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)dig_hc595_ds_dr=1;
- else dig_hc595_ds_dr=0;
- dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- dig_hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
- delay_short(1);
- dig_hc595_st_dr=1;
- delay_short(1);
- dig_hc595_sh_dr=0; //拉低,抗干扰就增强
- dig_hc595_st_dr=0;
- dig_hc595_ds_dr=0;
- }
- //LED灯的74HC595驱动函数
- void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
- {
- unsigned char i;
- unsigned char ucTempData;
- hc595_sh_dr=0;
- hc595_st_dr=0;
- ucTempData=ucLedStatusTemp16_09;//先送高8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)hc595_ds_dr=1;
- else hc595_ds_dr=0;
- hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- ucTempData=ucLedStatusTemp08_01;//再先送低8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)hc595_ds_dr=1;
- else hc595_ds_dr=0;
- hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
- delay_short(1);
- hc595_st_dr=1;
- delay_short(1);
- hc595_sh_dr=0; //拉低,抗干扰就增强
- hc595_st_dr=0;
- hc595_ds_dr=0;
- }
- void usart_receive(void) interrupt 4 //串口接纳数据中止
- {
- if(RI==1)
- {
- RI = 0;
- ++uiRcregTotal;
- if(uiRcregTotal>const_rc_size)//超越缓冲区
- {
- uiRcregTotal=const_rc_size;
- }
- ucRcregBuf[uiRcregTotal-1]=SBUF; //将串口接纳到的数据缓存到接纳缓冲区里
- if(ucSendCntLock==0)//原子锁判别
- {
- ucSendCntLock=1; //加锁
- uiSendCnt=0;//及时喂狗,虽然在守时中止那儿此变量会不断累加,可是只需串口的数据还没发送结束,那么它永久也长不大,由于每个串口接纳中止它都被清零。
- ucSendCntLock=0; //解锁
- }
- }
- else//我在其它单片机上都不必else这段代码的,可能在51单片机上多添加” TI = 0;”稳定性会更好吧。
- {
- TI = 0;//假如不是串口接纳中止,那么必定是串口发送中止,及时铲除发送中止的标志,不然一向发送中止
- }
- }
- void T0_time(void) interrupt 1 //守时中止
- {
- TF0=0;//铲除中止标志
- TR0=0; //关中止
- /* 注释一:
- * 此处多添加一个原子锁,作为中止与主函数同享数据的维护,实践上是学习了”红金龙吸味”关于原子锁的主张.
- */
- if(ucSendCntLock==0)//原子锁判别
- {
- ucSendCntLock=1; //加锁
- if(uiSendCnt
- {
- uiSendCnt++; //表面上这个数据不断累加,可是在串口中止里,每接纳一个字节它都会被清零,除非这个中心没有串口数据过来
- ucSendLock=1; //开自锁标志
- }
- ucSendCntLock=0; //解锁
- }
- if(ucVoiceLock==0) //原子锁判别
- {
- if(uiVoiceCnt!=0)
- {
- uiVoiceCnt–; //每次进入守时中止都自减1,直到等于零中止。才中止鸣叫
- beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
- }
- else
- {
- ; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也能够。
- beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
- }
- }
- if(ucStatus!=0) //处于非待机的状况,Led闪耀
- {
- if(ucLedLock==0)//原子锁判别
- {
- uiLedCnt++; //Led闪耀计时器不断累加
- }
- }
- if(ucStatus==1) //处于正在通讯的状况,
- {
- if(ucSendTimeOutLock==0)//原子锁判别
- {
- uiSendTimeOutCnt++; //超时计时器累加
- }
- }
- key_scan(); //按键扫描函数
- display_drive();//数码管字模的驱动函数
- TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
- TL0=0x0b;
- TR0=1;//开中止
- }
- void delay_short(unsigned int uiDelayShort)
- {
- unsigned int i;
- for(i=0;i
- {
- ; //一个分号相当于履行一条空句子
- }
- }
- void delay_long(unsigned int uiDelayLong)
- {
- unsigned int i;
- unsigned int j;
- for(i=0;i
- {
- for(j=0;j<500;j++)//内嵌循环的空指令数量
- {
- ; //一个分号相当于履行一条空句子
- }
- }
- }
- void initial_myself(void)//榜首区 初始化单片机
- {
- /* 注释二:
- * 矩阵键盘也能够做独立按键,条件是把某一根公共输出线输出低电平,
- * 模仿独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
- * 朱兆祺51学习板的S1便是本程序中用到的一个独立按键。
- */
- key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
- led_dr=1;//点亮独立LED灯
- beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
- hc595_drive(0x00,0x00);//封闭一切通过别的两个74HC595驱动的LED灯
- TMOD=0x01;//设置守时器0为工作方式1
- TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
- TL0=0x0b;
- //装备串口
- SCON=0x50;
- TMOD=0X21;
- /* 注释三:
- * 为了确保串口中止接纳的数据不丢掉,有必要设置IP = 0x10,相当于把串口中止设置为最高优先级,
- * 这个时分,串口中止能够打断任何其他的中止服务函数完结嵌套,
- */
- IP =0x10;//把串口中止设置为最高优先级,有必要的。
- TH1=TL1=-(11059200L/12/32/9600);//串口波特率为9600。
- TR1=1;
- }
- void initial_peripheral(void) //第二区 初始化外围
- {
- ucDigDot8=0; //小数点悉数不显现
- ucDigDot7=0;
- ucDigDot6=0;
- ucDigDot5=0;
- ucDigDot4=0;
- ucDigDot3=0;
- ucDigDot2=0;
- ucDigDot1=0;
- EA=1; //开总中止
- ES=1; //答应串口中止
- ET0=1; //答应守时中止
- TR0=1; //发动守时中止
- }
总结陈词:
前面花了很多篇幅具体地解说了串口收发数据的程序结构,从下一节开端我解说单片机掉电后数据保存的内容,欲知概况,请听下回分化—–使用AT24C02进行掉电后的数据保存。