原创 一个让人头痛而又有趣的软件bug

2020-6-11 22:56 13204 89 14 分类: MCU/ 嵌入式 文集: C语言
这是我前几天遇到的bug,RS485协议程序我已经做过无数次了,有无数个成功的先例。当开始做这个小程序的时候,我认为对我而言这只是小菜一碟,我挥挥手就可以把它搞定。
但是事情出乎我的意料,程序工作不正常!
先把有bug的代码贴出来:(代码很长,如果看起来眼睛脑壳痛,请直接跳过源代码,看后面的文字说明就好了,贴出代码是为了让人能感受到debug有时候是一件让人多么痛苦的事情)
  1. void RS485_Poll(void)
  2. {
  3. uint8_t byte;
  4. static uint8_t index = 0,len = 0;
  5. static uint16_t check;
  6. uint8_t i;
  7. if(!BSP_RxFifoOut(&byte))
  8. {
  9. if(s_u8BusIdleFlag == 1)
  10. {
  11. if((s_u8RetryCount > 0) && (s_u16DelaySendPack_Timer_1ms == 0))
  12. {
  13. s_u8CollisionDetecting = 1;
  14. s_u8DataVerifyIndex = 0;
  15. len = s_u8OutPack[PROTOCOL_STATE_LEN] + 2;
  16. for(i = 0;i < len;i++)
  17. {
  18. BSP_USART1Tx(s_u8OutPack[i]);
  19. }
  20. s_u8BusIdleFlag = 0;
  21. s_u8Tick_64us = 32;
  22. }
  23. }
  24. }
  25. else
  26. {
  27. s_u8BusIdleFlag = 0;
  28. s_u8Tick_64us = 32;
  29. if(s_u8CollisionDetecting == 1)
  30. {
  31. if(byte == s_u8OutPack[s_u8DataVerifyIndex++])
  32. {
  33. if(s_u8DataVerifyIndex == s_u8OutPack[PROTOCOL_STATE_LEN] + 2)
  34. {
  35. s_u8CollisionDetecting = 0;
  36. s_u8RetryCount = 0;
  37. }
  38. }
  39. else
  40. {
  41. s_u8CollisionDetecting = 0;
  42. if(s_u8RetryCount > 0)
  43. {
  44. s_u8RetryCount--;
  45. s_u16DelaySendPack_Timer_1ms = GetPeseudoRandom();
  46. }
  47. }
  48. }
  49. else
  50. {
  51. switch(s_ProtocolState)
  52. {
  53. case PROTOCOL_STATE_HEAD0:
  54. if(byte == 0xAA)
  55. {
  56. s_ProtocolState = PROTOCOL_STATE_HEAD1;
  57. s_u8Reset_Timer_1ms = 0;
  58. index = 0;
  59. s_u8InPack[index++] = byte;
  60. }
  61. break;
  62. case PROTOCOL_STATE_HEAD1:
  63. if(byte == 0xAA)
  64. {
  65. s_ProtocolState = PROTOCOL_STATE_LEN;
  66. s_u8InPack[index++] = byte;
  67. }
  68. else
  69. {
  70. s_ProtocolState = PROTOCOL_STATE_HEAD0;
  71. }
  72. break;
  73. case PROTOCOL_STATE_LEN:
  74. s_u8InPack[index++] = byte;
  75. len = byte;
  76. if((len <= (MAX_PACK_LEN - 2))
  77. && (len >= (MIN_PACK_LEN - 2)))
  78. {
  79. s_ProtocolState = PROTOCOL_STATE_SRC_NET;
  80. }
  81. else
  82. {
  83. s_ProtocolState = PROTOCOL_STATE_HEAD0;
  84. }
  85. break;
  86. case PROTOCOL_STATE_SRC_NET:
  87. s_u8InPack[index++] = byte;
  88. s_u8SrcNet = byte;
  89. s_ProtocolState = PROTOCOL_STATE_SRC_DEV;
  90. break;
  91. case PROTOCOL_STATE_SRC_DEV:
  92. s_u8InPack[index++] = byte;
  93. s_u8SrcDev = byte;
  94. s_ProtocolState = PROTOCOL_STATE_SRC_TYPE_H;
  95. break;
  96. case PROTOCOL_STATE_SRC_TYPE_H:
  97. s_u8InPack[index++] = byte;
  98. s_u16SrcType = ((uint16_t)byte)<<8;
  99. s_ProtocolState = PROTOCOL_STATE_SRC_TYPE_L;
  100. break;
  101. case PROTOCOL_STATE_SRC_TYPE_L:
  102. s_u8InPack[index++] = byte;
  103. s_u16SrcType |= byte;
  104. s_ProtocolState = PROTOCOL_STATE_CMD_H;
  105. break;
  106. case PROTOCOL_STATE_CMD_H:
  107. s_u8InPack[index++] = byte;
  108. s_u16Command = ((uint16_t)byte)<<8;
  109. s_ProtocolState = PROTOCOL_STATE_CMD_L;
  110. break;
  111. case PROTOCOL_STATE_CMD_L:
  112. s_u8InPack[index++] |= byte;
  113. s_u16Command |= byte;
  114. s_ProtocolState = PROTOCOL_STATE_DST_NET;
  115. break;
  116. case PROTOCOL_STATE_DST_NET:
  117. s_u8InPack[index++] = byte;
  118. s_u8DstNet = byte;
  119. s_ProtocolState = PROTOCOL_STATE_DST_DEV;
  120. break;
  121. case PROTOCOL_STATE_DST_DEV:
  122. s_u8InPack[index++] = byte;
  123. s_u8DstDev = byte;
  124. if(len > MIN_PACK_LEN)
  125. {
  126. s_ProtocolState = PROTOCOL_STATE_DAT;
  127. }
  128. else
  129. {
  130. s_ProtocolState = PROTOCOL_STATE_CRC_H;
  131. }
  132. break;
  133. case PROTOCOL_STATE_DAT:
  134. s_u8InPack[index++] = byte;
  135. if(index >= len)
  136. {
  137. s_ProtocolState = PROTOCOL_STATE_CRC_H;
  138. }
  139. break;
  140. case PROTOCOL_STATE_CRC_H:
  141. s_u8InPack[index++] = byte;
  142. check = ((uint16_t)byte)<<8;
  143. s_ProtocolState = PROTOCOL_STATE_CRC_L;
  144. break;
  145. case PROTOCOL_STATE_CRC_L:
  146. s_u8InPack[index++] = byte;
  147. check |= byte;
  148. if(check == CalcCRC16(s_u8InPack + 2,len - 2))
  149. {
  150. CommandHandle();
  151. }
  152. s_ProtocolState = PROTOCOL_STATE_HEAD0;
  153. break;
  154. default:
  155. break;
  156. }
  157. }
  158. }
  159. }
  160. static void CommandHandle(void)
  161. {
  162. if(((s_u8DstNet == s_u8LocalNet) && (s_u8DstDev == s_u8LocalDev))
  163. || ((s_u8DstNet == 0xff) && (s_u8DstDev == 0xff)))
  164. {
  165. if(s_u16Command == 0xE3E0)
  166. {
  167. if((((s_u8InPack[PROTOCOL_STATE_DAT + 2] == 0) || (s_u8InPack[PROTOCOL_STATE_LEN] == 13))
  168. &&(s_u8InPack[PROTOCOL_STATE_DAT] < 0x11))
  169. || (s_u8InPack[PROTOCOL_STATE_DAT + 2] == 1))
  170. {
  171. if(s_u8InPack[PROTOCOL_STATE_DAT + 1] == 0)
  172. {
  173. Message_FifoIn(MSG_SERIAL_STOP);
  174. }
  175. else if(s_u8InPack[PROTOCOL_STATE_DAT + 1] == 1)
  176. {
  177. Message_FifoIn(MSG_SERIAL_OPEN);
  178. }
  179. else if(s_u8InPack[PROTOCOL_STATE_DAT + 1] == 2)
  180. {
  181. Message_FifoIn(MSG_SERIAL_CLOSE);
  182. }
  183. }
  184. else if((((s_u8InPack[PROTOCOL_STATE_DAT + 2] == 0) || (s_u8InPack[PROTOCOL_STATE_LEN] == 13))
  185. &&(s_u8InPack[PROTOCOL_STATE_DAT] >= 0x11))
  186. || (s_u8InPack[PROTOCOL_STATE_DAT + 2] == 2))
  187. {
  188. if(s_u8InPack[PROTOCOL_STATE_DAT + 1] <= 0)
  189. {
  190. s_u8TargetPercent = s_u8InPack[PROTOCOL_STATE_DAT + 1];
  191. Message_FifoIn(MSG_SERIAL_TP);
  192. }
  193. }
  194. if(!((s_u8LocalNet == 0xff) && (s_u8LocalDev == 0xff)))
  195. {
  196. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xE3;
  197. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xE1; // 注释掉此行后,可以连续接收命令
  198. BuildInPack();
  199. SendInPack();
  200. }
  201. }
  202. else if(s_u16Command == 0xE3E2)
  203. {
  204. if((((s_u8InPack[PROTOCOL_STATE_DAT + 2] == 0) || (s_u8InPack[PROTOCOL_STATE_LEN] == 13))
  205. &&(s_u8InPack[PROTOCOL_STATE_DAT] < 0x11))
  206. || (s_u8InPack[PROTOCOL_STATE_DAT + 2] == 1))
  207. {
  208. s_u8InPack[PROTOCOL_STATE_DAT + 1] = GetCurtainControlState();
  209. }
  210. else if((((s_u8InPack[PROTOCOL_STATE_DAT + 2] == 0) || (s_u8InPack[PROTOCOL_STATE_LEN] == 13))
  211. &&(s_u8InPack[PROTOCOL_STATE_DAT] >= 0x11))
  212. || (s_u8InPack[PROTOCOL_STATE_DAT + 2] == 2))
  213. {
  214. s_u8InPack[PROTOCOL_STATE_DAT + 1] = Curtain_GetPercent();
  215. }
  216. if(!((s_u8LocalNet == 0xff) && (s_u8LocalDev == 0xff)))
  217. {
  218. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xE3;
  219. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xE3;
  220. BuildInPack();
  221. SendInPack();
  222. }
  223. }
  224. else if(s_u16Command == 0xF001)
  225. {
  226. if(CurtainController_IsProgState())
  227. {
  228. uint8_t i;
  229. for(i = 0;i < 8;i++)
  230. {
  231. s_u8Mac[i] = s_u8InPack[PROTOCOL_STATE_DAT + i];
  232. eeprom_write_byte(EEPROM_HDL_MAC + i,s_u8Mac[i]);
  233. }
  234. s_u8InPack[PROTOCOL_STATE_DAT] = 0xF8;
  235. Message_FifoIn(MSG_SERIAL_LEAVE_LEARN);
  236. }
  237. else
  238. {
  239. s_u8InPack[PROTOCOL_STATE_DAT] = 0xF5;
  240. }
  241. s_u8InPack[PROTOCOL_STATE_LEN] = 12;
  242. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xF0;
  243. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x02;
  244. BuildInPack();
  245. SendInPack();
  246. }
  247. else if(s_u16Command == 0xF003)
  248. {
  249. uint8_t i;
  250. for(i = 0;i < 8;i++)
  251. {
  252. s_u8InPack[PROTOCOL_STATE_DAT + i] = s_u8Mac[i];
  253. }
  254. s_u8InPack[PROTOCOL_STATE_DAT + i++] = s_u8LocalNet;
  255. s_u8InPack[PROTOCOL_STATE_DAT + i++] = s_u8LocalDev;
  256. s_u8InPack[PROTOCOL_STATE_DAT + i++] = 0x02;
  257. s_u8InPack[PROTOCOL_STATE_DAT + i++] = 0xC1;
  258. s_u8InPack[PROTOCOL_STATE_LEN] = 23;
  259. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xF0;
  260. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x04;
  261. BuildInPack();
  262. SendInPack();
  263. }
  264. else if(s_u16Command == 0x000E)
  265. {
  266. uint8_t i;
  267. for(i = 0;i < 20;i++)
  268. {
  269. s_u8InPack[PROTOCOL_STATE_DAT + i] = s_u8Remark[i];
  270. }
  271. s_u8InPack[PROTOCOL_STATE_LEN] = 31;
  272. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0x00;
  273. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x0F;
  274. BuildInPack();
  275. SendInPack();
  276. }
  277. else if(s_u16Command == 0x0010)
  278. {
  279. if((s_u8LocalNet == s_u8InPack[PROTOCOL_STATE_DST_NET]) && (s_u8LocalDev == s_u8InPack[PROTOCOL_STATE_DST_DEV]))
  280. {
  281. uint8_t i;
  282. for(i = 0;i < 20;i++)
  283. {
  284. s_u8Remark[i] = s_u8InPack[PROTOCOL_STATE_DAT + i];
  285. eeprom_write_byte(EEPROM_HDL_REMARK + i,s_u8Mac[i]);
  286. }
  287. s_u8InPack[PROTOCOL_STATE_LEN] = 12;
  288. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0x00;
  289. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x11;
  290. BuildInPack();
  291. SendInPack();
  292. }
  293. }
  294. else if(s_u16Command == 0xE5F5)
  295. {
  296. if(CurtainController_IsProgState())
  297. {
  298. s_u8InPack[PROTOCOL_STATE_DAT + 0] = s_u8LocalNet;
  299. s_u8InPack[PROTOCOL_STATE_DAT + 1] = s_u8LocalDev;
  300. s_u8InPack[PROTOCOL_STATE_LEN] = 13;
  301. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xE5;
  302. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xF6;
  303. BuildInPack();
  304. SendInPack();
  305. Message_FifoIn(MSG_SERIAL_LEAVE_LEARN);
  306. }
  307. }
  308. else if(s_u16Command == 0xE5F7)
  309. {
  310. if(CurtainController_IsProgState())
  311. {
  312. s_u8LocalNet = s_u8InPack[PROTOCOL_STATE_DAT + 0];
  313. s_u8LocalDev = s_u8InPack[PROTOCOL_STATE_DAT + 1];
  314. eeprom_write_byte(EEPROM_HDL_NET,s_u8LocalNet);
  315. eeprom_write_byte(EEPROM_HDL_DEV,s_u8LocalDev);
  316. s_u8InPack[PROTOCOL_STATE_LEN] = 13;
  317. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xE5;
  318. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xF8;
  319. BuildInPack();
  320. SendInPack();
  321. Message_FifoIn(MSG_SERIAL_LEAVE_LEARN);
  322. }
  323. }
  324. else if(s_u16Command == 0xE8F8)
  325. {
  326. if(CurtainController_IsProgState())
  327. {
  328. /* s_u8InPack[PROTOCOL_STATE_DAT] = 0xF8;
  329. s_u8InPack[PROTOCOL_STATE_LEN] = 12;
  330. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xE8;
  331. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xF9;
  332. BuildInPack();
  333. SendInPack(); */
  334. eeprom_write_byte(EEPROM_PROTOCOL_TYPE,PROTOCOL_TYPE_RAEX_485);
  335. SerialProtocol_init();
  336. Message_FifoIn(MSG_SERIAL_LEAVE_LEARN);
  337. }
  338. }
  339. else if(s_u16Command == 0xFE05)
  340. {
  341. uint8_t i;
  342. uint8_t flag = 1;
  343. for(i = 0;i < 8;i++)
  344. {
  345. if(s_u8InPack[PROTOCOL_STATE_DAT + i] != s_u8Mac[i])
  346. {
  347. flag = 0;
  348. }
  349. }
  350. if(flag == 1)
  351. {
  352. s_u8InPack[PROTOCOL_STATE_LEN] = 12;
  353. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xFE;
  354. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x06;
  355. s_u8InPack[PROTOCOL_STATE_DAT] = 0xF8;
  356. BuildInPack();
  357. SendInPack();
  358. }
  359. }
  360. else if(s_u16Command == 0xF005)
  361. {
  362. uint8_t i;
  363. uint8_t flag = 1;
  364. for(i = 0;i < 8;i++)
  365. {
  366. if(s_u8InPack[PROTOCOL_STATE_DAT + i] != s_u8Mac[i])
  367. {
  368. flag = 0;
  369. }
  370. }
  371. if(flag == 1)
  372. {
  373. s_u8LocalNet = s_u8InPack[PROTOCOL_STATE_DAT + 8];
  374. s_u8LocalDev = s_u8InPack[PROTOCOL_STATE_DAT + 9];
  375. eeprom_write_byte(EEPROM_HDL_NET,s_u8LocalNet);
  376. eeprom_write_byte(EEPROM_HDL_DEV,s_u8LocalDev);
  377. s_u8InPack[PROTOCOL_STATE_DAT] = 0xF8;
  378. s_u8InPack[PROTOCOL_STATE_LEN] = 12;
  379. s_u8InPack[PROTOCOL_STATE_CMD_H] = 0xF0;
  380. s_u8InPack[PROTOCOL_STATE_CMD_L] = 0x06;
  381. BuildInPack();
  382. SendInPack();
  383. }
  384. }
  385. }
  386. }

