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

假装这是一个目录?

老大分配了个任务给我,使用的是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; 这种写法才是正确的

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值