裸机编程与调试 - 在项目中遇到的各类问题,解决经验分享(常见新手问题)

 

目录

一、软件调试四个目标

二、裸机软件调试

三、裸机编程

五、单片机软件调试中常见的案例(杂记)


往期系列文章:

1、裸机项目开发经验分享 - 完整开发流程介绍(项目规划与执行、器件选型、资料检索、产品测试思路等)

2、裸机项目代码设计与管理 - 项目程序的组织管理、设计优化思路等经验分享

3、裸机编程与调试 - 在项目中遇到的各类问题,解决经验分享(常见新手问题)

4、在实际开发项目中总结的硬件调试经验分享 - 调试目标、方法,以及常见调试问题解决

5、基于AltiumDesigner软件的PCB,原理图设计完整介绍,项目经验分享 [硬件开发设计]

6、硬件开发设计 - 焊接电路板,介绍焊接概念,焊接步骤,常见错误,难点等

7、电池供电应用中的电源设计:干电池特性了解、LDO与DC-DC选型设计、电流检测方案要点

 

虽然传闻,项目程序三分编写,七分调试;但在执行项目程序之前必须有良好的思路和规划,并不是胡乱一通写完,这样只会大大增加多余调试以及返工风险;

 

一、软件调试四个目标

*项目的开发时间基本用在调试与及BUG的修复(维护)
1、确保程序表达书写正确(多练);
2、程序流程是否正确(中断正常跳入、触发一些条件时是否能进入相应的流程);
3、变量的值是否正确(必要时可以观看寄存器的值);
4、引脚的电平变化是否正确(示波器捉取);
 

二、裸机软件调试

*积累寻找软硬故障的经验,当你积累上百个问题的时候,你会很自然地处理其它问题;
1、C语言的一般错误:内存泄漏(数组越界)、堆栈溢出、内存错误、字符串问题;
 
2、在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。
 
3、程序调试与硬件调试一样,需要分模块进行调试,软件更需要细分调试[硬件层(时钟、中断)、驱动层(IIC、SPI、UART、GPIO)、应用层(逻辑控制)],程序的确定性比硬件高,不要害怕程序问题;
 
4、调试方法一般方法:演绎法、强制纠错法(转换语句表达)、串口打印;
 
5、单片机的时钟配置、看门狗时钟配置、中断设置、RAM初始化等在系统配置文件中执行,启动文件( 裸机的bootloader );
 
6、裸机程序调试
    a、从GPIO输出输入开始检查(示波器检测高低电平波形是否正确)
    b、根据软件的控制程序去查看硬件表现是否正常;
     c、保证硬件连接无误再进行软硬联调;
    d、有时考虑硬件的干扰也是解决问题的关键(语音IC连线太长)
    e、配置顺序问题 - AD(开启时钟与上电使能顺序);
    f、漏配置问题 - 时钟没启动、使能;
    g、延时问题 - 时钟频率没稳定,配置没稳定;
    h、时序问题;
 
7、调试过程中不同编译器对寄存器的操作有所不同;
    a、ARM仿真器进行仿真、调试时,不进行操作内部各种寄存器与状态寄存器还会改变;
    b、有的寄存器标志位在读取操作会改变;
    c、复位重启的重要性:地址的重装载;一些数据的重新初始化;寄存器标志位的清除;
 
8、善于利用串口打印运行信息,最简单也最有效;
 
9、程序使调试出来的吗:  http://blog.csdn.net/zhangxingping/article/details/6899293
 
10、查看堆栈溢出:    http://qshanbao.blog.ustc.edu.cn/?p=809
 
 

 
12、解决问题的能力(心态十分重要):
    a、冷静分析,捕捉更多的现象和解决方案;
    b、现象复现;
    c、对整个项目的完整把握,才能做出有效的分析!!!!(最稳重的解决思路);
    d、解决问题的清晰思路十分重要;
    e、心态与技术同样重要;
 
13、清晰各种千奇百怪的错误的特征,积累以及解决;
 

三、裸机编程

1、准备工作
    a、成功的软件(demo)和硬件(开发板);主要用于对比分析问题;
    b、有类似的调试经验能快速上手;
 
2、单片机内部空间有自己操作外设的ROM(利用指针函数触发一下,即可执行特定操作,用户无法了解其实现过程);
 
3、先把前人的路演习一遍,然后构建自己的项目,后期不断把学习到的新东西应用在新项目上,不断优化自己的程序架构和形成风格;
 