第一个函数void RS485_Poll(void)是用来解析数据包的,第二个函数static void CommandHandle(void)是用来处理数据包里面的命令的。
当我用串口工具向mcu发送控制命令的时候,总是第一个命令可以正常响应,随后的命令都不能正常响应。反复检查源代码,没有发现错误。这种代码我编写过无数此了,我想即使首次运行能不能通过,我也能极快找出bug,半小时之内一定能拿下这段代码的调试。好久没有遇到过让人头痛的bug了,我以为这个bug三五分钟也可以搞定。
我带着满满的信心,查看源代码。第一遍,看不出问题原因出自哪里。第二遍,更仔细的看,还是看不出来。第三、第四、第五......还是看不出来。
怎么办呢?一下子有点束手无策,我应该怎么办才能定位bug在哪里?
是不是数据包接收出错了?最有可能出错的就是crc校验吧?带着这个疑问,我注释掉void RS485_Poll(void)函数中对CommandHandle()函数的调用,并且在CommandHandle()函数原来的地方插入BSP_ToggleLed()。发现数据包接收没有任何错误。
到这里,又开始觉得束手无策了!
经过许久的煎熬后,不知道什么原因,我认为可以试试把应答数据包的代码注释掉再看看有没有什么线索。想到了就干,立马注释掉tatic void CommandHandle(void)中发送应答数据包对应的代码,再测试发现每次发送命令都可以正常执行了!如获至宝!我想应该离成功不远了,心情瞬间愉快了许多。
再逐行减少注释代码的函数,最后发现问题“位于”下面一行代码:
s_u8InPack[PROTOCOL_STATE_CMD_L] = 0xE1; // 注释掉此行后,可以连续接收命令
根据以往的调试经验,一旦定位了错误在哪一行,改正这一行里面的错误,debug就通过。
可是翻来覆去的看这一行代码,却看不出一丁点的问题来!
调试再次进入胶着状态,漫长的调试让我的大脑极度疲劳,我感觉思维已经停止了,头脑一片空白,就这样束手无策了N久,才想到试试把接收到的数据包原封不动的返回到串口助手,看看能不能发现这行代码是如何影响程序的正确执行的。
想到就干,void RS485_Poll(void)函数中对调用函数CommandHandle()之后插入如下返回接收到的数据包的代码:
  1. for(i = 0;i < pack_len;i++)
  2. {
  3. Bsp_uart_send_byte(s_u8InPack[i]);
  4. }
