嵌入式开发常遇的问题收集汇总(持续更新)

ぃ、小莉子 提交于 2019-11-28 20:10:37

STM32部分:

1、问题:在调试公司项目的时候,将使用DMA25通道采集adc3的子工程代码移植到总工程代码中,发现程序在进入到主循环的时候总是卡死。

     寻找问题:使用mdk DEBUG仿真,停止仿真程序会停在HardFault_Handler函数里的死循环while(1)中。这说明STM32出现了硬件错误。最后发现总工程中也用到了 DMA2的通道5,是用在uart4_TX上的,在硬件资源分配上产生了冲突,最后导致程序卡死。

     解决办法:使用ADC3多通道采集的非DMA方式,让出DMA资源

 

2、问题:使用MDK写程序并编译出现

                  ..\Vehicle\pedal.c(166): error:  #18: expected an expression

     寻找问题:最后在网络上找到了问题所在

                       

       Misc Controls 在MDK中默认是C90,将其改为C99即可解决     

      解决办法:如下图所示添加--c99

                       

 

3、问题:定义结构体导致出现内存大小异常问题

          

 

4、问题:在调试别人的程序过程中发现程序卡死。

     寻找问题:经过仿真发现程序总是在启动文件的
DMA2_Channel2_IRQHandler 
DMA2_Channel3_IRQHandler 
DMA2_Channel4_5_IRQHandler 
                B       . 

                ENDP 

                ALIGN  这里卡死,一直卡在B那里,返回不了。

      解决办法:与往常一样在网上寻找解决方案。最后了解到如果初始化了一个定时器但是却没有定义它的中断函数,导致找不到中断入口就死了,就会出现此类错误。

 

5、问题:写好代码,并在stm32上烧录,发现程序中的timer5,始终无法进入中断运行。

     寻找问题:经过分析,我的代码配置的芯片是是符合调试的芯片的,都是stm32f103t8u6,但是我的启动文件和,如下图所示define处定义的是  STM32F10X_HD,当将HD换成MD后此时已经找不到timer5的中断相关配置的宏,说明此芯片属于小型号的stm32系列的产品,不含有timer5。

               

    解决办法:将timer5换成timer3等其他的t8系列含有的定时器。

 

 

6、问题:说一个由于自己粗心大意而产生的问题,导致自己浪费了半个小时的时间

                  一个结构体变量sendata在一个Laser_ranging.c文件中使用,明明添加了相关DATASend.h文件,DATASend.h文件中也声明了该变量,但是提示error: use of undeclared identifier

     找到问题:最后在DATASend.h(如下代码所示)中发现  __RS485_H在另一个.h文件中已经用过了,又恰好在Laser_ranging.c文件中两个.h都包含了,所以只能包含一个,恰好排除了后一个DATASend.h的包含。


#ifndef  __RS485_H
#define __RS485_H              
#include "stm32f10x.h"                              
#include <string.h>                    
#include "Laser_ranging.h"         
#define Angle_Data_SIZE_MAX   2U
#define Attitude_Data_SIZE_MAX  9U

typedef struct
{
        uint8_t       Angle_id0;       
        uint8_t       Angle_id1;        
        uint16_t     Angle_data_length;  
        uint16_t     Angle_data[Angle_Data_SIZE_MAX];
        uint8_t       Attitude_id0;        
        uint8_t       Attitude_id1;       
        uint16_t     Attitude_data_length;
        uint16_t     Attitude_data[Attitude_Data_SIZE_MAX];    
        uint16_t pack_crc;  
}  Data_TX_Send;

extern Data_TX_Send sendata;

extern void Sensordata_TX_Str_Send(void);

#endif       

 

7、问题:在使用MDK debug调试打开watch窗口,输入变量名,查看变量,对于数组,可以直接出入数组名,就可以查看所有的元素的变量。(之前都是数组元素一个一个的输入,没有想过去尝试,有时导致工作的效率比较低。)

 

 

8、问题:在使用MDK 查看公司的老项目的STM32的工程文件,将产品开箱,利用SWD的方式将程序下载到产品中,发现并不会出现该有的现象,后来发现在如下图所示的位置中,可以选择为不同的工程,之前下载的工程为SynboxMini,而应该下载的工程为Synbox。

当程序更改为工程Synbox,就好使了(在此之前并没有用过mdk的这种使用方式)。

 

9、问题:NMI_Hander中断发生的原因。

待解决:

 

nrf52832部分