4、编写程序命名 (标准):
    a、函数名称:驱动层+芯片+部件+事件+操作;
    b、常量名称:功能+操作;
    c、变量名称:变量类型+功能+操作;
    d、对齐(函数段对齐)
    e、命名清晰
 
5、程序的模块化与函数不同:
     a、模块化是指将复杂的程序功能化整为零而成的功能块,一个模块可能由多个函数组成,可能只有一个模块,可能是紧密相连的代码块;
     b、函数是为了将需要多次使用的代码统一编写;
 
6、含中断的程序,易出BUG;用并行的想法去编写;调试时多思路去解决;
 
7、程序编写就是集成的过程,多看例程(积累的过程);
 
8、使用程序状态标志变量的基本原则:默认状态->状态1->状态2->状态3->默认状态;
 
9、复用代码放在h文件,必须避免代码重复;
 
10、尽量以库函数接口形式编写函数,减少函数的耦合性(输入参数以及引用参数);
 
11、形成标准以及自我风格的编写风格(命名、缩进、标注、对齐、文件架构等  参考成熟的代码文件);
 
12、 程序尽量选用模板去编程,因为有很多设置会导致错误;
 
13、配置一个模块之前要了解以下几点:
    a、IC实现功能的原理以及结构简单了解;
    b、通讯接口以及协议,软件必须做到无一错误;
    c、一些注意事项,(无论硬件或者软件),往往是解决问题的关键;
    d、硬件连接图以及原理;
    e、应用场景以及选型优势;
    
14、单片机是一种面向流程化的开发;
    a、狭义上:初始化程序的按照一定的顺序才能被正确执行;
    b、广义上:必须确保每个流程都是定向的;
 
15、if与for函数如果只有一行代码,则可以省略符号{ };“ 勘误:任何函数只有一行代码都可省略{ }”
 
16、GPIO的中断开关要控制好,不用的一定要习惯关上,会影响其他的中断;
 
17、注意编程软件自动把无用的语句自动清除(优化),如定义变量没有使用;
 
18、变量必须需赋初值;静态变量不赋初值默认为零;
 
19、*(unsigned int *)0x002200 &= ~(1<<0);  左值必须为变量,可赋值的symbol; 
 
 

五、单片机软件调试中常见的案例(杂记)

1、单片机驱动最好参照官方以及成熟的例子,设计模式,不然会出很多岔子;
 
2、当你的程序较大时,就需要注意内存是否够用(常量与静态变量的储存);
 
3、当中断多时切记注意栈的大小是否够用,不够用会造成数据的覆盖,导致程序崩溃;
 
4、申请大内存缓冲区必须慎重,尤其函数嵌套的栈内存不可估计,单片机内存宝贵,需要慎重使用。
 
5、*程序的死机原因(满足一定条件才触发,最难找出问题的出处,故编程时考虑排除问题的技巧设置):
    a、指针越界;
    b、数组越界;
    c、使用memcpy( );导致司机;
    d、导致死机的根本原因是误操作内存;
    e、RAM内存溢出;
    f、内存泄漏!
    g、读了只读内存;会导致死机;
    h、指针越界会导致死机;
 
6、节省ROM内存:
    a、换成裸机驱动;
    b、节省打印信息;
    c、使用外部EEPROM储存信息;
    d、多使用重复代码函数封装;
    e、手工代码优化: http://bbs.21ic.com/icview-734770-1-1.html ;
    f、编译器优化:1)编译压缩比: https://jingyan.baidu.com/article/546ae185d915971148f28c69.html ; 2)发行版本 
 
7、程序的压缩思路:
    a、减少重复代码(利用函数、宏定义等方式将重复代码封装);
    b、删减,优化程序(删除调试信息输出);
    c、调整编译器压缩比;
 
8、压缩程序思路:
   a、优化蓝牙控制引脚;
   b、清除错误表达与串口调试语句;
   c、换用裸机驱动;
   d、多使用重复代码函数封装;
 
9、一些时而出现时而好现象分析:
   a、 现象:AD睡眠后醒来读取不了数据; 解决:睡眠前解初始化,醒来后重新初始化;
   b、 现象:IIC通讯传感器,一半通讯正确,一半则无效;原因是原理图出错,导致通讯时地址有时有效,有时没效; 解决:示波器检查通讯的正常性;
 
10、中断向量读取标志->导致中断不断触发,主程序无法继续;
 
