完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
问题描述
先描述一下整个过程吧:最近在做的一个项目,用的华大的单片机,一款国产的MCU,主要做的是低功耗方面的,外加NB-IoT;当时修改过代码之后下载到设备上面,发现设备休眠之后就再也无法唤醒了,直到看门狗复位,而且设备不进入休眠的话就不会有问题。于是我拿上一个版本的代码跟当前的代码进行比较,对比之后发现只是修改了CRC校验的长度,如下图(只是把 uint8_t 的 DataLen,改成 uint16_t ),当时实在想不到把 uint8_t 的数据类型改成 uint16_t 能发生什么错误,一度以为是某些寄存器被未知的东西修改掉了,并且一开始肯定是进入休眠后的问题; uint8_t modebus_crc_check (uint8_t *puchMsg,uint16_t DataLen) { uint16_t calculate_crc, crc_in_msg; crc_in_msg = *(puchMsg + DataLen - 2 ) | (*(puchMsg + DataLen -1 ) 《《8 ); calculate_crc = modebus_crc_calculate(puchMsg, DataLen-2); if(calculate_crc == crc_in_msg) return 1; else return 0; } 查找问题的过程 华大的这款单片机给出了休眠的模板代码,并且在休眠的模式下是不能进行调试的,所以这给查找问题带来了困难;但山重水复疑无路,还有另一种浅睡眠模式,这个模式支持调试,并且其运行方式跟休眠模式的方式一样,所以我就稍微修改了代码,让其运行在浅睡眠模式。 最终,让我找到了问题所在,接下来根据代码和描述讲解找到问题的整个过程: 1.下图是单片机进入休眠前的过程,进入休眠前需要把所有的管脚全部配置成输出低电平,这样做是为了让管脚在休眠模式下不耗电 M0P_GPIO-》PAADS = 0x0000; M0P_GPIO-》PBADS = 0x0000; M0P_GPIO-》PCADS = 0xC000; M0P_GPIO-》PDADS = 0x0000; M0P_GPIO-》PADIR = 0x0000; M0P_GPIO-》PBDIR = 0x0000; M0P_GPIO-》PCDIR = 0x0000; M0P_GPIO-》PDDIR = 0x0000; M0P_GPIO-》PAOUT = 0x0000; M0P_GPIO-》PBOUT = 0x0000; M0P_GPIO-》PCOUT = 0x0000; M0P_GPIO-》PDOUT = 0x0000; 2.紧接着上面的内容,把需要在休眠模式中用到的管脚再给初始化,如下图,配置串口0的接收管脚为外部中断管脚,在休眠模式下如果串口有收到数据,就会被唤醒, void DeepSleepModePeripheralSet(void) { Uart0RX_GPIOExit(); } 3.就是在第二步中出现了问题,串口在进入休眠之前把接收管脚配置成外部中断触发,此时因为刚被配置,产生了抖动,使MCU以为接收到了数据,然后就进入到接收处理函数中,如下图,该抖动每次都让MCU误以为收到了一个字节的数据,所以 len 每次都是1,这个暂时我也搞不清楚怎么防止抖动;好戏来了,NB_Data_RevPro 就在这个函数里面发生了一些好玩的事情,不过现在先关注一下NB_Data_RevPro这个函数传入的前两个参数 :tempBuf 和 len(就是这两个参数导致了后边发生的错误),tempBuf是一个有500字节容量的数组,len是16位的。 void local_communication_proc(void) { uint8_t tempBuf[500] = {0}; uint16_t len = 0; len = Uart0_RecvData(tempBuf); if(len != 0) { NB_Data_RevPro(tempBuf,len,UART_RECV); } } 4.上一步中说到抖动产生的len=1,传入下图这个函数,modebus_crc_check(cmd+2,data_len-4) 记住,该函数中 data_len-4,这个操作,上一步中传入的len=1,即data_len-4 = 1-4,按理说应该是-3,但是因为是无符号的数据类型,所以-3 = FD(如果 modebus_crc_check 函数的DataLen是uint8_t的话),真正的问题发生在 modebus_crc_check 这个函数中 void NB_Data_RevPro(uint8_t *cmd,uint16_t data_len,uint8_t recv_type) { nb_frame_t out_frame; if(modebus_crc_check(cmd+2,data_len-4)) { if(NB_SUCCESS == bui_nb_frame(cmd,data_len,&out_frame)) { if(!check_dstnation_addr(&out_frame)) { return; } NB_frame_data_proc(&out_frame,recv_type); } } } 5.根据下图中的代码段来分析: uint8_t modebus_crc_check (uint8_t *puchMsg,uint16_t DataLen) { uint16_t calculate_crc, crc_in_msg; crc_in_msg = *(puchMsg + DataLen - 2 ) | (*(puchMsg + DataLen -1 ) 《《8 ); calculate_crc = modebus_crc_calculate(puchMsg, DataLen-2); if(calculate_crc == crc_in_msg) return 1; else return 0; } uint8_t *puchMsg数组的首地址指针,指向的是第3步中的tempBuf,uint16_t DataLen,当时就改了这个出现了错误; 如果是uint16_t DataLen,第4步中的-3 = FFFD;如果是uint8_t DataLen,第4步中的-3 = FD;结合tempBuf的长度,观察一下两者的区别:FFFD的10进制是65533,大于500,FD的10进制是253,小于500。所以在计算 crc_in_msg = *(puchMsg + DataLen - 2 ) | (*(puchMsg + DataLen -1 ) 《《8 ); 中,puchMsg + FFFD超出了数组的范围,造成内存溢出,导致进入错误死循环,而puchMsg + FD其实也是错误的,只不过FD=253,无法超出500的范围,所以没有造成错误。这也解释了 uint16_t 和 uint8_t 之间的修改导致死机重启的原因; 解决方案 以上的问题还是自己写代码不够严谨造成的,第4步中的 modebus_crc_check(cmd+2,data_len-4) 之前就应该判断data_len的大小的,如下图的代码段就是解决方案: void NB_Data_RevPro(uint8_t *cmd,uint16_t data_len,uint8_t recv_type) { nb_frame_t out_frame; if(data_len 《= 6) return; if(modebus_crc_check(cmd+2,data_len-4)) { if(NB_SUCCESS == bui_nb_frame(cmd,data_len,&out_frame)) { if(!check_dstnation_addr(&out_frame)) { return; } NB_frame_data_proc(&out_frame,recv_type); } } } 对照着第4步中的代码,看出来了吗,就增加了一句代码: if(data_len 《= 6) return; 就把问题解决了。 总结之后,还是自己的不严谨造成了,以后写代码的第一步一定要先判断函数输入参数的合法性,其次才是对输入参数的处理。。。 哈哈,好了,失败是成功之母,一帆风顺其实也不好,经验都是在一次次的查找错误中增长的。记录学习中的琐事,大家共勉! |
|
|
|
只有小组成员才能发言,加入小组>>
2518 浏览 0 评论
1098浏览 2评论
710浏览 1评论
460浏览 0评论
201浏览 0评论
346浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 07:10 , Processed in 1.237108 second(s), Total 51, Slave 42 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号