1、问题:在调试公司项目的时候,一块刚刚焊接好的nrf52832蓝牙小板子,板子上有ads1292芯片、mpu9250芯片、stm32f103芯片,ads1292芯片用于采集皮电,mpu9250用于采集三轴加速度数据以排除运动干扰,stm32与52832串口连接传输温度数据。同样的两块小板子 ,一块好使另一块不好使,不好使的现象是,小板子作为Server向Client广播,连接上后(可以连接),发现Server不能向Client发送数据。而另外一块板子好使。

     寻找问题:此时,通过排除法可知:(1)另外一块板子好使说明程序并没有问题;(2)小板子既然能够连接Client说明nrf52832没有问题;(3)屏蔽掉stm32的串口发送部分,排除掉串口数据的干扰;(4)然后在代码中屏蔽掉ads1292芯片相关驱动和应用代码,发现小板子依然不好使,排除ads1292的干扰;(5)最后在代码中屏蔽掉mpu9250的相关驱动和应用代码,发现小板子好使了,问题就在MPU9250上。

     解决办法:更换MPU9250芯片。

 

 

LINUX与C部分

 

 

1、Ubuntu下的串口助手cutecom的安装与使用 ,可以体验到在WIN中使用串口助手的感觉。

 

2、cp210x串口驱动在LINUX环境中的安装与使用

 

3、ubuntu中的常用命令   

 

4、在ubuntu中开发ESP32环境配置  参考链接:

https://docs.espressif.com/projects/esp-idf/zh_CN/latest/index.html

 

5、区别yum与apt-up 的区别。

6、在ESP32蓝牙perpher.C文件中 ,结构体赋值操作方式中,对于只带  . 如何来了解 如下,赋值时可按照条件省略相应的结构体明,直接操作其成员进行赋值。

typedef void (* esp_gattc_cb_t)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);

 

typedef struct{

     esp_gattc_cb_t   gattc_cb;

     uint16_t gattc_if;

     uint16_t app_id;

     uint16_t conn_id;

     uint16_t service_start_handle;

     uint16_t service_end_handle;

     uint16_t char_handle;

     esp_bd_addr_t remote_bda;

}gattc_profile_inst;

 

/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */

 

gattc_profile_inst   gl_profile_tab[PROFILE_NUM] = {

     [PROFILE_APP_ID_FOOT] = {

           .gattc_cb = foot_gatt_profile_event_handler,

           .gattc_if = ESP_GATT_IF_NONE,

           .app_id = PROFILE_APP_ID_FOOT,

     },

     [PROFILE_APP_ID_CHEST] = {

          .gattc_cb = chest_gatt_profile_event_handler,

          .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */

          .app_id = PROFILE_APP_ID_CHEST,

     },

     [PROFILE_APP_ID_WRIST] = {

          .gattc_cb = wrist_gatt_profile_event_handler ,

          .gattc_if = ESP_GATT_IF_NONE,

          .app_id = PROFILE_APP_ID_WRIST,

     },

     [PROFILE_APP_ID_NULL] = {

          .gattc_cb = imu_gatt_profile_event_handler,

          .gattc_if = ESP_GATT_IF_NONE,

          .app_id = PROFILE_APP_ID_NULL,

     },

};

此种赋值方式可以与以下这种赋值方式进行对比

 

typedef struct 
{
    uint8_t     SensorID_Index;                                            //id index
    uint8_t         SensorID;                                                        //high 2bit is sensor number
    uint8_t     SensorGPSSateliteCount;                                            //sensor battery mesage
    uint8_t            SensorConnectStatus;                                //sensor connect status;true or false
    
    #pragma            anon_unions
    union
    {
            uint16_t                             SensorData[SENSOR_DATA_SIZE_MAX];        //sensor data
            VEHICLE_DATA_t                Vehicle_send;
            GPS_DATA_t                        Gps_Data;
            IMU_DATA_t                        IMU_Data;
        
    };
    void                (*pSensor_Init_Fun)(void);    
    void              (*pSensor_GetData_Fun)(void);    
    void                (*pSensor_Start_Fun)(void);    
    void                (*pSensor_Stop_Fun)(void);
    uint8_t            Sensor_Data_RDY;//1:ok,0 not ok
    uint8_t            DMA_Rx_Status:4;//    0:error,1:half,2:complate
    uint8_t            DMA_Tx_Status:4;//    0:error,1:half,2:complate
}SENSOR_STRUCT;

 

