debug手法案例—代码量越多定位越重要

别人的文章:
秘笈:电子电路调试步骤及注意事项

1、反推法

1.1、问题出现

如下代码程序运行效果与个人设计不符

//*********************************************************
#include "SYSCFG.h"
//***********************宏定义****************************
typedef unsigned char u8;
#define	Key PA4
//***********************变量定义****************************
static u8 TimeCnt = 0;    //记录按下时长,刻10ms
static u8 lock = 0;       //lock变量用于判断是否是第一次进行按键确认状态
typedef enum
{
  KEY_CHECK = 0,     //检测状态
  KEY_COMFIRM = 1,   //按键确认,已经识别到了前沿抖动
  KEY_RELEASE = 2    //按键释放,
}KEY_STATE;  
static  KeyState =0;   // 初始化按键状态为检测状态
static u8 g_KeyFlag = 0;                // 按键有效标志,0: 按键值无效; 1:按键值有效
typedef enum 
{
    NULL_KEY = 0,
    SHORT_KEY =1,
    LONG_KEY=2
}KEY_TYPE;
static KEY_TYPE g_KeyActionFlag;		//用于区别长按和短按

static int shortCnt;
static int longtCnt;

/**

 */
void Key_Scan(void)
{  
    switch (KeyState)
    {
        //按键未按下状态,此时判断Key的值
        case   0:    
           if(!Key)   
            {
                KeyState =  1;  //如果按键Key值为0,说明按键开始按下,进入下一个状态 
            }
            TimeCnt = 0;                  
            break;
            
            
            
        case   1:
            if(!Key)                     //查看当前Key是否还是0,还是就计数加一
            {          
                TimeCnt++; 
                if(TimeCnt > 61) 
                {
                
                }
                if(TimeCnt > 8) 
                {
                
                }
                                
            }   
            else                         // 不还是0,按键释放了
            {
                KeyState = 2;
                TimeCnt = 0;             //计数复位
            } 
            break;
            
            
            
         case  2:
             if(Key)                     //当前Key值为1,说明按键已经释放,返回开始状态
             { 
                 KeyState =  0;    
             }
             break;
             
         default: break;
    }    
}
/*-------------------------------------------------
 *  函数名:interrupt ISR
 *	功能:  定时器0的中断处理
 *  输入:  无
 *  输出:  无
 --------------------------------------------------*/
void interrupt ISR(void)			
{ 
    static u8 cnt; 
	if(T0IE && T0IF)		 //开定时器/计数器0中断&&中断标志置位
	{
		cnt++;
        if(cnt>2)           // 每 8.16*n ms执行一次按键扫描程序
        {
            Key_Scan();
            cnt = 0;
        }
        T0IF = 0;           //清零time0中断标志
	} 
} 

/*-------------------------------------------------
 *	函数名称:	TIMER0_INITIAL
 *	功能:    	初始化设置定时器0
 *	设置TMR0定时时长=(1/系统时钟频率)*指令周期*预分频值*255
 *					=(1/16000000)*2*256*255=8.16ms                      
 -------------------------------------------------*/
void TIMER0_INITIAL (void)  
{
	OPTION = 0B00000111;
    //Bit5: 	T0CS Timer0时钟源选择 
	//			1-外部引脚电平变化T0CKI 0-内部时钟(FOSC/2)
	//Bit4: 	T0CKI引脚触发方式 1-下降沿 0-上升沿
	//Bit3: 	PSA 预分频器分配位 0-Timer0 1-WDT 
	//Bit[2:0]: PS2 8个预分频比 111 - 1:256

	T0IF = 0;						//清空T0软件中断
}

//***********************函数声明**************************
void configurate_initial(void);            //上电系统初始化

/*-------------------------------------------------
* 函数名:main 
* 功能:  主函数
* 输入:  无
* 输出:  无
 --------------------------------------------------*/
void main()
{

    u8 i=0;
    //上电初始化
	configurate_initial();     //初始化配置
    
    TIMER0_INITIAL();      //每8.16ms 执行一次按键扫描程序
    GIE = 1; 			   //开中断
	T0IE = 1;			   //开定时器/计数器0中断
    
    u8 switch11=0;
//    PA7=1;//开启电源
//    PC3=1;//开启激光发射器
    
    //上电初始化完毕后
    while(1)
    {
       if(KeyState==1&&TimeCnt > 61)     //16ms*61=1s
       {
          PA7=1;//开启电源
          PC3=1;//开启激光发射器
          if(i==1)
          {
             PA7=0;//关闭电源
          }
          i++;
       }
        
//       if(KeyState==1&&TimeCnt > 8)   //16ms*8=128ms
//       {
//           PC3=~PC3;//取反激光发射器的开关  
//       }
    }
}