发现第一次返回的数据包和串口助手发出的数据包相同,随后返回的数据包的s_u8InPack[PROTOCOL_STATE_CMD_L] 错误。
再次查看,void RS485_Poll(void)函数中写入s_u8InPack[PROTOCOL_STATE_CMD_L]那一行语句:


  1. case PROTOCOL_STATE_CMD_L:
  2. s_u8InPack[index++] |= byte;
  3. s_u16Command |= byte;
  4. s_ProtocolState = PROTOCOL_STATE_DST_NET;
  5. break;

发现代码录入出错了!正确的代码应该是“s_u8InPack[index++] = byte;”但错写成了“s_u8InPack[index++] |= byte;”至此终于找到了问题的“根源”!
|= 与=之间的差别再加上调试过程中一些凑巧,产生了bug位于其他代码段的假象,从而导致了一个初看起来让人无比困惑的bug!为了找出这个bug,我从中午11点开始,午休也不休息,直忙到下午两点。
这真的是挺折磨人的,要不是对编程有浓厚的兴趣,我想肯定无法正确找到原因。这个过程虽然折磨人,但是对困难的挑战,和挑战成功,却让人从中找到莫名的满足感。回顾整个过程,又觉得饶有趣味。
其中有两点很值得深思:如何才能避免类似的录入错误,如何才能更快速的定位类似的错误?

