STM32 hal库Can发送数据时第一包会发送两次

该问题在发送can数据时,采用查询方式
那么就会进行一个等待超时:
    while(!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox)))
    {
      /* Check for the Timeout */
      if(Timeout != HAL_MAX_DELAY)
      {
       if((Timeout == 0U)||((HAL_GetTick() - tickstart ) > Timeout))
       {
         hcan->State = HAL_CAN_STATE_TIMEOUT;

         __HAL_CAN_CANCEL_TRANSMIT(hcan, transmitmailbox);

         /* Process unlocked */
         __HAL_UNLOCK(hcan);
         return HAL_TIMEOUT;
        }
      }
    }
产生等待超时,返回hal_timeout;但是此时却发送成功了;
此时那么应该考虑是不是该标志在检测之间已经被清除了,那么还会有什么东西可能这检查之前就去清标志呢?
  答案中有一个:进入中断了。

那又是什么中断呢? can发送中断

好那我们看一下发送中断里边会清除这些标志吗?
void HAL_CAN_IRQHandler(CAN_HandleTypeDef* hcan)
.........
if(__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME))
.........
 /* Clear transmission status flags (RQCPx and TXOKx) */
      SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0  | CAN_TSR_RQCP1  | CAN_TSR_RQCP2 | \
                                   CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);
......
我们可以看到,在邮箱都为空时会清除,我们由于采用查询方式自然只会占一个邮箱,那么它发送ok了自然会产生邮箱为空的中断请求。
此时can发送中断函数如果没有编写中断函数或者 中断函数没有相应的HAL_CAN_Transmit_IT()来开启相应中断, ,那么can发送中断相关中断的标志位不会再打开了,也就相当于关闭了can发送中断

好,到这儿应该也明白了为什么会出现标题所说的现象:
在调用HAL_CAN_Transmit()后,can将该消息发送出去了,产生中断清除了标志,然后我们还在那儿傻等标志变化为发送成功,等不到自然就会增生timeout,然后我们判断发送没成功,进行下一次发送,can发送中断此时相当于关闭状态,本次发送数据时不会再有人来清除发送标志了,所以can发送成功,不会再有timeout了。

解决方案: 要用中断发送就用中断发送就好,不用中断发送的话关闭/不开启can发送中断就好。

另外多说一嘴: 采用查询发送时,发送成功后应清除相应标志,否则那儿永远都是发送成功。
    		while( HAL_CAN_Transmit(&hcan1, 0x1) != HAL_OK) {
			if(retry++ > 5) {
				iSysDebug(0, "send can data failed\r\n");
				SET_BIT(hcan1.Instance->TSR, CAN_TSR_RQCP0  | CAN_TSR_RQCP1  | CAN_TSR_RQCP2 | \
				        CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);
				return HAL_ERROR;
			}
		}
		/* Clear transmission status flags (RQCPx and TXOKx) */
		SET_BIT(hcan1.Instance->TSR, CAN_TSR_RQCP0  | CAN_TSR_RQCP1  | CAN_TSR_RQCP2 | \
		        CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);


### 使用STM32 HAL通过SPI发送数据 为了实现通过SPI接口发送数据,可以利用STM32 HAL提供的函数来简化操作。下面是一个完整的例子,展示了如何初始化SPI并发送一组数据。 #### 初始化SPI外设 首先,在`main.c`文件中的`MX_SPI1_Init()`函数里设置SPI参数: ```c static void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; // 设置为主设备模式 hspi1.Init.Direction = SPI_DIRECTION_2LINES;// 双向通信 hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 数据大小为8位 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 钟极性低电平有效 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // 钟相位第一个跳变沿采样 hspi1.Init.NSS = SPI_NSS_SOFT; // 软件管理NSS信号 hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 波特率预分频器 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // MSB先传送 hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 禁用TI模式 hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // CRC校验关闭 hspi1.Init.CRCPolynomial = 7; hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE; hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE; if (HAL_SPI_Init(&hspi1) != HAL_OK){ Error_Handler(); } } ``` 这段代码配置了SPI作为主控端,并指定了诸如波特率、帧格式等必要属性[^2]。 #### 发送数据到从机 接着定义一个用于实际发送数据的过程如下所示: ```c void SendDataToSlave(uint8_t *data, uint16_t size) { /* Check the parameters */ if ((NULL == data)||(0 >= size)){ return ; } /* Wait until TXE flag is set to send new byte */ while(HAL_SPI_GetState(&hspi1)!= HAL_SPI_STATE_READY); /* Transmit Data using DMA */ if (HAL_SPI_Transmit_DMA(&hspi1, data, size) != HAL_OK){ /* Transfer error in transmission process */ Error_Handler(); } else { /* Success callback can be implemented here or wait for completion */ } } /* Call this function after transfer complete interrupt occurs */ void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if(hspi->Instance==SPI1){ /* Process completed successfully */ // Add your code here when transmit finished. } } ``` 上述程序片段中含了两个主要部分:一个是负责启动DMA传输过程的`SendDataToSlave()`;另一个是在每次完成一次DMA事务之后被调用的回调方法`HAL_SPI_TxCpltCallback()`. 这种设计允许应用程序在后台执行其他任务的同处理SPI通讯事件.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值