/*-------------------------------------------------
* 函数名:POWER_INITIAL
* 功能:  上电系统初始化
* 输入:  无
* 输出:  无
 --------------------------------------------------*/	
void configurate_initial(void) 
{ 
    //中断与时钟配置
	OSCCON = 0B01110001;	//IRCF=111=16MHz/2T=8MHz,0.125us
	INTCON = 0;  			//暂禁止所有中断
    
    PORTA = 0B00000000;		//将PA0~7的引脚状态初始为0
    PORTC = 0B00000000; 	//将PC0~7的引脚状态初始为0
    
    OPTION = 0B00001000;	//地址OPTION处的Bit3控制看门狗时钟和时钟0的共用分频电路归谁。为1归看门狗,Bit2~0是分频电路的分频系数PS=000
    MSCKCON = 0B00000000;   //地址MSCKCON处的Bit6->0,禁止PA4,PC5稳压输出;  Bit5->0,TIMER2时钟为Fosc;  Bit4->0,禁止LVR       
	CMCON0 = 0B00000111; 	//关闭比较器,CxIN为数字IO口
    
    //PA4
	TRISA = 0B00010000;		//地址TRISA处的Bit4=1,表输入
    WPUA = 0B00010000;     	//地址WPUA处的Bit4=1,表开上拉

    //PC3 
	TRISC = 0B00000000;		//地址TRISA处的Bit3=0,表输出
    WPUC = 0B00001000;     	//地址WPUA处的Bit3=0,表开上拉
    
    //PA7
	TRISA = 0B00000000;		//地址TRISA处的Bit7=0,表输出
    WPUA = 0B10000000;     	//地址WPUA处的Bit7=1,表开上拉
}

1.2、减少代码量,使用指示灯PC3

去掉一些代码,希望多按几次按键后引脚PC3可以在示波器上出现上升沿和下降沿。
在这里插入图片描述
事实是PC3一直保存高电平

//*********************************************************
#include "SYSCFG.h"
//***********************宏定义****************************
typedef unsigned char u8;
#define	Key PA4
//***********************变量定义****************************
static u8 TimeCnt = 0;    //记录按下时长,刻10ms
 
static  KeyState =0;   // 初始化按键状态为检测状态

/**

 */
void Key_Scan(void)
{  
    switch (KeyState)
    {
        //按键未按下状态,此时判断Key的值
        case   0:    
           if(!Key)   
            {
                KeyState =  1;  //如果按键Key值为0,说明按键开始按下,进入下一个状态 
				PC3=1}
            TimeCnt = 0;   			
            break;
            
            
            
        case   1:
            if(!Key)                     //查看当前Key是否还是0,还是就计数加一
            {          
                TimeCnt++; 				
            }   
            else                         // 不还是0,按键释放了
            {
                KeyState = 2;
				PC3=0;
                TimeCnt = 0;             //计数复位
            } 
            break;
            
            
            
         case  2:
             if(Key)                     //当前Key值为1,说明按键已经释放,返回开始状态
             { 
                 KeyState =  0;  
                 PC3=1}
             break;
             
         default: break;
    }    
}
/*-------------------------------------------------
 *  函数名:interrupt ISR
 *	功能:  定时器0的中断处理
 *  输入:  无
 *  输出:  无
 --------------------------------------------------*/
void interrupt ISR(void)			
{ 
    static u8 cnt; 
	if(T0IE && T0IF)		 //开定时器/计数器0中断&&中断标志置位
	{
		cnt++;
        if(cnt>2)           // 每 8.16*n ms执行一次按键扫描程序
        {
            Key_Scan();
            cnt = 0;
        }
        T0IF = 0;           //清零time0中断标志
	} 
} 

/*-------------------------------------------------
 *	函数名称:	TIMER0_INITIAL
 *	功能:    	初始化设置定时器0
 *	设置TMR0定时时长=(1/系统时钟频率)*指令周期*预分频值*255
 *					=(1/16000000)*2*256*255=8.16ms                      
 -------------------------------------------------*/
