记录单片机调试过程中遇到的坑:包括STM32、NXP芯片
假装这是一个目录?
老大分配了个任务给我,使用的是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,¶m_request,0); //通过队列发送写指令
在task2中进行接收处理及响应:
if(xQueueReceive(CAN_to_E2RomDataQueueHandle,¶m_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,¶m_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; 这种写法才是正确的