作者: Qeecoda, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-1099225.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

PARTNER CONTENT

文章评论16条评论)

登录后参与讨论

海州.王 2020-12-31 09:30

很多if()else(),逻辑上面看得人头疼。

Qeecoda 2020-7-6 08:46

追忆流年寻梦少年: 这种问题是比较烦;
但对于解决问题、发现bug,过程可以优化,一般串口调试的第一步就是原发原收,或者是自发自收,看发送、接收情况,这样子就可以快速定位收端 ...
我也是认为要遵循类似的思路,如果推理分析和凭经验都不能快速定位问题,就要把程序分拆成多个基本环节的组合,充分验证每个环节都是正确的,这样可确保“万无一失“?

追忆流年寻梦少年 2020-6-30 13:34

这种问题是比较烦;
但对于解决问题、发现bug,过程可以优化,一般串口调试的第一步就是原发原收,或者是自发自收,看发送、接收情况,这样子就可以快速定位收端代码异常;然后根据收端的数据,再进一步设计发送0000,1111,1234等,根据接收数据异常分析,再进一步定位代码。
当然,这一般都是事后的总结,发现前面浪费了时间,可以优化的。

Qeecoda 2020-6-20 13:40

allen_zhan: 很难有人会犯这种&quot;录入错误&quot;吧.
这种代码移植, 工程师拿手的是 copy - paste.
很多时候,单片机代码并没有一个‘原板’可供移植。