void TIMER0_INITIAL (void)  
{
	OPTION = 0B00000111;
    //Bit5: 	T0CS Timer0时钟源选择 
	//			1-外部引脚电平变化T0CKI 0-内部时钟(FOSC/2)
	//Bit4: 	T0CKI引脚触发方式 1-下降沿 0-上升沿
	//Bit3: 	PSA 预分频器分配位 0-Timer0 1-WDT 
	//Bit[2:0]: PS2 8个预分频比 111 - 1:256

	T0IF = 0;						//清空T0软件中断
}

/*-------------------------------------------------
* 函数名:POWER_INITIAL
* 功能:  上电系统初始化
* 输入:  无
* 输出:  无
 --------------------------------------------------*/	
void configurate_initial(void) 
{ 
    //中断与时钟配置
	OSCCON = 0B01110001;	//IRCF=111=16MHz/2T=8MHz,0.125us
	INTCON = 0;  			//暂禁止所有中断
    
    PORTA = 0B00000000;		//将PA0~7的引脚状态初始为0
    PORTC = 0B00000000; 	//将PC0~7的引脚状态初始为0
    
    OPTION = 0B00001000;	//地址OPTION处的Bit3控制看门狗时钟和时钟0的共用分频电路归谁。为1归看门狗,Bit2~0是分频电路的分频系数PS=000
    MSCKCON = 0B00000000;   //地址MSCKCON处的Bit6->0,禁止PA4,PC5稳压输出;  Bit5->0,TIMER2时钟为Fosc;  Bit4->0,禁止LVR       
	CMCON0 = 0B00000111; 	//关闭比较器,CxIN为数字IO口
    
    //PA4
	TRISA = 0B00010000;		//地址TRISA处的Bit4=1,表输入
    WPUA = 0B00010000;     	//地址WPUA处的Bit4=1,表开上拉

    //PC3 
	TRISC = 0B00000000;		//地址TRISA处的Bit3=0,表输出
    WPUC = 0B00001000;     	//地址WPUA处的Bit3=0,表开上拉
    
    //PA7
	TRISA = 0B00000000;		//地址TRISA处的Bit7=0,表输出
    WPUA = 0B10000000;     	//地址WPUA处的Bit7=1,表开上拉
}

/*-------------------------------------------------
* 函数名:main 
* 功能:  主函数
* 输入:  无
* 输出:  无
 --------------------------------------------------*/
void main()
{

    //上电初始化
	configurate_initial();     //初始化配置
    
    TIMER0_INITIAL();      //每8.16ms 执行一次按键扫描程序
    GIE = 1; 			   //开中断
	T0IE = 1;			   //开定时器/计数器0中断
    
    PA7=1;//开启电源
    PC3=1;//开启激光发射器
    
    //上电初始化完毕后
    while(1)
    {
    }
}

1.3、继续减少代码量

去掉了函数Key_Scan。改成PC3=~PC3;希望可以在示波器上看到方波,每隔8.16ms变换一次电频。
成功输出了:
在这里插入图片描述
问题就在函数Key_Scan中。

//*********************************************************
#include "SYSCFG.h"
//***********************宏定义****************************
typedef unsigned char u8;
#define	Key PA4
//***********************变量定义****************************
static u8 TimeCnt = 0;    //记录按下时长,刻10ms
 
static  KeyState =0;   // 初始化按键状态为检测状态

/**

 */
void Key_Scan(void)
{  
    switch (KeyState)
    {
        //按键未按下状态,此时判断Key的值
        case   0:    
           if(!Key)   
            {
                KeyState =  1;  //如果按键Key值为0,说明按键开始按下,进入下一个状态 
				PC3=1}
            TimeCnt = 0;   			
            break;
            
            
            
        case   1:
            if(!Key)                     //查看当前Key是否还是0,还是就计数加一
            {          
                TimeCnt++; 				
            }   
            else                         // 不还是0,按键释放了
            {
                KeyState = 2;
				PC3=0;
                TimeCnt = 0;             //计数复位
            } 
            break;
            
            
            
         case  2:
             if(Key)                     //当前Key值为1,说明按键已经释放,返回开始状态
             { 
                 KeyState =  0;  
                 PC3=1}
             break;
             
         default: break;
    }    
}
/*-------------------------------------------------
 *  函数名:interrupt ISR
 *	功能:  定时器0的中断处理
 *  输入:  无
 *  输出:  无
 --------------------------------------------------*/