SENSOR_STRUCT     Sensor_Struct[SENSOR_INDEX_MAX]=
{
   
           {SENSOR_VEHICLE_INDEX,            vehicleSensorType,                0,                                0,                    {0,0,0,0,0,0,0,0,0}, Init_Vehicle,        Get_VehicleData,            StartSample_Vehicle,                StopSample_Vehicle},         //


           {SENSOR_GPS_INDEX,                    gpsType,                                    0,                                0,                    {0,0,0,0,0,0,0,0,0}, Init_Gps,                Get_JY901GpsData,            StartSample_Gps,                        StopSample_Gps},    //


           {SENSOR_IMU_INDEX,                    imuType,                                    0,                                0,                    {0,0,0,0,0,0,0,0,0}, Init_IMU,                Get_IMUData    ,                    StartSample_IMU,                        StopSample_IMU},     //     

   
           {SENSOR_BACK_INDEX,                    backSensor,                            0,                                0,                    {0,0,0,0,0,0,0,0,0}, Init_Back,                Get_BackData,                    StartSample_Back,                        StopSample_Back},//    


           {SENSOR_OBD_INDEX,                    obdType,                                    0,                                0,                    {0,0,0,0,0,0,0,0,0}, Init_OBD,                Get_OBDData,                    StartSample_OBD,                        StopSample_OBD}, //    

};


8、关于位段的理解。

     六月三日,这是个美好的晴天的日子,在今天阅读代码的时候,又遇到了自己懵逼的地方,如下:

    uint8_t            DMA_Rx_Status:4;  //    0:error,1:half,2:complate
    uint8_t            DMA_Tx_Status:4;  //    0:error,1:half,2:complate

    为何定义了一个uint8_t之後,后面会出现一个冒号 : ,经过查找了解到,这是个位段,每个变量占据4个位,使两个变量变为一个字节大小。

 

9、关于位移<< 、>>  和乘除 * 、/   之间运算速度的比较的思考:

     经过查找资料得到以下结论:

     速度:移位>乘法>除法。
     计算机中有专门的移位功能部件,这也是最基础的部件。乘法和除法都是靠移位实现的。乘2^n,左移n位,除2^n,右移n位。

     另外,原始的乘法器是一步一步乘(移位)出来的,每次取乘数的一位与被乘数操作,1则把被乘数照写,0则为0,然后乘数右移。这样循环,最后把每一步结果加起来。
     后面通过阵列连乘器改进速度,一次算出上面每一步的结果,然后直接相加。

     乘法是加操作,而除法是每步的结果作加法或减法(加减交替法),有的算法还需要恢复上一次的结果(余数恢复法),而且每一步加减后还要进行移位,所以最慢。

     具体参见<<计算机组成原理>>。

     因为移位速度快,所以经常被应用。比如给程序加花指令(增加Cracker破解难度)时,很多地方使用移位操作。

 

10、探讨了关于嵌入式开发中,字节左移合并的问题

     比如buff[1]   与buff[2]  要合并为一个字  temp  通常的做法是

     temp  =  buff[1] << 8 + buff[2] ;

     但是这种做法会出错,至于为什么会出做........

 

     所以正确的做法为  temp = (  (  (  buff[1]  <<  8  )  &  0xff00  )  |  (  buff[2]  &  0x00ff  )  ); 

 

11、c语言中,符号#与符号##的相关用法。

     使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.

     连接 https://blog.csdn.net/baidu_33850454/article/details/79363033  中有详细说明。

 

12、不要质疑sizeof宏的运行效率,它在程序编译的时候就已经被计算好了,不参与到程序的实际运行。

 

警告语错误收集

 

1、warning: #550-D: variable "d" was set but never used
描述:变量'd'定义但从未使用,或者是,虽然这个变量你使用了,但编译器认为变量d所在的语句没有意义,编译器把它优化了.
解决方法:仔细衡量所定义的变量d是否有用,若是认定变量d所在语句有意义,那么尝试用volatile关键字修饰变量d,若是真的没有用,那么删除掉以释放可能的内存.

2、warning: #1-D: last line of file ends without a newline
描述:文件最后一行不是新的一行.编译器要求程序文件的最后一行必须是空行,想了半天没想通为什么要这样.
解决方法:可以不理会.若是觉得出现警告不爽,那么在出现警告的文件的最后一行敲个回车,空出一行.

3、warning: #111-D: statement is unreachable
描述:声明不可能到达.多出现在这种场合:
int main(void)
{
...

while(1) //无限循环,这在不使用操作系统的程序中最常见
{
...

}

return 0; //这句声明在正常情况下不可能执行到,编译器发出警告
}

解决方法:不理会.

4、 warning: C3017W: data may be used before being set
描述:变量'data'在使用前没有明确的赋值.如:
uint8 i,data; //定义变量i和data,二者都没有明确赋值

for ( i = 0; i < 8; i++) //变量'i'在语句中被赋值0
{
if ( IO1PIN & SO_CC2420 )
data |= 0x01; //变量'data'在使用前没有明确赋值,编译器发出警告
else
data &= ~0x01;
}
解决方法:应仔细衡量该变量的初始值是否为0,若是,可以不理会这个警告,因为MDK编译器在程序执行前,会将使用到的数据区初始化为0,但若是该变量的初始值不应该是0,忽略这个警告可能会引起致命错误.这个警告应引起足够重视.应养成变量赋初值的习惯,好在有编译器给把关.