Qeecoda 2020-6-20 13:39

allen_zhan: 很难有人会犯这种&quot;录入错误&quot;吧.
这种代码移植, 工程师拿手的是 copy - paste.
即使已经做过的代码,有时候也难免手痒会重构代码的。

allen_zhan 2020-6-19 17:10

很难有人会犯这种"录入错误"吧.
这种代码移植, 工程师拿手的是 copy - paste.

Qeecoda 2020-6-16 21:04

majunling606: 这个,折磨人了
痛并快乐着吧

Qeecoda 2020-6-16 21:02

southcreek: 有一条原则,计算机总是对的。如果出错了,一定是人为因素。
但如果计算机的设计原理使大多数的人总是出错,那么计算机的设计本身也需要改进。

Qeecoda 2020-6-16 21:00

southcreek: 有一条原则,计算机总是对的。如果出错了,一定是人为因素。
绝大多数情况是这样!

用户1515636 2020-6-15 19:35

这个,折磨人了
相关推荐阅读
Qeecoda 2024-09-13 14:28
秋夜偶书
      秋·月夜  玻色子  2023-9-13旧事依稀又月明,浮生若梦意难平。霜风十里江南夜,秋月无边故人情。...
Qeecoda 2023-07-08 08:25
电池快充标志引发的思考
        随着氮化镓技术的普及,快充功能特别是手机锂电池快充功能,在我们的生活中日益普及。快速充电显著提升了充电速度,给我们的生活带来巨大的便捷性。快充的...
Qeecoda 2022-05-18 16:02
MCP2515驱动代码
[code]/* * @file mcp2515.h * @author TanQi * @version V1.0.0 * @date 2022-1-20 * @brief He...
Qeecoda 2021-06-19 17:37
实用可靠的干接点消抖滤波算法
[code]/* * * 干接点常用于实现各种电气设备开关停控制的弱电接口,两个触点可以组合出无动作、开、停和关4种状态。 * 干接点信号可以由手控面板发出,也可以由中控设备发出。 * 在手动应用中常...
Qeecoda 2020-05-17 16:02
电子产品常见故障现象、原因分析及改进措施(一)
1. 故障现象:通电无反应。原因分析:(1)接插件接不良;(2)470压敏电阻击穿;(3)开关电源的高压整流桥击穿;(4)开关电源的高压整理滤波电容损坏;(5)开关电源的开关管损坏;(6)开关电源的高...
EE直播间
更多
我要评论
16
89
关闭 站长推荐上一条 /3 下一条