void interrupt ISR(void)			
{ 
    static u8 cnt; 
	if(T0IE && T0IF)		 //开定时器/计数器0中断&&中断标志置位
	{
		cnt++;
        if(cnt>2)           // 每 8.16*n ms执行一次按键扫描程序
        {
            PC3=~PC3;
            cnt = 0;
        }
        T0IF = 0;           //清零time0中断标志
	} 
} 

/*-------------------------------------------------
 *	函数名称:	TIMER0_INITIAL
 *	功能:    	初始化设置定时器0
 *	设置TMR0定时时长=(1/系统时钟频率)*指令周期*预分频值*255
 *					=(1/16000000)*2*256*255=8.16ms                      
 -------------------------------------------------*/
void TIMER0_INITIAL (void)  
{
	OPTION = 0B00000111;
    //Bit5: 	T0CS Timer0时钟源选择 
	//			1-外部引脚电平变化T0CKI 0-内部时钟(FOSC/2)
	//Bit4: 	T0CKI引脚触发方式 1-下降沿 0-上升沿
	//Bit3: 	PSA 预分频器分配位 0-Timer0 1-WDT 
	//Bit[2:0]: PS2 8个预分频比 111 - 1:256

	T0IF = 0;						//清空T0软件中断
}

/*-------------------------------------------------
* 函数名:POWER_INITIAL
* 功能:  上电系统初始化
* 输入:  无
* 输出:  无
 --------------------------------------------------*/	
void configurate_initial(void) 
{ 
    //中断与时钟配置
	OSCCON = 0B01110001;	//IRCF=111=16MHz/2T=8MHz,0.125us
	INTCON = 0;  			//暂禁止所有中断
    
    PORTA = 0B00000000;		//将PA0~7的引脚状态初始为0
    PORTC = 0B00000000; 	//将PC0~7的引脚状态初始为0
    
    OPTION = 0B00001000;	//地址OPTION处的Bit3控制看门狗时钟和时钟0的共用分频电路归谁。为1归看门狗,Bit2~0是分频电路的分频系数PS=000
    MSCKCON = 0B00000000;   //地址MSCKCON处的Bit6->0,禁止PA4,PC5稳压输出;  Bit5->0,TIMER2时钟为Fosc;  Bit4->0,禁止LVR       
	CMCON0 = 0B00000111; 	//关闭比较器,CxIN为数字IO口
    
    //PA4
	TRISA = 0B00010000;		//地址TRISA处的Bit4=1,表输入
    WPUA = 0B00010000;     	//地址WPUA处的Bit4=1,表开上拉

    //PC3 
	TRISC = 0B00000000;		//地址TRISA处的Bit3=0,表输出
    WPUC = 0B00001000;     	//地址WPUA处的Bit3=0,表开上拉
    
    //PA7
	TRISA = 0B00000000;		//地址TRISA处的Bit7=0,表输出
    WPUA = 0B10000000;     	//地址WPUA处的Bit7=1,表开上拉
}

/*-------------------------------------------------
* 函数名:main 
* 功能:  主函数
* 输入:  无
* 输出:  无
 --------------------------------------------------*/
void main()
{

    //上电初始化
	configurate_initial();     //初始化配置
    
    TIMER0_INITIAL();      //每8.16ms 执行一次按键扫描程序
    GIE = 1; 			   //开中断
	T0IE = 1;			   //开定时器/计数器0中断
    
    PA7=1;//开启电源
    PC3=1;//开启激光发射器
    
    //上电初始化完毕后
    while(1)
    {
    }
}

1.4、"事故"位置找到,分析事故现场

1)反推法

中断程序可以按照设定的时间,一次又一次的执行。
中断函数里的Key_Scan函数却不能正常执行。
PC3恒为1,所以PC3=0没有被执行。
PC3=0要执行的话,Key就要等于0。
所以Key一直没有等于0,一直为1。

为什么一直为1?
设计是要Key,即引脚PA4做上拉输入。当按键按下时被拉低为0。
在这里插入图片描述

2)祸根

看看PA4的配置

