【保持更新】记录单片机调试过程中遇到的坑

it2023-11-05  82

记录单片机调试过程中遇到的坑:包括STM32、NXP芯片

假装这是一个目录?零、 CubeMX1.发现新大陆 一、串口与DMA1.配置与使用2.串口中断与DMA3.串口中断异常 二、PWM三、FDCAN1.CAN波特率=APB频率/BRP分频器/ (1+tBS1+tBS2)2.CAN配置为发送的时候,数据长度的是在第16位开始设置!!3.CAN配置为接收的时候,HAL_FDCAN_ConfigGlobalFilter(函数的第二和第三个参数要正确配置,网上的教程会让整Reject,结果是一直进不了中断)4.CAN接收时候会回调 HAL_FDCAN_RxFifo0Callback函数,在这里写接收数据和处理部分的代码 四、字节对齐对结构体的影响五、指针形参中应当使用.访问

假装这是一个目录?

老大分配了个任务给我,使用的是STM32G431C8芯片;处于学习的目的,打算将整个项目的驱动层自己手把手配一遍,期间遇到了一些问题,然后有些问题是以前遇到过的,就一并归纳到这里,记录一下,以防以后再掉进这个坑里

零、 CubeMX

1.发现新大陆

今天在组长指导下,我才知道原来CubeMX固件包里面会有一个例程目录!? 大家在CubeMX-->Project Manager -->Project 最下面找那个Default Firmware Location,就可以找到了!!(告诉我,我是不是最后一个知道的....) ![例程](https://img-blog.csdnimg.cn/20201020225349423.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDUxNTg2OQ==,size_16,color_FFFFFF,t_70#pic_center) ![文件夹一览](https://img-blog.csdnimg.cn/20201020225842893.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDUxNTg2OQ==,size_16,color_FFFFFF,t_70#pic_center)

一、串口与DMA

1.配置与使用

串口和DMA进行了配置以后要自己手动打开 //main.c uint8_t usart2_rxbuf[200]={0}; __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能串口空闲中断; HAL_UART_Receive_DMA(&huart2, (uint8_t*)usart2_rxbuf, 400); //启动DMA,接收N/2就进入DMA中断

2.串口中断与DMA

空闲中断时候会触发串口中断,串口中断不会触发空闲中断,所以在串口中断服务函数里面做判断的时候需要注意两者的优先级(即先后顺序);

void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ static char count_char=0; //测试用的 /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)!=RESET){ //是空闲中断,关停DMA,转移DMA过来的数据,复位DMA的计数,重新打开DMA,清除空闲中断 __HAL_UART_CLEAR_IDLEFLAG(&huart2); HAL_UART_DMAStop(&huart2); uint8_t data_length = 200 - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); HAL_UART_Transmit_DMA(&huart2,usart2_rxbuf,data_length); //转发出去 //这里我将数据原路转发了,需要的话可以自己加环形队列处理 HAL_UART_Receive_DMA(&huart2, (uint8_t*)usart2_rxbuf,200); } else if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_RXNE)!=RESET){ //触发IDLE的时候也会触发RXNE usart2_rxbuf[count_char++]=USART2->RDR; __HAL_UART_CLEAR_FLAG(&huart2,UART_FLAG_RXNE); //同时清掉了IDLE if(count_char == 10){ count_char=0; } }

3.串口中断异常

在上述情况下,发现有时候如果发送端发送频率太快的话,串口虽然仍然工作和回传,但是回传的数据一直都是错误的,初步判断是buffer的size太小(瞎猜的);只能选择从DMA中断函数下刀了:

void DMA1_Channel5_IRQHandler(void) { /* USER CODE BEGIN DMA1_Channel5_IRQn 0 */ HAL_UART_DMAStop(&huart2); /* USER CODE END DMA1_Channel5_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart2_rx); /* USER CODE BEGIN DMA1_Channel5_IRQn 1 */ //防止卡死,重新开启一下DMA HAL_UART_Receive_DMA(&huart2, (uint8_t*)usart2_rxbuf,200); /* USER CODE END DMA1_Channel5_IRQn 1 */ }

二、PWM

配置PWM时候: Prescaler = 3-1 Preiod =1000-1 产生主载波频率为72M / ( 2+1) / ( 999 +1) = 24K的PWM 生成代码以后需要手动开启PWM (以前不知道要手动开,一直以为自己配错了系列) HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);

三、FDCAN

使用的是STM32G431C8芯片,官方例程给的是FDCAN,尝试着自己去配置成普通CAN来使用

1.CAN波特率=APB频率/BRP分频器/ (1+tBS1+tBS2)

2.CAN配置为发送的时候,数据长度的是在第16位开始设置!!

uint32_t FDCAN1_Send_Msg(uint8_t* msg,uint32_t len) { FDCAN1_TxHeader.Identifier=0x12; //32位ID FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID; //标准ID FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME; //数据帧 //FDCAN1_TxHeader.DataLength=0x00080000; //数据长度 FDCAN1_TxHeader.DataLength=(uint32_t)len<<16; //数据长度(注意左移) FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE; FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF; //关闭速率切换 FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN; //传统的CAN模式 FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS; //无发送事件 FDCAN1_TxHeader.MessageMarker=0xdd; if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送 return 0; }

3.CAN配置为接收的时候,HAL_FDCAN_ConfigGlobalFilter(函数的第二和第三个参数要正确配置,网上的教程会让整Reject,结果是一直进不了中断)

int FDCAN_Filter_Start() { FDCAN_FilterTypeDef FDCAN1_RXFilter; FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID FDCAN1_RXFilter.FilterIndex=0; //滤波器索引 FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型 FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0 FDCAN1_RXFilter.FilterID1=0x111; //0x000; //32位ID FDCAN1_RXFilter.FilterID2=0x7ff; //如果FDCAN配置为传统模式的话,这里是32位掩码 if(HAL_FDCAN_ConfigFilter(&hfdcan1,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化 HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, DISABLE, DISABLE); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); HAL_FDCAN_Start(&hfdcan1); //开启FDCAN return 0; }

4.CAN接收时候会回调 HAL_FDCAN_RxFifo0Callback函数,在这里写接收数据和处理部分的代码

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET) { if(hfdcan == &hfdcan1)//CANFD1中断 { uint8_t buf_rec[8]; HAL_FDCAN_GetRxMessage(&hfdcan1,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf_rec); // 缓存数据buffer,自己加队列进行处理或者当场处理 HAL_FDCAN_ActivateNotification(&hfdcan1,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0); } } }

下一次写博客不知道又是啥时候了,先咕再说;

四、字节对齐对结构体的影响

为了方便交互,定义了一个结构体

typedef struct{ char request_type; uint32_t ptr; }Param_Package;

在task1往RTOS队列里面写一条请求,由task2进行响应

Param_Package param_request; param_request.request_type=0; //写指令 param_request.ptr=(uint8_t *)RWBuffer; //存放变量地址; xQueueSend(CAN_to_E2RomDataQueueHandle,&param_request,0); //通过队列发送写指令

在task2中进行接收处理及响应:

if(xQueueReceive(CAN_to_E2RomDataQueueHandle,&param_req,0)==pdTRUE){ if(param_req.request_type==0){ //写请求 memcpy(&app_param.crc,(uint8_t *)param_req.ptr,4); //uint32_t crc uint32_t crc_=crc16_calculate_e(((uint8_t*)param_req.ptr+4),sizeof(app_param.mqtt_param)); if(crc_ == app_param.crc){ //crc校验通过 //Update Param AT24CXX_Write(PARAM_BASE_ADDR,param_req.ptr,sizeof(app_param.mqtt_param)+4); //存入E2ROM param_res.request_type=1; //参数更改成功 param_res.ptr=0; }else{ //crc校验不通过 //【存在的问题:不能上报错误信息?】 param_res.request_type=0; //参数更改失败 param_res.ptr=0; //错误码 } xQueueSend(E2Rom_to_CANDataQueueHandle,&param_res,0); //通过队列进行响应 }

在task1中param_request.ptr为0x1ffc100 在task2中param_req.ptr为 0x1ffbd10 一开始猜测是以下几种情况导致的:①指针使用错误;②结构体成员数据类型错误;分别对这两种情况进行了尝试,但是情况依旧存在 后来用局部变量代替了buff[5]代替了结构体实例,两边实现了正常交互;然后碰巧在最近遇到过因为结构体字节对齐问题导致的sizeof()结果出错,于是尝试了在结构体前面增加向1字节对齐的宏

#pragma pack(1) //结构体按照1字节对齐 typedef struct{ char request_type; uint32_t ptr; //针对responce 是否要做错误码 }Param_Package;

五、指针形参中应当使用.访问

报错信息: invalid type argument of ‘->’ (have 'struct ')

/** * @brief 获取软件编译时间 * * @param app_msg * @return 指针返回 */ static void Hard_Time_Get(App_param* point){ uint32_t date=0; uint32_t time=0; char str_[50]; uint32_t year,month,day; memcpy(str_,__DATE__,sizeof(__DATE__)); memcpy(str_+sizeof(__DATE__)-1,__TIME__,sizeof(__TIME__)); year=(str_[7])*10000000 + (str_[8])*1000000 + (str_[9])*100000 +(str_[10])*10000; date+=year; switch(str_[0]+str_[1]+str_[2]){ case JAN: month=1; break; case FEB: month=2; break; case MAR: month=3; break; case APR: month=4; break; case MAY: month=5; break; case JUN: month=6; break; case JUL: month=7; break; case AUG: month=8; break; case SEP: month=9; break; case OCT: month=10; break; case NOV: month=11; break; case DEC: month=12; break; } date+=month*100; day=(str_[4])*10+(str_[5]); date+=day; date-=(0x30)*11110011; point->tbox_param.EditDate=date; time=(str_[11])*100000+(str_[12])*10000+(str_[14])*1000+(str_[15])*100+(str_[17])*10+(str_[18]); time-=(0x30)*111111; point->tbox_param.EditTime=time; }

point->tbox_param->EditDate=date; 这种写法就会导致上面的报错 point->tbox_param.EditDate=date; 这种写法才是正确的

最新回复(0)