5、warning: #177-D: variable "temp" was declared but never referenced
描述:变量'temp'进行了声明但没有引用.多出现在声明了一个变量,但却没有使用它,它和warning: #550-D: variable "temp" was set but never used不同之处在于temp从没有使用过.
解决方法:若是定义的变量确实没有用,删除掉;若是有用,则在程序中使用.
与该警告类似的还有 warning: #177-D: function "MACProcessBeacon" was declared but never referenced

6、warning: #940-D: missing return statement at end of non-void function "DealwithInspect2"
描述:返回非空的函数"DealwithInspect2"的最后缺少返回值声明.如:
int DealwithInspect2(uint32 test)
{
...
...
...
//此处应该是return x;返回一个int型数据,若是没有返回值,编译器产生警告
}

7、warning: #1295-D: Deprecated declaration lcd_init - give arg types

描述:在定义函数的时候,如果你写上函数参数,就会有这个警告,比如void timer_init(); 这里就没有形参,如果这样的话,编译器会给出警告.

8、 error: #65: expected a ";"
描述:缺少分号.大多是漏忘';'.
解决方法:双击错误行,在定位到错误点的附近找到没加';'号的语句,加上分号.并不一定在定位到的错误行才却分号,可能是这行的上一行,也可能是下一行.

9、error: #65: expected a ";"和 error: #20: identifier "xxxx" is undefined一块出现,而且后面的error: #20错误可能一大堆
描述:这个错误对于第一次遇上的人来说绝对是个噩梦,当错误出现,满怀希望的双击错误提示,来到错误行时却愕然发现,错误行绝对没有错,于是找找错误行的上一行,下一行,没有错误,再找上上行,下下行...让人无比郁闷的事情出现了:编译提示的所有错误行都不可能有错误出现.其实这最可能是你在.h文件声明外部变量或者函数时,没有在声明语句的最后加分号!如果你有很多模块,如main.c,lcd.c,key.c...有很多头文件,如lcd.h,key.h,若是在lcd.h文件声明函数时没有加分号,那么这种错误可能定为到main.c中,所以要检查所有头文件.
解决方法:仔细检查.h文件,将分号补上.

10、error: L6200E: Symbol flagu multiply defined (by uart0.o and main.o).

描述:变量(也是一种符号)flagu多处定义(在uart0.c中和main.c都定义了).通常错在全局变量定义重复.比如:在main.c中定义全局变量flagu:

uint8 flagu=0;

在uart0.c中也用到该变量,于是声明此变量,我通常都是先复制定义的变量再在变量前面加关键字extern修饰:

extern uint8 flagu=0;

然后编译,就会出现上面的连接错误,原因在于,我在uart0.c中是又定义了一个变量,而不是声明变量,因为我给变量赋了初值"flagu=0",这样就重复定义了变量flag.正确的声明方法是去掉赋值部分:

extern uint8 flagu;

解决办法:找到重复定义的变量,看情况修改一处.

11、error: #159: declaration is incompatible with previous "wr_lcd" (declared at line 40)
描述:在wr_lcd函数还没有声明之前就已经使用了.多出现在两种情况:第一种,wr_lcd函数体还没有写,就已经用到了它,这种情况多出现在写一个程序的大体结构中,只是简单写一下框架.第二种情况比较常见,函数a调用函数b,但函数b的函数体在函数a的下面:
void a(void) //函数a的实体
{
b(); //调用函数b
}

void b(void) //函数b的实体
{
...
}
这样如果点编译,就会产生error: #159的错误,因为当函数a调用函数b时,发现在这之前都没有函数b的任何声明.
解决方法:在函数a调用函数b之前,对函数b进行声明,如:
void b(void); //对函数b进行声明

void a(void) //函数a的实体
{
b(); //调用函数b
}

void b(void) //函数b的实体
{
...
}

12、 error: #137: expression must be a modifiable lvalue

描述:表达式必须是一个可以修改的左值.主要出现在这种现象:

a=NUM;

NUM是一个数值或表达式,a为一个变量,但a被定义为像const这种不可更改的类型,导致NUM不能赋值给变量a.

解决方法:要么放弃赋值,要么修改变量属性.

13、error: #18: expected a ")"
如果是出现在c文件中, 多半是因为少了一个")",或者错误行有编译器不识别的字符

如果出现在头文件中,错误行又是一个函数声明,多半是因为在函数声明中有编译器不认识的字符

收集问题的目的是为了不再次犯错,提高开发效率。

 

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!