void configurate_initial(void) 
{ 
    //中断与时钟配置
	OSCCON = 0B01110001;	//IRCF=111=16MHz/2T=8MHz,0.125us
	INTCON = 0;  			//暂禁止所有中断
    
    PORTA = 0B00000000;		//将PA0~7的引脚状态初始为0
    PORTC = 0B00000000; 	//将PC0~7的引脚状态初始为0
    
    OPTION = 0B00001000;	//地址OPTION处的Bit3控制看门狗时钟和时钟0的共用分频电路归谁。为1归看门狗,Bit2~0是分频电路的分频系数PS=000
    MSCKCON = 0B00000000;   //地址MSCKCON处的Bit6->0,禁止PA4,PC5稳压输出;  Bit5->0,TIMER2时钟为Fosc;  Bit4->0,禁止LVR       
	CMCON0 = 0B00000111; 	//关闭比较器,CxIN为数字IO口
    
    //PA4
	TRISA = 0B00010000;		//地址TRISA处的Bit4=1,表输入
    WPUA = 0B00010000;     	//地址WPUA处的Bit4=1,表开上拉

    //PC3 
	TRISC = 0B00000000;		//地址TRISA处的Bit3=0,表输出
    WPUC = 0B00001000;     	//地址WPUA处的Bit3=0,表开上拉
    
    //PA7
	TRISA = 0B00000000;		//地址TRISA处的Bit7=0,表输出
    WPUA = 0B10000000;     	//地址WPUA处的Bit7=1,表开上拉
}

可以发现PA4被配置成了关闭上拉的输出。祸根就是它。
在这里插入图片描述

2、利用示波器的动态波形

利用示波器,运行程序,观察程序运行在不同阶段时,信号的动态变化。

长按按键:开关机
短按按键:按第1次长亮,
按第2次光强有强到弱,再从弱到强循环。需要中断
按第3次10HZ闪烁

问题:按第3次激光10HZ闪烁几下后,变成长亮?
示波器上看到,按第3次时,出现了占空比逐渐变化的动态波形。非常的直观
在这里插入图片描述
据此可以程序逻辑上有问题,按两次的处理程序,在按三次后运行了。

注释渐变程序
在这里插入图片描述

按三次,激光正常闪烁了
为什么渐变程序执行了
因为渐变程序在主函数,从按两次的处理程序中断返回,执行的不一定是上图180行的判断,若是渐变程序的182~190,就直接执行了一遍,才去执行180行的判断。

3、溢出造成的奇怪现象

为什么两边不同的代码产生了不同的波形?
在这里插入图片描述
努力猜测,解释现象
可以理解为time2计时开始点与time1拉高时间点不一致。

但是为什么不一致?难以猜测解释

我们把time2的延时加长看看。看看补偿上,行不行。
在这里插入图片描述
还是这个样子
发现:无论是对高偏短的延长定时,还是对高偏长的缩短定时。波形依旧,则就说明,不是步调的原因,猜测不正确。

time2不是与time1存配合有问题,单独对time2进行测试,看看time2自身的配置是否可以达到效果。
在这里插入图片描述
在这里插入图片描述
实际定时值为3.5us,与个人设计的500相差极大。
time2配置有问题
在这里插入图片描述
PR2是8位,最大值是255
祸根是这个溢出
在这里插入图片描述
修改后效果正常了。

4、二分法

将数学的零点类比为祸根:
对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。

5、隔离与定位

5.1 硬件调试

就去查看电路是否可以正常运行,那个地方不能正常运行

1)没有思路

当遇到没有思路时,就要在电路上做改变,无论是改变电池电压,还是把一些线割掉,亦或是把一些器件给焊取下来,就是要做出改变,再去根据新的现象猜测目标问题的原因。

2)隔离与定位

排除思想很重要

6、搜索法

发现程序效果不对时,ctr+f相关变量,看看项目中所有地方的幅值与引用是否正确。

7、细化现象以得到指向

7.1细化使用产品时的异常现象