11、低功耗调试:
    a、功耗的产生的根本原因:1) 引脚之间存在电压差,电流不断流走 2) IC内部引脚的置高置低;
    b、IO配置模式(IIC外接上拉:需要配置上拉输出);
    c、SPI配置空闲模式,下拉输出;
    d、其余配置空闲模式,下拉输出;
    e、掌握功耗的一些细节:
          1)mA级以上一般是模块一直在工作中;
          2)uA(百位级):有如:(1)FLASH(品牌不对,导致驱动不匹配);(2)模块配置正确(模块没接上,输出/输入要对应);
          3)uA(百位以内):重点考察模块配置出错;
    f、LED等产生的1mA功耗;
    g、中断引脚的低功耗处理;比如滑盖、按键在正常使用情况下为一直按下(接地状态):
          1)GPIO配置为浮空,外部接2MΩ上拉电阻,消除高功耗;
 
12、仿真调试:
    a、一般驱动才会用到仿真,实时仿真少用;
    b、芯片深度睡眠难以仿真,采用实时调试;
 
13、宏定义定义的常量类型默认是int类型;
 
14、对地址进行强制转换类型(避免指针内存混乱):(void*)  ->   (uint8_t *)
 
15、电源问题导致MCU与WIFI模块的串口通讯出现问题;
 
16、软件控制逻辑:
    a、当控制逻辑增加时,必然存在很多非正常操作导致的BUG,组合BUG、综合BUG的出现;
    b、尽量控制单线程控制,减少组合逻辑混乱,尽管限制单逻辑响应速度不够快;
 
17、蓝牙通信一些健壮性手段:a、重发机制;b、校验机制;c、等待超时;
 
18、切勿设置过多的标志位;尽量简化整个程序;
    a、busy标志位的重要和运用,占用与释放;
    b、全局标量转化为局部变量;
    c、全局标志位 转化为 函数的参数,这样增加了灵活性和降低复杂度;
 
20、AD的程序与例程的位置不一致(校准与分频要适才行);
 
22、一定要消息数据类型的判断,别溢出,否则出现死机情况;
    a、定时器在不断地进行自增操作(unsigned char类型),所以在系统运行一段时间后就出现死机,程序跑飞;
 
23、while(retry_Times--);慎用,理清楚自减后的条件(先自减,再调用);
 
24、PL2303波特率最高支持115200!!!!
 
25、SPI读写FLASH
    a、W25Q16/W25Q32通用;
    b、同一种类型,不同牌子的芯片可能会存在操作时序的差异;
    c、24C32/24C64,地址差异一个字节;驱动时注意时序操作;
 
26、单片机中断处理:
    a、中断函数一定要轻盈化,快进快出;
    b、中断不做任何处理(只做倒计时),这样可以减少很多不可预见性的问题;
 
27、一些局部变量(自增或自减),尽量添加volatile修饰,以防被编译器优化失效:
    a、加上volatile的情况:标志位、递减值、重要寄存器;
 
28、框架结构灵活:是以牺牲内存空间、架构臃肿为代价;
    a、兼容性的处理办法一般采用条件编译;
    
29、善用指针操作:
    a、利用指针自加操作,可以节省运行栈内存,运行速度更快;
    b、保存接收的数据,以防止被覆盖;
    c、善用指针能减少大量内存;
    
30、串口乱码缺失问题:
    a、刷新睡眠时间;
    b、优先开启串口初始化以及开启中断;
 
31、时得时不得的现象解决思路:
    a、考虑输入参数或者其他调用的参数出错,出现冲突;
    b、更换参数的表达方式;
    
32、独立调试的时候能成功,在文件中不能正常:
    a、原因,数据冲突,解决办法:使用全局变量时先清空;
 
33、蓝牙到APP数据出现断包现象;解决:更改串口接收条件;由超时退出变为检测+超时机制;
 
34、AD软件配置问题:
    a、分频数太大,导致AD检测存在不稳定状态;
    b、ADC初始化配置有误(一定要遵循官方的设计);
 
35、keil编译器没有报错,下载程序后单片机不运行,原因是单片机内存不足导致;
 
36、按键不稳定,误触唤醒灯问题解决:
    a、将按键处理搬出中断处理;
    b、更换初始化标志变量;
    c、外加上拉、滤波电路;
    d、增加判断机制:如若唤醒后没有用户操作,执行立即睡眠;
    
37、影响堆栈的可能:
    a、全局变量、缓冲区、for循环、递归、函数的嵌套;
    b、keil自带启动代码模板,自动将剩余的data ram空间分配给栈;
    
