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.精通UART,SPI,IIC,USB,CAN 485常见的协议移植和驱动开发,并且示波器调信号,
14.编码器控制PWM,ADC传感器读取,
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 *last=NULL; if(last!=rt_current_thread) { last=rt_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);
}