您的位置 首页 IOT

第44节:从机的串口收发归纳程序结构

开场白:根据上一节的预告,本来这一节内容打算讲利用AT24C02进行掉电后的数据保存的,但是由于网友261854681强烈建议我讲一个完整的串口收

开场白:
依据上一节的预告,原本这一节内容计划讲“使用AT24C02进行掉电后的数据保存”的,可是由于网友“261854681”强烈主张我讲一个完好的串口收发程序实例,因而我决议再花两节篇幅讲讲这方面的内容。
实践上在大部分的项目中,串口都需求“一收一应对”的握手协议,上位机作为主机,单片机作为从机,主机先发一串数据,从机收到数据后进行校验判别,假如校验正确则回来正确应对指令,假如校验过错则回来过错应对指令,主机收到应对指令后,假如发现是正确应对指令则持续发送其它的新数据,假如发现是过错应对指令,或许超时没有接纳到任何应对指令,则持续重发,假如接连重发三次都是过错应对或许无应对,主机就进行报错处理。
这节先讲从机的收发端程序实例。要教会我们三个知识点:
榜首个:为了确保串口中止接纳的数据不丢掉,在初始化时有必要设置IP = 0x10,相当于把串口中止设置为最高优先级,这个时分,串口中止能够打断任何其他的中止服务函数,完结中止嵌套。
第二个:从机端的收发端程序结构
第三个:从机的状况指示程序结构。能够指示待机,通讯中,超时犯错三种状况。

具体内容,请看源代码解说。

(1)硬件渠道:
依据朱兆祺51单片机学习板

(2)完结功用:
显现和独立按键部分依据第29节的程序来改编,用朱兆祺51单片机学习板中的S1,S5,S9,S13作为独立按键。
一共有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.
通讯协议:EB 00 55GG 00 02 XX XXCY
其间第1,2,3位EB 00 55便是数据头
其间第4位GG便是数据类型。01代表更改参数1,02代表更改参数2,03代表更改参数3,04代表更改参数4,
其间第5,6位00 02便是有用数据长度。高位在左,低位在右。
其间从第7,8位XX XX是被更改的参数。高位在左,低位在右。
第9位CY是累加和,前面一切字节的累加。
一个完好的通讯有必要接纳完4串数据,每串数据之间的间隔时刻不能超越10秒钟,不然以为通讯超时犯错引发蜂鸣器报警。假如接纳到得数据校验正确,
则回来校验正确应对:eb 00 55 f5 00 00 35,
不然回来校验犯错应对::eb 00 55 fa 00 00 3a。
体系处于待机状况时,LED灯一向亮,
体系处于非待机状况时,LED灯闪耀,
体系处于通讯超时犯错状况时,LED灯闪耀,而且蜂鸣器间歇鸣叫报警。

通过电脑的串口帮手,顺次发送以下测试数据,将会别离更改参数1,参数2,参数3,参数4。留意,每串数据之间的时刻最大不能超越10秒,不然体系以为通讯超时报警。
把参数1更改为十进制的1: eb 00 55 01 00 02 00 01 44
把参数2更改为十进制的12:eb 00 55 02 00 02 00 0c 50
把参数3更改为十进制的123: eb 00 55 03 00 02 00 7b c0
把参数4更改为十进制的1234:eb 00 55 04 00 02 04 d2 1c