38、LPC824 GPIO口配置问题:
    a、配置为消极模式后,唤醒需要增加配置GPIO复用功能,然后才能正常配置;
    
39、指纹模块调试:
    a、指纹重复录入;返回的错误代码不兼容新版本;
    b、开发过程中,模块的资料都是不断更新的,注意做适配;
    c、模块的问题;(指纹模块由于内存不足,导致不同手指录入都成功);
    d、指纹头反应不灵敏,供电为生电导致的;
    e、电源波动导致指纹触摸失效;
    f、固件出错,部分指令失效;
    g、辨识度不够,无反应等问题;更新固件;
 
40、初始化顺序十分重要,千万不能随意更改,即使行得通,也会存在极大的隐患;
 
41、蓝牙锁板电路设计注意:
    a、电机与电源端接近,并且增加线路的横街面积;
    b、采用钽电容稳压;
    
42、BOD配置时睡眠时没有启动时钟,导致清除标志位失败,系统一直复位(系统唤醒后检查复位标志位,发现复位标志位有效则进行复位);
    a、每一种外设都有对应的时钟开启,发现外设失效时首先考虑外设时钟是否正常;    
    
43、触摸按键的抗干扰设计:
    a、抗干扰设计(串入电阻、滤波电容接地);
    b、电源RC滤波电路;
 
44、RFID读卡灵敏度调试:1)一、匹配电路  二、接收电路(滤波电容、匹配电阻)  三、LPCD参数调整;(唤醒幅度、接收放大);
 
45、注意器件的上电初始化问题,一定的延时十分重要(指纹上电需要延时200ms才能正常发送指令);
    
46、单片机的串口打印十分耗时;
    
47、IO复用问题:
    a、烧录引脚可以复用;
    b、复用思路:分时复用引脚;
    
48、keil搜索全词匹配,注意空格也算字符,搜索时有差异;
 
49、全局数组变量数据被踩踏,原因排查: 1)检查局部变量在某一时刻的定义是否栈溢出了;2)检查全局变量的定义是否过大;
 
51、PL2303波特率最高支持115200;
 
52、串口卡死不能打印,晶振频率不同,按照公式修改寄存器值;
 
53、RTC时钟问题,外部晶振配置问题;
 
54、MKL16看门狗问题:1)初始化必须在SystemInit(void),内完成; 2)SystemInit(void);调用无法查看;
 
55、按键在主循环检测,注意与taskglobal的任务冲突,操作逻辑问题;
 
56、单片机栈空间很小,注意嵌套的局部变量累计超过栈空间(函数返回才会释放栈内存),造成溢出;
 
57、strcat输入的第一个参数必须是确定大小的数组,指针会死机;
 
58、局部缓冲区定义过大(比如为1K),全局数组变量空间数据会被修改!
 
59、IC、ID卡分类:
    a、频率:13.56Mhz  高频卡 卡号:4字节(一般都是这种)、7字节、10字节
    b、频率:125khz    低频卡 卡号:固定为10位
    c、TypeA:M1卡、银行卡、公交车卡  TypeB:身份证
 
60、FLASH读写速度比EEPROM更快;
 
61、理清字符串与数字之间的关系;ASCII与数字之间的关系;内存中储存形式一致,实质是解释不同;
 
62、函数传递进来的指针数据,应该尽量保存在本地,这样操作安全并且速度快;
 
63、选用for(;;) 比while(1)更节省空间;
 
64、有些情况寻找中间变量会更节省运行时间; 如a->b->c->age,引用过长,就需要引用中间变量;
 
65、同时声明多个变量优于单独声明变量;
 
66、数据域出错,全局变量覆盖;解决局部变量改全局变量;
 
67、一些头文件符号缺失或错误(不会出现在问题行,而是指向C文件)
 
68、GPIO口不能直接赋值,用setbit或resetbit
 
69、数值超出范围uint8_t uint16_t(integer conversion resulted in truncation)
 
70、stm8头文件定义在C文件或者h文件都是可行的
 
71、注意变量需要初始化(赋0),不赋值的话,初始值为随机值;
 
72、注意使用寄存器值的时候尽量用位与清零,尤其是复杂的程序时,防止不明导致寄存器值的变化,良好习惯;
 
73、使用memcpy();拷贝数组缓冲区不足,指针越界导致程序死机;
 
 
------------------------------End of the article--------------------------------------------
 
 
 
 
 
 
 
 
 
  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值