当前位置: 首页 > news >正文

嵌入式软件代码开发或者代码优化的一些方法(持续更新)

一、消抖

这段代码为例,消抖前判断一次,消抖后判断一次,Delay之后记得重新读取数据。

uint8_t servo_change = HAL_GPIO_ReadPin(GPIOE,trash_servo); uint8_t trash_change = HAL_GPIO_ReadPin(GPIOE,monitor1); while (1) { uint8_t new_servo_state = HAL_GPIO_ReadPin(GPIOE,trash_servo); uint8_t new_trash_state = HAL_GPIO_ReadPin(GPIOE,monitor1); if(trash_change!=new_trash_state) { HAL_Delay(10); new_trash_state = HAL_GPIO_ReadPin(GPIOE,monitor1); if(trash_change!=new_trash_state) { if(new_trash_state==GPIO_PIN_RESET) //monitor1 catch the sign of full { HAL_GPIO_WritePin(GPIOE,LED_Warning,GPIO_PIN_RESET); //light up the warning LED printf("FULL LED_ON\r\n"); } else if(new_trash_state==GPIO_PIN_SET) //normal situation { HAL_GPIO_WritePin(GPIOE,LED_Warning,GPIO_PIN_SET); printf("nomal LED_OFF\r\n"); } } trash_change=new_trash_state; } if(servo_change!=new_servo_state) { HAL_Delay(50); new_servo_state = HAL_GPIO_ReadPin(GPIOE,trash_servo); if(servo_change!=new_servo_state) //���� { if(new_servo_state==GPIO_PIN_RESET) { Servo_Set_angle(angle_left); printf("left!\r\n"); HAL_Delay(2000); servo_stop(); } else if(new_servo_state==GPIO_PIN_SET) { Servo_Set_angle(angle_right); printf("right!\r\n"); HAL_Delay(2000); servo_stop(); } servo_change=new_servo_state;//����״̬ } } }

二、初级状态机:防止连续转换导致崩溃的技巧

也是以上面的代码为例,合理利用2个变量来体现状态转换的时机。保证代码不会因为频繁转换状态,控制器件反复转换状态,导致器件崩溃或者发热,损坏。

上面的代码就利用 servo_change和new_servo_state 这2个变量来读取舵机状态转换的瞬间,只读取状态变化的瞬间,而不是一味的识别高电平或者低电平来控制舵机,有效防止舵机崩溃。

三、状态机

初级状态机和状态机的共同点都是读取变化的瞬间,而不是读取数据,前者更适用于状态只有2种的时候,后者适合需要转换多种状态的时候

一般是利用枚举还有switch结构组成。枚举用于列举各种状态,switch用于读取各种状态下应该完成什么事。

例如下面的例子

定义一个枚举,以及一个枚举类型的变量

typedef enum { LED_OFF, // 熄灭 LED_ON, // 常亮 LED_BLINK_SLOW, // 慢闪 LED_BLINK_FAST // 快闪 } LED_State; LED_State current_state = LED_OFF;

结合switch结构实现按一下转换一次状态的功能

while (1) { // 按键检测(状态变化才切换) if (key_press_flag) { key_press_flag = 0; // 状态切换:按一下变一个模式 switch (current_state) { case LED_OFF: current_state = LED_ON; break; case LED_ON: current_state = LED_BLINK_SLOW; break; case LED_BLINK_SLOW: current_state = LED_BLINK_FAST; break; case LED_BLINK_FAST: current_state = LED_OFF; break; } } // 根据当前状态做动作 switch (current_state) { case LED_OFF: HAL_GPIO_WritePin(GPIOx, GPIO_PIN_0, GPIO_PIN_RESET); break; case LED_ON: HAL_GPIO_WritePin(GPIOx, GPIO_PIN_0, GPIO_PIN_SET); break; case LED_BLINK_SLOW: HAL_GPIO_TogglePin(GPIOx, GPIO_PIN_0); HAL_Delay(500); break; case LED_BLINK_FAST: HAL_GPIO_TogglePin(GPIOx, GPIO_PIN_0); HAL_Delay(100); break; } }

四、阅读数据手册的心得

2026.7.11,大二小学期期间

最近是在搞LVGL+CAN+IAP的那个项目,虽然自己手搓出来没看教程,但是其实中间不少依赖AI,最大的问题就是遇到自己不知道怎么解决的问题时,不清楚从什么地方下手,最近通过AI帮我排查错误时,学到一招,就是依靠硬件寄存器说话!

当然,排查代码错误,首先还是得从正常的思路去排查,比如说,我做的仪表盘的项目,发现的问题是2个CAN收发器不通讯,最好最简单的办法就是切半调试,缩小问题的范围,找到出问题的地方。大家都经常这么调试,但是总会遇到一些奇奇怪怪的问题,于是就靠数据手册里的状态寄存器来排查问题了。

下面是AI告诉我的排查方法,我自己复盘了一下,当时我遇到的问题是CAN不通,后面AI告诉我,让我不要猜哪里出问题,先测试c8t6有没有问题,让我打开回环模式,如果CAN的回环模式能通,说明问题不在c8t6的软件代码层面,如果CAN的回环模式不通,说明大概率是c8t6这边出了问题,或者是硬件的问题了。

然后就是读取CAN的主状态寄存器了,AI加了中间的读取状态寄存器的代码,我查了数据手册,发现读取的是发送状态寄存器(CAN_TSR)TXKO0这一位,是为了看看邮箱0的数据到底有没有发送出去。还读取了错误状态寄存器(ESR)的LEC位,为的是看看CAN通信过程中到底是哪一步出了问题。

终于,在这这几行代码的调试帮助下,我发现问题了,打印的日志显示,错误的地方是在CAN 控制器尝试发送隐性位(高电平/逻辑1)时,监控到总线上的电平却是显性(低电平/逻辑0)。总线上有东西在强行把电平拉低,说明总线电气特性异常。然后AI就让我测试回环模式,看看是不是CAN控制器的问题,结果发现回环模式能正常进行,则大概率是CAN收发器的问题了。AI就让我检测引脚有没有接对,让我检查供电是不是5V,结果最后发现,居然是c8t6的板子的CAN收发器供电不足5V导致的一系列问题!

uint32_t Can_Send_Msg(uint32_t id,uint32_t len,uint8_t *buf) { CAN_TxHeaderTypeDef TxMessage; uint32_t Tx_Mail = CAN_TX_MAILBOX0; TxMessage.StdId = id; TxMessage.IDE=CAN_ID_STD; TxMessage.DLC=len; TxMessage.RTR=CAN_RTR_DATA; if(HAL_OK!=HAL_CAN_AddTxMessage(&hcan,&TxMessage,buf,&Tx_Mail)) return 1; printf("Sending\r\n"); while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan)!=3); uint32_t tsr = hcan.Instance->TSR; printf("TSR=0x%08lX, TXOK0=%d\r\n", tsr, (tsr & CAN_TSR_TXOK0) ? 1 : 0); uint32_t esr = hcan.Instance->ESR; printf("ESR=0x%08lX, LEC=%d\r\n", esr, (esr >> 4) & 7); printf("Send_OK\r\n"); return 0; }

以上排查流程都是我从来没想到的,其实也是我不太会使用数据手册,现在知道数据手册的重要性了,到真正的项目开发,没有教程给你指导可能出现的错误,或者教程压根就不会让你碰这些可能出现的错误,我就一直不会想到使用数据手册,就会导致我一离开教程或者手把手教学就啥都做不出来。

总的来看,虽然只是一个小小的供电的问题,但是其中的排查过程可谓是十分艰辛,这也体现细节的重要性(我之前犯过这个错好几次了),但是也让我学会很多,让我意识到数据手册的重要性。

http://www.cnnetsun.cn/news/3324068.html

相关文章:

  • VERT实战指南:构建完全本地的下一代文件转换系统
  • Kohya_ss深度解析:AI模型训练环境配置与性能优化实践手册
  • futures-await与标准库async/await对比:历史演进与技术差异终极指南
  • 学Simulink——基于人工神经网络(ANN)的双向DC - AC逆变器控制仿真
  • 苍穹外卖day12
  • Skinny ORM详解:基于ScalikeJDBC的高效数据库操作指南
  • Jackhammer与Jira集成指南:实现漏洞自动上报到缺陷跟踪系统
  • Xcode 14.0 与 15.4 版本对比:Swift 5.7 到 5.10 的 3 大关键升级点
  • FastAsyncWorldEdit 开发者API指南:如何编写高性能的异步世界编辑插件?
  • 【晨迹相机】HarmonyOS ArkTS 实战:临时授权失效?使用 fileIo 实现临时 datashare 物理文件保存
  • Qwen3-ASR-Toolkit API调用优化:10个提升转录准确率的实用技巧
  • 比较长的字符串中,是否含有特定字符串_repeat
  • BurpSuite 插件迁移实战:Windows 环境 3 步完成配置同步与 Python 插件修复
  • 【转盘决定吧】HarmonyOS ArkTS 实战:阅读页长文滚动、字号控制与双列适配
  • 如何快速实现 JSON API 规范:Neomerx/Json-Api 与 Laravel、Symfony 框架集成终极指南 [特殊字符]
  • Creative View Pager性能优化指南:如何提升滚动流畅度与内存管理
  • 深度视觉象棋AI:如何用YOLOv5技术打造你的专属象棋教练
  • 07-运放应用电路-热电偶测温
  • ibus-libpinyin性能优化:提升输入响应速度的10个技巧
  • SUID国际化(i18n)实现:多语言Material Design应用的构建方法
  • prompt-ops性能优化:让你的提示词训练速度提升2倍
  • Codex模型列表不一致怎么办?CLI登录方式和配置排查
  • Dotto任天堂3DS版体验:掌机上的像素艺术创作指南
  • Midjourney光影对比崩溃预警:当--stylize >800时,对比度梯度溢出阈值临界点(实测v6.1.2内核崩溃日志+规避补丁)
  • DicomObjects COM 8.XX.125.0/32-64
  • mysql 使用逗号拼接一列数据
  • 二分查找的三重变奏:有序区间、旋转最小值与平方根——从“猜数字”到“向量检索”的思维演化
  • 如何快速部署PilotGo-plugin-prometheus:5分钟搭建集群监控系统
  • 马斯克的第一性原理到底是什么:不是鸡汤,是拆到物理层的工程方法
  • AI自动化落地卡点全突破,n8n Agent搭建避坑清单,92%开发者踩过的3类致命配置错误