当出现让人摸不着头脑的现象时,进一步具体化现象,
比如有灯乱闪,要细化成以什么样的频率闪,或者是其他闪烁的特征。
再如:个人人水平仪项目:出现按键H1和V1无法控制激光。
细化1:在推动开机状态,有时会出现按键H1和V1无法控制激光。
————还无法得到指向。
细化2:在推动开机状态,在切换接收模式和非接收模式时,有时会出现按键H1和V1无法控制激光。
————还无法得到指向。
细化3:在推动开机状态,在非接收模式时,短按pow按键调节光强,再切换回到接收模式,不做任何处理,立即返回非接收模式,此时一定会出现按键H1和V1无法控制激光。都是如果立即返回非接收模式后先短按pow按键调节光强,到此POW短按次数为4时,H1和V1又会变得受控。
————可以得到指向:看看代码对上诉步骤都是怎么处理的。

7.2细化产品异常时,软件运行的环境变量,硬件电流电压

7.3有指向可以顺藤摸瓜

POW短按次数为4时,H1和V1又会变得受控
上述句子的4非常有研究价值

8、树形结构的猜想与层层验证

分析问题时:对可能功能实现的硬软件执行流程,树形结构分析,那一块是祸根
软件树形:
在这里插入图片描述

9、串口调试

keil调试需要两个特定引脚,串口只需要任意一个引脚

#ifndef __UART_H
#define __UART_H
/****软件串口开始**********************************************/
//	异步收发		接收和发送同时进行互不影响
//  三倍采样率	如9600波特率,定时器中断时间为34.6uS
//  2020-05-27更新1.20版:
//      优化结构体定义
//      优化串口接收部分,接收更稳定正确
//  2019 BY:问星
//********************************************************************
//定义接收和发送的IO口
#define IO_RX RA6 //定义接收的IO口
#define IO_TX RA7 //定义发送的IO口
// sbit IO_RX P2 ^ 1;
// sbit IO_TX P2 ^ 2;
/* 
使用方法:

1、定义接收和发送的IO口
	//C51
	sbit IO_RX P2^1;
	sbit IO_TX P2^2;
	
	//PIC
	#define	IO_RX RC3	//定义接收的IO口
	#define	IO_TX RC4	//定义发送的IO口
	
2、在main主程序while(1)循环之前加上串口初始化代码
	UART_INIT();
3、设置一个定时器的中断时间为34.6uS,在定时器中断里加上下边的代码
	UART_TX();
	UART_RX();
4、在main主程序while(1)循环中判断,UART_RX_STA标志是否是RX_DATAOK,如果是则收到新数据,可以处理收到的数据或将数据转存。
	if(UART_RX_STA==RX_DATAOK)
	{
		unsigned char dat;
		dat=UART_RX_BUFF;	//UART_RX_BUFF里的数据为串口收到的数据
		UART_RREST();	//准备接收下一字节数据
	}
5、在发送数据前判断UART_TX_STA标志是否是TX_FREE空闲状态,是空闲状态
	if(UART_TX_STA==TX_FREE)
	{
		unsigned char dat;
		dat=0xF2;
		UART_TDATA(dat);	//将dat从串口发送出去
	}
*/
//********************************************************************
#define UC8 unsigned char

struct
{
	UC8 STA;  //接收状态标志 0:空闲;1:开始接收;2:接收完成
			  //发送状态标志 0:空闲;1:准备发送;2:正在发送;3:发送停止位;0:发送完成
	UC8 NUM;  //8位单字节接收/发送计数
	UC8 CNT;  //接收/发送周期计数
	UC8 BUFF; //接收/发送缓存变量
} UART_RX, UART_TX;

#define UART_RX_STA UART_RX.STA //接收状态
#define UART_TX_STA UART_TX.STA //发送状态
#define UART_RX_BUFF UART_RX.BUFF	//接收缓存
#define UART_TX_BUFF UART_TX.BUFF	//发送缓存

#define RX_FREE 0	//接收空闲状态
#define RX_DATAOK 3 //数据接收完成状态
#define TX_FREE 0	//发送空闲状态

//********************************************************************
//串口初始化
#define UART_INIT()      \
	{                    \
		IO_TX = 1;       \
		UART_RX.CNT = 0; \
		UART_RX.STA = 0; \
		UART_TX.STA = 0; \
	}