(3)源代码解说如下:

  1. #include “REG52.H”
  2. #define const_voice_short40 //蜂鸣器短叫的持续时刻
  3. #define const_key_time120 //按键去颤动延时的时刻
  4. #define const_key_time220 //按键去颤动延时的时刻
  5. #define const_key_time320 //按键去颤动延时的时刻
  6. #define const_key_time420 //按键去颤动延时的时刻
  7. #define const_led_0_5s200 //大约0.5秒的时刻
  8. #define const_led_1s 400 //大约1秒的时刻
  9. #define const_send_time_out 4000//通讯超时犯错的时刻 大约10秒
  10. #define const_rc_size20//接纳串口中止数据的缓冲区数组巨细
  11. #define const_receive_time5//假如超越这个时刻没有串口数据过来,就以为一串数据现已悉数接纳完,这个时刻依据实践情况来调整巨细
  12. #define const_send_size10//串口发送数据的缓冲区数组巨细
  13. void initial_myself(void);
  14. void initial_peripheral(void);
  15. void delay_short(unsigned int uiDelayShort);
  16. void delay_long(unsigned int uiDelaylong);
  17. //驱动数码管的74HC595
  18. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  19. void display_drive(void); //显现数码管字模的驱动函数
  20. void display_service(void); //显现的窗口菜单服务程序
  21. //驱动LED的74HC595
  22. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  23. void T0_time(void);//守时中止函数
  24. void usart_receive(void); //串口接纳中止函数
  25. void usart_service(void);//串口服务程序,在main函数里
  26. void eusart_send(unsigned char ucSendData); //发送一个字节,内部自带每个字节之间的delay延时
  27. void key_service(void); //按键服务的应用程序
  28. void key_scan(void);//按键扫描函数 放在守时中止里
  29. void status_service(void);//状况显现的应用程序
  30. sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
  31. sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
  32. sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
  33. sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
  34. sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
  35. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
  36. sbit led_dr=P3^5;//作为状况指示灯 亮的时分表明待机状况.闪耀表明非待机状况,处于正在发送数据或许犯错的状况
  37. sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
  38. sbit dig_hc595_st_dr=P2^1;
  39. sbit dig_hc595_ds_dr=P2^2;
  40. sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
  41. sbit hc595_st_dr=P2^4;
  42. sbit hc595_ds_dr=P2^5;
  43. unsigned char ucSendregBuf[const_send_size]; //发送的缓冲区数组
  44. unsigned intuiSendCnt=0; //用来辨认串口是否接纳完一串数据的计时器
  45. unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接纳完一串数据只处理一次
  46. unsigned intuiRcregTotal=0;//代表当时缓冲区现已接纳了多少个数据
  47. unsigned char ucRcregBuf[const_rc_size]; //接纳串口中止数据的缓冲区数组
  48. unsigned intuiRcMoveIndex=0;//用来解析数据协议的中心变量
  49. unsigned charucSendCntLock=0; //串口计时器的原子锁
  50. unsigned char ucRcType=0;//数据类型
  51. unsigned intuiRcSize=0;//数据长度
  52. unsigned char ucRcCy=0;//校验累加和
  53. unsigned intuiLedCnt=0;//操控Led闪耀的延时计时器
  54. unsigned intuiSendTimeOutCnt=0; //用来辨认接纳数据超时的计时器
  55. unsigned char ucSendTimeOutLock=0; //原子锁
  56. unsigned char ucStatus=0; //当时状况变量 0代表待机 1代表正在通讯进程 2代表发送犯错
  57. unsigned char ucKeySec=0; //被触发的按键编号
  58. unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
  59. unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
  60. unsigned intuiKeyTimeCnt2=0; //按键去颤动延时计数器
  61. unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
  62. unsigned intuiKeyTimeCnt3=0; //按键去颤动延时计数器
  63. unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
  64. unsigned intuiKeyTimeCnt4=0; //按键去颤动延时计数器
  65. unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
  66. unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
  67. unsigned charucVoiceLock=0;//蜂鸣器鸣叫的原子锁
  68. unsigned char ucDigShow8;//第8位数码管要显现的内容
  69. unsigned char ucDigShow7;//第7位数码管要显现的内容
  70. unsigned char ucDigShow6;//第6位数码管要显现的内容
  71. unsigned char ucDigShow5;//第5位数码管要显现的内容
  72. unsigned char ucDigShow4;//第4位数码管要显现的内容
  73. unsigned char ucDigShow3;//第3位数码管要显现的内容
  74. unsigned char ucDigShow2;//第2位数码管要显现的内容
  75. unsigned char ucDigShow1;//第1位数码管要显现的内容
  76. unsigned char ucDigDot8;//数码管8的小数点是否显现的标志
  77. unsigned char ucDigDot7;//数码管7的小数点是否显现的标志
  78. unsigned char ucDigDot6;//数码管6的小数点是否显现的标志
  79. unsigned char ucDigDot5;//数码管5的小数点是否显现的标志
  80. unsigned char ucDigDot4;//数码管4的小数点是否显现的标志
  81. unsigned char ucDigDot3;//数码管3的小数点是否显现的标志
  82. unsigned char ucDigDot2;//数码管2的小数点是否显现的标志
  83. unsigned char ucDigDot1;//数码管1的小数点是否显现的标志
  84. unsigned char ucDigShowTemp=0; //暂时中心变量
  85. unsigned char ucDisplayDriveStep=1;//动态扫描数码管的进程变量
  86. unsigned char ucWd1Update=1; //窗口1更新显现标志
  87. unsigned char ucWd2Update=0; //窗口2更新显现标志
  88. unsigned char ucWd3Update=0; //窗口3更新显现标志
  89. unsigned char ucWd4Update=0; //窗口4更新显现标志
  90. unsigned char ucWd=1;//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
  91. unsigned intuiSetData1=0;//本程序中需求被设置的参数1
  92. unsigned intuiSetData2=0;//本程序中需求被设置的参数2
  93. unsigned intuiSetData3=0;//本程序中需求被设置的参数3
  94. unsigned intuiSetData4=0;//本程序中需求被设置的参数4
  95. unsigned char ucTemp1=0;//中心过渡变量
  96. unsigned char ucTemp2=0;//中心过渡变量
  97. unsigned char ucTemp3=0;//中心过渡变量
  98. unsigned char ucTemp4=0;//中心过渡变量
  99. //依据原理图得出的共阴数码管字模表
  100. code unsigned char dig_table[]=
  101. {
  102. 0x3f,//0 序号0
  103. 0x06,//1 序号1
  104. 0x5b,//2 序号2
  105. 0x4f,//3 序号3
  106. 0x66,//4 序号4
  107. 0x6d,//5 序号5
  108. 0x7d,//6 序号6
  109. 0x07,//7 序号7
  110. 0x7f,//8 序号8
  111. 0x6f,//9 序号9
  112. 0x00,//无 序号10
  113. 0x40,//- 序号11
  114. 0x73,//P 序号12
  115. };
  116. void main()
  117. {
  118. initial_myself();
  119. delay_long(100);
  120. initial_peripheral();
  121. while(1)
  122. {
  123. key_service(); //按键服务的应用程序
  124. usart_service();//串口服务程序
  125. display_service(); //显现的窗口菜单服务程序
  126. status_service();//状况显现的应用程序
  127. }
  128. }
  129. void status_service(void)//状况显现的应用程序
  130. {
  131. if(ucStatus!=0) //处于非待机的状况,Led闪耀
  132. {
  133. if(uiLedCnt
  134. {
  135. led_dr=1;//前半秒亮
  136. if(ucStatus==2)//处于发送数据犯错的状况,则蜂鸣器间歇鸣叫报警
  137. {
  138. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  139. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  140. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  141. }
  142. }
  143. else if(uiLedCnt
  144. {
  145. led_dr=0; //前半秒灭
  146. }
  147. else
  148. {
  149. uiLedCnt=0; //延时计时器清零,让Led灯处于闪耀的重复循环中
  150. }
  151. }
  152. else//处于待机状况,Led一向亮
  153. {
  154. led_dr=1;
  155. }
  156. }
  157. void usart_service(void)//串口服务程序,在main函数里
  158. {
  159. unsigned int i;
  160. if(uiSendCnt>=const_receive_time&&ucSendLock==1) //阐明超越了必定的时刻内,再也没有新数据从串口来
  161. {
  162. ucSendLock=0; //处理一次就锁起来,不必每次都进来,除非有新接纳的数据
  163. //下面的代码进入数据协议解析和数据处理的阶段
  164. uiRcMoveIndex=0; //由于是判别数据头,所以下标移动变量从数组的0开端向最尾端移动
  165. while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))
  166. {
  167. if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55)//数据头eb 00 55的判别
  168. {
  169. ucRcType=ucRcregBuf[uiRcMoveIndex+3]; //数据类型一个字节
  170. uiRcSize=ucRcregBuf[uiRcMoveIndex+4]; //数据长度两个字节
  171. uiRcSize=uiRcSize<<8;
  172. uiRcSize=uiRcSize+ucRcregBuf[uiRcMoveIndex+5];
  173. ucRcCy=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]; //记载最终一个字节的校验
  174. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=0;//清零最终一个字节的累加和变量
  175. for(i=0;i<(3+1+2+uiRcSize);i++) //核算校验累加和
  176. {
  177. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]+ucRcregBuf[uiRcMoveIndex+i];
  178. }
  179. if(ucRcCy==ucRcregBuf[uiRcMoveIndex+6+uiRcSize])//假如校验正确,则进入以下数据处理
  180. {
  181. switch(ucRcType) //依据不同的数据类型来做不同的数据处理
  182. {
  183. case 0x01: //设置参数1
  184. ucStatus=1; //从设置参数1开端,表明当时处于正在发送数据的状况
  185. uiSetData1=ucRcregBuf[uiRcMoveIndex+6];//把两个字节合并成一个int类型的数据
  186. uiSetData1=uiSetData1<<8;
  187. uiSetData1=uiSetData1+ucRcregBuf[uiRcMoveIndex+7];
  188. ucWd1Update=1; //窗口1更新显现
  189. break;
  190. case 0x02: //设置参数2
  191. uiSetData2=ucRcregBuf[uiRcMoveIndex+6];//把两个字节合并成一个int类型的数据
  192. uiSetData2=uiSetData2<<8;
  193. uiSetData2=uiSetData2+ucRcregBuf[uiRcMoveIndex+7];
  194. ucWd2Update=1; //窗口2更新显现
  195. break;
  196. case 0x03: //设置参数3
  197. uiSetData3=ucRcregBuf[uiRcMoveIndex+6];//把两个字节合并成一个int类型的数据
  198. uiSetData3=uiSetData3<<8;
  199. uiSetData3=uiSetData3+ucRcregBuf[uiRcMoveIndex+7];
  200. ucWd3Update=1; //窗口3更新显现
  201. break;
  202. case 0x04: //设置参数4
  203. ucStatus=0; //从设置参数4结束发送数据的状况,表明发送数据的进程成功,切换回待机状况
  204. uiSetData4=ucRcregBuf[uiRcMoveIndex+6];//把两个字节合并成一个int类型的数据
  205. uiSetData4=uiSetData4<<8;
  206. uiSetData4=uiSetData4+ucRcregBuf[uiRcMoveIndex+7];
  207. ucWd4Update=1; //窗口4更新显现
  208. break;
  209. }
  210. ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
  211. ucSendregBuf[1]=0x00;
  212. ucSendregBuf[2]=0x55;
  213. ucSendregBuf[3]=0xf5;//代表校验正确
  214. ucSendregBuf[4]=0x00;
  215. ucSendregBuf[5]=0x00;
  216. ucSendregBuf[6]=0x35;
  217. for(i=0;i<7;i++)//回来校验正确的应对指令
  218. {
  219. eusart_send(ucSendregBuf[i]);//发送一串数据给上位机
  220. }
  221. }
  222. else
  223. {
  224. ucSendTimeOutLock=1; //原子锁加锁
  225. uiSendTimeOutCnt=0;//超时计时器计时清零
  226. ucSendTimeOutLock=0; //原子锁解锁
  227. ucSendregBuf[0]=0xeb; //把预备发送的数据放入发送缓冲区
  228. ucSendregBuf[1]=0x00;
  229. ucSendregBuf[2]=0x55;
  230. ucSendregBuf[3]=0xfa; //代表校验过错
  231. ucSendregBuf[4]=0x00;
  232. ucSendregBuf[5]=0x00;
  233. ucSendregBuf[6]=0x3a;
  234. for(i=0;i<7;i++)//回来校验过错的应对指令
  235. {
  236. eusart_send(ucSendregBuf[i]);//发送一串数据给上位机
  237. }
  238. }
  239. ucSendTimeOutLock=1; //原子锁加锁
  240. uiSendTimeOutCnt=0;//超时计时器计时清零
  241. ucSendTimeOutLock=0; //原子锁解锁
  242. break; //退出循环
  243. }
  244. uiRcMoveIndex++; //由于是判别数据头,游标向着数组最尾端的方向移动
  245. }
  246. uiRcregTotal=0;//清空缓冲的下标,便利下次从头从0下标开端承受新数据
  247. }
  248. }
  249. void eusart_send(unsigned char ucSendData) //发送一个字节,内部自带每个字节之间的delay延时
  250. {
  251. ES = 0; //关串口中止
  252. TI = 0; //清零串口发送完结中止请求标志
  253. SBUF =ucSendData; //发送一个字节
  254. delay_short(400);//每个字节之间的延时,这儿十分要害,也是最简单犯错的当地。延时的巨细请依据实践项目来调整
  255. TI = 0; //清零串口发送完结中止请求标志
  256. ES = 1; //答应串口中止
  257. }
  258. void display_service(void) //显现的窗口菜单服务程序
  259. {
  260. switch(ucWd)//本程序的中心变量,窗口显现变量。类似于一级菜单的变量。代表显现不同的窗口。
  261. {
  262. case 1: //显现P–1窗口的数据
  263. if(ucWd1Update==1)//窗口1要悉数更新显现
  264. {
  265. ucWd1Update=0;//及时清零标志,防止一向进来扫描
  266. ucDigShow8=12;//第8位数码管显现P
  267. ucDigShow7=11;//第7位数码管显现-
  268. ucDigShow6=1; //第6位数码管显现1
  269. ucDigShow5=10;//第5位数码管显现无
  270. //先分化数据
  271. ucTemp4=uiSetData1/1000;
  272. ucTemp3=uiSetData1%1000/100;
  273. ucTemp2=uiSetData1%100/10;
  274. ucTemp1=uiSetData1%10;
  275. //再过渡需求显现的数据到缓冲变量里,让过渡的时刻越短越好
  276. if(uiSetData1<1000)
  277. {
  278. ucDigShow4=10;//假如小于1000,千位显现无
  279. }
  280. else
  281. {
  282. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  283. }
  284. if(uiSetData1<100)
  285. {
  286. ucDigShow3=10;//假如小于100,百位显现无
  287. }
  288. else
  289. {
  290. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  291. }
  292. if(uiSetData1<10)
  293. {
  294. ucDigShow2=10;//假如小于10,十位显现无
  295. }
  296. else
  297. {
  298. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  299. }
  300. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  301. }
  302. break;
  303. case 2://显现P–2窗口的数据
  304. if(ucWd2Update==1)//窗口2要悉数更新显现
  305. {
  306. ucWd2Update=0;//及时清零标志,防止一向进来扫描
  307. ucDigShow8=12;//第8位数码管显现P
  308. ucDigShow7=11;//第7位数码管显现-
  309. ucDigShow6=2;//第6位数码管显现2
  310. ucDigShow5=10; //第5位数码管显现无
  311. ucTemp4=uiSetData2/1000; //分化数据
  312. ucTemp3=uiSetData2%1000/100;
  313. ucTemp2=uiSetData2%100/10;
  314. ucTemp1=uiSetData2%10;
  315. if(uiSetData2<1000)
  316. {
  317. ucDigShow4=10;//假如小于1000,千位显现无
  318. }
  319. else
  320. {
  321. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  322. }
  323. if(uiSetData2<100)
  324. {
  325. ucDigShow3=10;//假如小于100,百位显现无
  326. }
  327. else
  328. {
  329. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  330. }
  331. if(uiSetData2<10)
  332. {
  333. ucDigShow2=10;//假如小于10,十位显现无
  334. }
  335. else
  336. {
  337. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  338. }
  339. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  340. }
  341. break;
  342. case 3://显现P–3窗口的数据
  343. if(ucWd3Update==1)//窗口3要悉数更新显现
  344. {
  345. ucWd3Update=0;//及时清零标志,防止一向进来扫描
  346. ucDigShow8=12;//第8位数码管显现P
  347. ucDigShow7=11;//第7位数码管显现-
  348. ucDigShow6=3;//第6位数码管显现3
  349. ucDigShow5=10; //第5位数码管显现无
  350. ucTemp4=uiSetData3/1000; //分化数据
  351. ucTemp3=uiSetData3%1000/100;
  352. ucTemp2=uiSetData3%100/10;
  353. ucTemp1=uiSetData3%10;
  354. if(uiSetData3<1000)
  355. {
  356. ucDigShow4=10;//假如小于1000,千位显现无
  357. }
  358. else
  359. {
  360. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  361. }
  362. if(uiSetData3<100)
  363. {
  364. ucDigShow3=10;//假如小于100,百位显现无
  365. }
  366. else
  367. {
  368. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  369. }
  370. if(uiSetData3<10)
  371. {
  372. ucDigShow2=10;//假如小于10,十位显现无
  373. }
  374. else
  375. {
  376. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  377. }
  378. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  379. }
  380. break;
  381. case 4://显现P–4窗口的数据
  382. if(ucWd4Update==1)//窗口4要悉数更新显现
  383. {
  384. ucWd4Update=0;//及时清零标志,防止一向进来扫描
  385. ucDigShow8=12;//第8位数码管显现P
  386. ucDigShow7=11;//第7位数码管显现-
  387. ucDigShow6=4;//第6位数码管显现4
  388. ucDigShow5=10; //第5位数码管显现无
  389. ucTemp4=uiSetData4/1000; //分化数据
  390. ucTemp3=uiSetData4%1000/100;
  391. ucTemp2=uiSetData4%100/10;
  392. ucTemp1=uiSetData4%10;
  393. if(uiSetData4<1000)
  394. {
  395. ucDigShow4=10;//假如小于1000,千位显现无
  396. }
  397. else
  398. {
  399. ucDigShow4=ucTemp4;//第4位数码管要显现的内容
  400. }
  401. if(uiSetData4<100)
  402. {
  403. ucDigShow3=10;//假如小于100,百位显现无
  404. }
  405. else
  406. {
  407. ucDigShow3=ucTemp3;//第3位数码管要显现的内容
  408. }
  409. if(uiSetData4<10)
  410. {
  411. ucDigShow2=10;//假如小于10,十位显现无
  412. }
  413. else
  414. {
  415. ucDigShow2=ucTemp2;//第2位数码管要显现的内容
  416. }
  417. ucDigShow1=ucTemp1;//第1位数码管要显现的内容
  418. }
  419. break;
  420. }
  421. }
  422. void key_scan(void)//按键扫描函数 放在守时中止里
  423. {
  424. if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  425. {
  426. ucKeyLock1=0; //按键自锁标志清零
  427. uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  428. }
  429. else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
  430. {
  431. uiKeyTimeCnt1++; //累加守时中止次数
  432. if(uiKeyTimeCnt1>const_key_time1)
  433. {
  434. uiKeyTimeCnt1=0;
  435. ucKeyLock1=1;//自锁按键置位,防止一向触发
  436. ucKeySec=1; //触发1号键
  437. }
  438. }
  439. if(key_sr2==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  440. {
  441. ucKeyLock2=0; //按键自锁标志清零
  442. uiKeyTimeCnt2=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  443. }
  444. else if(ucKeyLock2==0)//有按键按下,且是榜首次被按下
  445. {
  446. uiKeyTimeCnt2++; //累加守时中止次数
  447. if(uiKeyTimeCnt2>const_key_time2)
  448. {
  449. uiKeyTimeCnt2=0;
  450. ucKeyLock2=1;//自锁按键置位,防止一向触发
  451. ucKeySec=2; //触发2号键
  452. }
  453. }
  454. if(key_sr3==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  455. {
  456. ucKeyLock3=0; //按键自锁标志清零
  457. uiKeyTimeCnt3=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  458. }
  459. else if(ucKeyLock3==0)//有按键按下,且是榜首次被按下
  460. {
  461. uiKeyTimeCnt3++; //累加守时中止次数
  462. if(uiKeyTimeCnt3>const_key_time3)
  463. {
  464. uiKeyTimeCnt3=0;
  465. ucKeyLock3=1;//自锁按键置位,防止一向触发
  466. ucKeySec=3; //触发3号键
  467. }
  468. }
  469. if(key_sr4==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
  470. {
  471. ucKeyLock4=0; //按键自锁标志清零
  472. uiKeyTimeCnt4=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
  473. }
  474. else if(ucKeyLock4==0)//有按键按下,且是榜首次被按下
  475. {
  476. uiKeyTimeCnt4++; //累加守时中止次数
  477. if(uiKeyTimeCnt4>const_key_time4)
  478. {
  479. uiKeyTimeCnt4=0;
  480. ucKeyLock4=1;//自锁按键置位,防止一向触发
  481. ucKeySec=4; //触发4号键
  482. }
  483. }
  484. }
  485. void key_service(void) //按键服务的应用程序
  486. {
  487. switch(ucKeySec) //按键服务状况切换
  488. {
  489. case 1:// 加按键 对应朱兆祺学习板的S1键
  490. switch(ucWd)//在不同的窗口下,设置不同的参数
  491. {
  492. case 1:
  493. uiSetData1++;
  494. if(uiSetData1>9999) //最大值是9999
  495. {
  496. uiSetData1=9999;
  497. }
  498. ucWd1Update=1;//窗口1更新显现
  499. break;
  500. case 2:
  501. uiSetData2++;
  502. if(uiSetData2>9999) //最大值是9999
  503. {
  504. uiSetData2=9999;
  505. }
  506. ucWd2Update=1;//窗口2更新显现
  507. break;
  508. case 3:
  509. uiSetData3++;
  510. if(uiSetData3>9999) //最大值是9999
  511. {
  512. uiSetData3=9999;
  513. }
  514. ucWd3Update=1;//窗口3更新显现
  515. break;
  516. case 4:
  517. uiSetData4++;
  518. if(uiSetData4>9999) //最大值是9999
  519. {
  520. uiSetData4=9999;
  521. }
  522. ucWd4Update=1;//窗口4更新显现
  523. break;
  524. }
  525. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  526. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  527. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  528. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  529. break;
  530. case 2:// 减按键 对应朱兆祺学习板的S5键
  531. switch(ucWd)//在不同的窗口下,设置不同的参数
  532. {
  533. case 1:
  534. uiSetData1–;
  535. if(uiSetData1>9999)
  536. {
  537. uiSetData1=0;//最小值是0
  538. }
  539. ucWd1Update=1;//窗口1更新显现
  540. break;
  541. case 2:
  542. uiSetData2–;
  543. if(uiSetData2>9999)
  544. {
  545. uiSetData2=0;//最小值是0
  546. }
  547. ucWd2Update=1;//窗口2更新显现
  548. break;
  549. case 3:
  550. uiSetData3–;
  551. if(uiSetData3>9999)
  552. {
  553. uiSetData3=0;//最小值是0
  554. }
  555. ucWd3Update=1;//窗口3更新显现
  556. break;
  557. case 4:
  558. uiSetData4–;
  559. if(uiSetData4>9999)
  560. {
  561. uiSetData4=0;//最小值是0
  562. }
  563. ucWd4Update=1;//窗口4更新显现
  564. break;
  565. }
  566. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  567. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  568. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  569. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  570. break;
  571. case 3:// 切换窗口按键 对应朱兆祺学习板的S9键
  572. ucWd++;//切换窗口
  573. if(ucWd>4)
  574. {
  575. ucWd=1;
  576. }
  577. switch(ucWd)//在不同的窗口下,在不同的窗口下,更新显现不同的窗口
  578. {
  579. case 1:
  580. ucWd1Update=1;//窗口1更新显现
  581. break;
  582. case 2:
  583. ucWd2Update=1;//窗口2更新显现
  584. break;
  585. case 3:
  586. ucWd3Update=1;//窗口3更新显现
  587. break;
  588. case 4:
  589. ucWd4Update=1;//窗口4更新显现
  590. break;
  591. }
  592. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  593. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  594. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  595. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  596. break;
  597. case 4:// 复位按键 对应朱兆祺学习板的S13键
  598. switch(ucStatus)//在不同的状况下,进行不同的操作
  599. {
  600. case 0://处于待机状况
  601. break;
  602. case 1://处于正在通讯的进程
  603. break;
  604. case 2: //发送数据犯错,比方中心超时没有接纳到数据
  605. ucStatus=0; //切换回待机的状况
  606. break;
  607. }
  608. ucVoiceLock=1;//原子锁加锁,维护主函数与中止函数的同享变量uiVoiceCnt
  609. uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
  610. ucVoiceLock=0;//原子锁解锁,维护主函数与中止函数的同享变量uiVoiceCnt
  611. ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
  612. break;
  613. }
  614. }
  615. void display_drive(void)
  616. {
  617. //以下程序,假如加一些数组和移位的元素,还能够紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
  618. switch(ucDisplayDriveStep)
  619. {
  620. case 1://显现第1位
  621. ucDigShowTemp=dig_table[ucDigShow1];
  622. if(ucDigDot1==1)
  623. {
  624. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  625. }
  626. dig_hc595_drive(ucDigShowTemp,0xfe);
  627. break;
  628. case 2://显现第2位
  629. ucDigShowTemp=dig_table[ucDigShow2];
  630. if(ucDigDot2==1)
  631. {
  632. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  633. }
  634. dig_hc595_drive(ucDigShowTemp,0xfd);
  635. break;
  636. case 3://显现第3位
  637. ucDigShowTemp=dig_table[ucDigShow3];
  638. if(ucDigDot3==1)
  639. {
  640. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  641. }
  642. dig_hc595_drive(ucDigShowTemp,0xfb);
  643. break;
  644. case 4://显现第4位
  645. ucDigShowTemp=dig_table[ucDigShow4];
  646. if(ucDigDot4==1)
  647. {
  648. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  649. }
  650. dig_hc595_drive(ucDigShowTemp,0xf7);
  651. break;
  652. case 5://显现第5位
  653. ucDigShowTemp=dig_table[ucDigShow5];
  654. if(ucDigDot5==1)
  655. {
  656. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  657. }
  658. dig_hc595_drive(ucDigShowTemp,0xef);
  659. break;
  660. case 6://显现第6位
  661. ucDigShowTemp=dig_table[ucDigShow6];
  662. if(ucDigDot6==1)
  663. {
  664. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  665. }
  666. dig_hc595_drive(ucDigShowTemp,0xdf);
  667. break;
  668. case 7://显现第7位
  669. ucDigShowTemp=dig_table[ucDigShow7];
  670. if(ucDigDot7==1)
  671. {
  672. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  673. }
  674. dig_hc595_drive(ucDigShowTemp,0xbf);
  675. break;
  676. case 8://显现第8位
  677. ucDigShowTemp=dig_table[ucDigShow8];
  678. if(ucDigDot8==1)
  679. {
  680. ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
  681. }
  682. dig_hc595_drive(ucDigShowTemp,0x7f);
  683. break;
  684. }
  685. ucDisplayDriveStep++;
  686. if(ucDisplayDriveStep>8)//扫描完8个数码管后,从头从榜首个开端扫描
  687. {
  688. ucDisplayDriveStep=1;
  689. }
  690. }
  691. //数码管的74HC595驱动函数
  692. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  693. {
  694. unsigned char i;
  695. unsigned char ucTempData;
  696. dig_hc595_sh_dr=0;
  697. dig_hc595_st_dr=0;
  698. ucTempData=ucDigStatusTemp16_09;//先送高8位
  699. for(i=0;i<8;i++)
  700. {
  701. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  702. else dig_hc595_ds_dr=0;
  703. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  704. delay_short(1);
  705. dig_hc595_sh_dr=1;
  706. delay_short(1);
  707. ucTempData=ucTempData<<1;
  708. }
  709. ucTempData=ucDigStatusTemp08_01;//再先送低8位
  710. for(i=0;i<8;i++)
  711. {
  712. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  713. else dig_hc595_ds_dr=0;
  714. dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  715. delay_short(1);
  716. dig_hc595_sh_dr=1;
  717. delay_short(1);
  718. ucTempData=ucTempData<<1;
  719. }
  720. dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  721. delay_short(1);
  722. dig_hc595_st_dr=1;
  723. delay_short(1);
  724. dig_hc595_sh_dr=0; //拉低,抗干扰就增强
  725. dig_hc595_st_dr=0;
  726. dig_hc595_ds_dr=0;
  727. }
  728. //LED灯的74HC595驱动函数
  729. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  730. {
  731. unsigned char i;
  732. unsigned char ucTempData;
  733. hc595_sh_dr=0;
  734. hc595_st_dr=0;
  735. ucTempData=ucLedStatusTemp16_09;//先送高8位
  736. for(i=0;i<8;i++)
  737. {
  738. if(ucTempData>=0x80)hc595_ds_dr=1;
  739. else hc595_ds_dr=0;
  740. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  741. delay_short(1);
  742. hc595_sh_dr=1;
  743. delay_short(1);
  744. ucTempData=ucTempData<<1;
  745. }
  746. ucTempData=ucLedStatusTemp08_01;//再先送低8位
  747. for(i=0;i<8;i++)
  748. {
  749. if(ucTempData>=0x80)hc595_ds_dr=1;
  750. else hc595_ds_dr=0;
  751. hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
  752. delay_short(1);
  753. hc595_sh_dr=1;
  754. delay_short(1);
  755. ucTempData=ucTempData<<1;
  756. }
  757. hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
  758. delay_short(1);
  759. hc595_st_dr=1;
  760. delay_short(1);
  761. hc595_sh_dr=0; //拉低,抗干扰就增强
  762. hc595_st_dr=0;
  763. hc595_ds_dr=0;
  764. }
  765. void usart_receive(void) interrupt 4 //串口接纳数据中止
  766. {
  767. if(RI==1)
  768. {
  769. RI = 0;
  770. ++uiRcregTotal;
  771. if(uiRcregTotal>const_rc_size)//超越缓冲区
  772. {
  773. uiRcregTotal=const_rc_size;
  774. }
  775. ucRcregBuf[uiRcregTotal-1]=SBUF; //将串口接纳到的数据缓存到接纳缓冲区里
  776. if(ucSendCntLock==0)//原子锁判别
  777. {
  778. ucSendCntLock=1; //加锁
  779. uiSendCnt=0;//及时喂狗,虽然在守时中止那儿此变量会不断累加,可是只需串口的数据还没发送结束,那么它永久也长不大,由于每个串口接纳中止它都被清零。
  780. ucSendCntLock=0; //解锁
  781. }
  782. }
  783. else//我在其它单片机上都不必else这段代码的,可能在51单片机上多添加” TI = 0;”稳定性会更好吧。
  784. {
  785. TI = 0;//假如不是串口接纳中止,那么必定是串口发送中止,及时铲除发送中止的标志,不然一向发送中止
  786. }
  787. }
  788. void T0_time(void) interrupt 1 //守时中止
  789. {
  790. TF0=0;//铲除中止标志
  791. TR0=0; //关中止
  792. /* 注释一:
  793. * 此处多添加一个原子锁,作为中止与主函数同享数据的维护,实践上是学习了”红金龙吸味”关于原子锁的主张.
  794. */
  795. if(ucSendCntLock==0)//原子锁判别
  796. {
  797. ucSendCntLock=1; //加锁
  798. if(uiSendCnt
  799. {
  800. uiSendCnt++; //表面上这个数据不断累加,可是在串口中止里,每接纳一个字节它都会被清零,除非这个中心没有串口数据过来
  801. ucSendLock=1; //开自锁标志
  802. }
  803. ucSendCntLock=0; //解锁
  804. }
  805. if(ucVoiceLock==0) //原子锁判别
  806. {
  807. if(uiVoiceCnt!=0)
  808. {
  809. uiVoiceCnt–; //每次进入守时中止都自减1,直到等于零中止。才中止鸣叫
  810. beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
  811. }
  812. else
  813. {
  814. ; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也能够。
  815. beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
  816. }
  817. }
  818. if(ucStatus!=0) //处于非待机的状况,Led闪耀
  819. {
  820. uiLedCnt++; //Led闪耀计时器不断累加
  821. }
  822. if(ucStatus==1) //处于正在通讯的状况,
  823. {
  824. if(ucSendTimeOutLock==0)//原子锁判别
  825. {
  826. uiSendTimeOutCnt++; //超时计时器累加
  827. if(uiSendTimeOutCnt>const_send_time_out)//超时犯错
  828. {
  829. uiSendTimeOutCnt=0;
  830. ucStatus=2;//切换到犯错报警状况
  831. }
  832. }
  833. }
  834. key_scan(); //按键扫描函数
  835. display_drive();//数码管字模的驱动函数
  836. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  837. TL0=0x0b;
  838. TR0=1;//开中止
  839. }
  840. void delay_short(unsigned int uiDelayShort)
  841. {
  842. unsigned int i;
  843. for(i=0;i
  844. {
  845. ; //一个分号相当于履行一条空句子
  846. }
  847. }
  848. void delay_long(unsigned int uiDelayLong)
  849. {
  850. unsigned int i;
  851. unsigned int j;
  852. for(i=0;i
  853. {
  854. for(j=0;j<500;j++)//内嵌循环的空指令数量
  855. {
  856. ; //一个分号相当于履行一条空句子
  857. }
  858. }
  859. }
  860. void initial_myself(void)//榜首区 初始化单片机
  861. {
  862. /* 注释二:
  863. * 矩阵键盘也能够做独立按键,条件是把某一根公共输出线输出低电平,
  864. * 模仿独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
  865. * 朱兆祺51学习板的S1便是本程序中用到的一个独立按键。
  866. */
  867. key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
  868. led_dr=1;//点亮独立LED灯
  869. beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
  870. hc595_drive(0x00,0x00);//封闭一切通过别的两个74HC595驱动的LED灯
  871. TMOD=0x01;//设置守时器0为工作方法1
  872. TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
  873. TL0=0x0b;
  874. //装备串口
  875. SCON=0x50;
  876. TMOD=0X21;
  877. /* 注释三:
  878. * 为了确保串口中止接纳的数据不丢掉,有必要设置IP = 0x10,相当于把串口中止设置为最高优先级,
  879. * 这个时分,串口中止能够打断任何其他的中止服务函数完结嵌套,
  880. */
  881. IP =0x10;//把串口中止设置为最高优先级,有必要的。
  882. TH1=TL1=-(11059200L/12/32/9600);//串口波特率为9600。
  883. TR1=1;
  884. }
  885. void initial_peripheral(void) //第二区 初始化外围
  886. {
  887. ucDigDot8=0; //小数点悉数不显现
  888. ucDigDot7=0;
  889. ucDigDot6=0;
  890. ucDigDot5=0;
  891. ucDigDot4=0;
  892. ucDigDot3=0;
  893. ucDigDot2=0;
  894. ucDigDot1=0;
  895. EA=1; //开总中止
  896. ES=1; //答应串口中止
  897. ET0=1; //答应守时中止
  898. TR0=1; //发动守时中止
  899. }

总结陈词:
这节具体讲了从机收发端的程序结构,而主机端的程序则用电脑的串口帮手来模仿。实践上,主机端的程序也有许多内容,它包含顺次发送每一串数据,依据回来的应对来决议是否需求重发数据,重发三次假如没反应则进行报错,以及超时没接纳到数据等等内容。主机收发端的程序结构是什么样的?欲知概况,请听下回分化—–主机的串口收发归纳程序结构

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/yingyong/iot/262080.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部