嵌入式开发笔记随记
1、CAN 485相关理解3、snprintf(写到哪里写多少个格式化字符,变量) snprintf(buf,sizeof(temp),%s,version);strncat(连接到哪里连接谁从哪里开始连接) strncat(buf,temp.sizeof(buf)-strlen(buf)-1);4、结构体数组的使用和初始化Device_ST *dev_st device[zj_id-23];5、与或非异或同或解决不了的变量取值问题使用遍历移位 与操作 再取反6、对于一个结构体实例化是一个二维数组比如DevInfo_md md_dev[3][MAX_DEV];假设 MAX_DEV 10那么 md_dev[3][MAX_DEV] 就是一个包含 3 行每行 10 个 DevInfo_md 元素的数组。你可以通过 md_dev[i] 访问某一行也可以通过 md_dev[i][j] 访问某一行的具体元素。md_dev[i] 是类型 DevInfo_md[MAX_DEV] 的一维数组md_dev[i][j] 是 DevInfo_md 类型的单个元素md_dev[i]访问的是这一行一个类型二维数组访问的是具体的某个元素7、函数指针结构体的用法8、开发商物业楼栋楼层 结构体指针的应用如果是全局的结构体创建很多的数组会占用大量的全局空间内存总10KB左右 数据量小均匀连续如果是局部结构体占用栈区空间消耗总40KB左右但是访问比较快不用考虑内存泄漏问题性能好代码简单如果使用结构体指针创建需要动态开辟内存空间 总120KB左右需要手动释放开发复杂但是节省内存空间不会导致溢出 数据量大不均匀对于启动文件的堆栈空间线程创建消耗栈区内存虽然可能创建的线程很多超过了栈区的总大小但是实际使用并没有超过当前线程分配的栈空间大小还有一些情况内存空间是动态调整的但是最好还是遵循内存布局使用指针不需要的时候释放空间需要再用另外访问的时候遍历也能少一些9、冒泡快速插入反转斐波那契质数10、中断和事件外部中断11.在多个实时变化的数据中取出最大值12.并发线程多个时间线程同时异步执行多个中继器同时执行不同的喷洒逻辑状态机消息队列定时器13.精通UARTSPI,IICUSBCAN 485常见的协议移植和驱动开发并且示波器调信号14.编码器控制PWMADC传感器读取15.IAP升级bootloader开发移植16.rt_thread操作系统 OTA升级功能17.rt_thread_mdelay打印部分哪个线程在占用 线程耗时比较短的线程打印18.对于数字类需要加U尾缀 然后移位操作 如果超过32位 1需要改成 (UINT64_C(1)) 所有的数字都要19.事件集的处理20.实时打印当前运行的线程名称在void rt_schedule(void)中最后增加 #if DEBUG_TIME static struct rt_thread *lastNULL; if(last!rt_current_thread) { lastrt_current_thread; rt_kprintf(RUN:%s\r\n, rt_current_thread-name); } #endif /* enable interrupt */ rt_hw_interrupt_enable(level); __exit: return;21.实时打印线程名称和delay时间rt_err_t rt_thread_mdelay(rt_int32_t ms) { #if DEBUG_TIME rt_thread_t thread; thread rt_thread_self(); if(ms 0 ms 1000) { rt_kprintf([DIRECT_MDELAY] th%s ms%d tick%u\r\n, thread ! RT_NULL ? thread-name : null, ms, (unsigned int)rt_tick_get()); } #endif rt_tick_t tick; tick rt_tick_from_millisecond(ms); return rt_thread_sleep(tick); }22.低功耗模式下让JLINKE一直保持连接DBG_ConfigPeriph(DBG_SLEEP | DBG_STOP | DBG_IWDG_STOP | DBG_WWDG_STOP, ENABLE);23.线程最好要统一处理尽量减少线程的使用增加局部函数体的调用局部变量一定要初始化24.当使用rt_thread_mdelay进行时间片切换的时候其他外部触发需要准确下一步跳转到xx线程运行的处理参考安定宝代码gpio点灯部分25.随机bug原因:1.边界问题 临界区2.共享内存3.全局变量多出修改4.上行输入诊断5.饱和限幅值6.溢出 泄露7.锁范围释放8中断嵌套优先级配置错误9野指针、空指针访问10未初始化局部变量11看门狗使用不当12Flash/EEPROM 擦写超时、写冲突13内存对齐问题软件设计规则□ 01. 我处理的是“当前状态”还是其实应该处理“状态变化”□ 02. 如果只允许触发一次我有没有保存 last / handled□ 03. 多个 bool 能不能用 bitmask 表达得更清楚□ 04. if/else 是否已经多到应该改成状态机□ 05. 有没有为了等时间而阻塞本可继续运行的任务□ 06. 超时判断是否使用了统一的 tick/时间单位□ 07. 输入会不会抖是否需要连续 N 次确认或迟滞□ 08. 中断里是否做了 printf、解析、循环、阻塞等重工作□ 09. 生产数据和消费数据速度不同是否需要队列/环形缓冲区□ 10. 共享变量是否存在多个写者能否明确唯一 owner□ 11. 多字段读取是否可能出现“一半新、一半旧”□ 12. 数组下标、长度、地址、枚举是否做了范围检查□ 13. “0 / -1 / 0xFF”到底是正常值还是故障值有效性是否应独立□ 14. 故障应该自动恢复还是必须人工 reset□ 15. 数值计算、计数器、乘法有没有溢出风险□ 16. 通信失败后是有限重试还是无脑高频重发需求一实现一风险点一查手册一做实验一总结一个模型。查看CAN时钟分频APB1时钟#include n32g45x.h#include rtthread.h/* 调试变量方便 Keil Watch 直接查看 */volatile uint32_t g_rcc_cfg 0;volatile uint32_t g_apb1_pres 0;volatile uint32_t g_apb1_div 0;volatile uint32_t g_pclk1 0;void CAN_Clock_Debug(void){/* 读取 RCC 时钟配置寄存器 */g_rcc_cfg RCC-CFG;/* 提取 APB1 分频配置 bit[10:8] */g_apb1_pres (g_rcc_cfg 8) 0x07U;/* 计算 APB1 分频系数 */if (g_apb1_pres 4U){g_apb1_div 1U;}else{g_apb1_div 1U (g_apb1_pres - 3U);}/* 计算 APB1 时钟 */g_pclk1 SystemCoreClock / g_apb1_div;rt_kprintf(\r\n CLOCK DEBUG \r\n);rt_kprintf(HCLK %u Hz\r\n,(unsigned)SystemCoreClock);rt_kprintf(CFG 0x%08X\r\n,(unsigned)g_rcc_cfg);rt_kprintf(PRES %u\r\n,(unsigned)g_apb1_pres);rt_kprintf(DIV %u\r\n,(unsigned)g_apb1_div);rt_kprintf(PCLK1 %u Hz\r\n,(unsigned)g_pclk1);}