//********************************************************************
//串口接收
#define UART_RX()                         \
	{                                     \
		switch (UART_RX.STA)              \
		{                                 \
		case 0:                           \
		{                                 \
			if (IO_RX == 0)               \
			{                             \
				UART_RX.CNT++;            \
				if (UART_RX.CNT == 2)     \
				{                         \
					UART_RX.BUFF = 0;     \
					UART_RX.STA = 1;      \
					UART_RX.NUM = 8;      \
					UART_RX.CNT = 0;      \
				}                         \
			}                             \
		}                                 \
		break;                            \
		case 1:                           \
		{                                 \
			UART_RX.CNT++;                \
			if (UART_RX.CNT == 3)         \
			{                             \
				if (IO_RX)                \
				{                         \
					UART_RX.BUFF |= 0x80; \
				}                         \
				else                      \
				{                         \
					UART_RX.BUFF &= 0x7F; \
				}                         \
				UART_RX.CNT = 0;          \
				UART_RX.NUM--;            \
				if (UART_RX.NUM > 0)      \
				{                         \
					UART_RX.BUFF >>= 1;   \
				}                         \
				else                      \
				{                         \
					UART_RX.STA = 2;      \
				}                         \
			}                             \
		}                                 \
		break;                            \
		case 2:                           \
		{                                 \
			UART_RX.CNT++;                \
			if (UART_RX.CNT == 3)         \
			{                             \
				UART_RX.STA = 3;          \
			}                             \
		}                                 \
		break;                            \
		default:                          \
			break;                        \
		}                                 \
	}
//********************************************************************
//准备接收下一数据
#define UART_RREST()     \
	{                    \
		UART_RX.STA = 0; \
		UART_RX.CNT = 0; \
	}

//********************************************************************
//串口发送数据
#define UART_TDATA(data)     \
	{                        \
		UART_TX.BUFF = data; \
		UART_TX.STA = 1;     \
		UART_TX.CNT = 0;     \
	}

//********************************************************************
//串口发送
#define UART_TX()                                   \
	{                                               \
		switch (UART_TX.STA)                        \
		{                                           \
		case 0:                                     \
			break;                                  \
		case 1:                                     \
		{                                           \
			UART_TX.CNT++;                          \
			if (UART_TX.CNT == 1)                   \
			{                                       \
				IO_TX = 0;                          \
			}                                       \
			else if (UART_TX.CNT == 3)              \
			{                                       \
				UART_TX.STA = 2;                    \
				UART_TX.CNT = 0;                    \
				UART_TX.NUM = 8;                    \
			}                                       \
		}                                           \
		break;                                      \
		case 2:                                     \
		{                                           \
			UART_TX.CNT++;                          \
			if (UART_TX.CNT == 1)                   \
			{                                       \
				IO_TX = UART_TX.BUFF & 0X01;        \
				UART_TX.BUFF = (UART_TX.BUFF >> 1); \
				UART_TX.NUM--;                      \
			}                                       \
			else if (UART_TX.CNT == 3)              \
			{                                       \
				UART_TX.CNT = 0;                    \
				if (UART_TX.NUM == 0)               \
				{                                   \
					UART_TX.STA = 3;                \
				}                                   \
			}                                       \
		}                                           \
		break;                                      \
		case 3:                                     \
		{                                           \
			UART_TX.CNT++;                          \
			if (UART_TX.CNT == 1)                   \
			{                                       \
				IO_TX = 1;                          \
			}                                       \
			else if (UART_TX.CNT == 3)              \
			{                                       \
				UART_TX.STA = 0;                    \
			}                                       \
		}                                           \
		break;                                      \
		default:                                    \
			break;                                  \
		}                                           \
	}

//********************************************************************

/*****软件串口结束*******************************************************/
#endif

10、感觉代码怎么看都是没有问题的

10.1、调用函数——软件断点:while(1);

调用函数存在,函数执行了意料之外的代码。
在可疑的函数后加软件断点:while(1);
在这里插入图片描述

10.2、HAL库

没有按照例程的使用规范,找出效果与自己想要的相违背。
好好对比例程,看是否有出入

10.3、想法错误

你实际编程让芯片干的事就是错的

10.4、多进程

多进程交叉运行,多个进程执行的特殊顺序,造成意料之外的效果

10.5、C语言语法

11、初始化配置时——配置顺序的重要性

在这里插入图片描述

12、debug粗心问题

12.1、丢三那四

12.2、张冠李戴

13、芯片手册不够详尽

13、1手册编写者认为你知道

14、debug代码逻辑有误

14.1确定自己的代码逻辑正确

在这里插入图片描述

VS Code配置C语言开发环境的超详细教程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值