用到RS485通信,但是CTRL是用单片机引脚控制的,并且用到DMA发送接收

CTRL用引脚控制,则需要自己判断切换发送接收的时机

1、简单的485发送

不使用中断不使用DMA时,直接在发送前后控制ctrl来切换发送状态即可

void rs485_send(uint8_t* sd_data,uint8_t sizeofsd_data)
{
//ctrl高电平发送,低电平接收
	HAL_GPIO_WritePin(GPIOC, Pc8_RS485_CTL, GPIO_PIN_SET);
	HAL_UART_Transmit(&huart6, sd_data, sizeofsd_data, 100);
//	rt_thread_mdelay(5);//不加也无影响,发送是阻塞的
	HAL_GPIO_WritePin(GPIOC, Pc8_RS485_CTL, GPIO_PIN_RESET);
}

HAL_UART_Transmit是阻塞发送的,等发送完成后才会调用到下面的HAL_GPIO_WritePin

2、很操蛋的DMA发送

HAL_UART_Transmit_DMA(&huart6, sd_data, sizeofsd_data)

使用DMA发送时,并不是阻塞。很快就能调用后面的函数,如果同上面一样,在下面直接让CTRL电平反转,则还未发送,就已经变成接收模式了。

如果不着急,可以在下面加入延时

void rs485_send(uint8_t* sd_data,uint8_t sizeofsd_data)
{
//ctrl高电平发送,低电平接收
	HAL_GPIO_WritePin(GPIOC, Pc8_RS485_CTL, GPIO_PIN_SET);
	HAL_UART_Transmit_DMA(&huart6, sd_data, sizeofsd_data);
	rt_thread_mdelay(5);
	HAL_GPIO_WritePin(GPIOC, Pc8_RS485_CTL, GPIO_PIN_RESET);
}

第二条发送的方法,正常应该没人会用,主要是引入个观点,DMA需要判断发送完成的时机

3、使用DMA发送,并且发送完成要及时切换回接收

DMA有一个可能用到的中断标志

TCIFx:x通道的传输完成标志

UART有两个可能用到的中断标志

TC:发送完成标志
TXE:发送缓冲区空标志

DMA的不清楚怎么说,大概和TXE差不多

解释一下几个寄存器:

首先要知道:发送时,数据先进入发送数据寄存器,然后进入移位寄存器开始发送。
当发送数据寄存器空了------则TXE置1;	但是要注意,TXE是只要是空的就置1,需要发现置1后就关掉他(TC不会,第一次空才会置1)
当移位寄存器空了---------则TC 置1;注意:	关DMA时,TC中断使能会被关掉

image-1736843153894

那么就能知道,TXE置1时,数据还有一部分没有发送出去
TC置1时,数据才是完全发送完
另外的DMA的那个中断标志,和TXE类似,是把数据全丢进移位寄存器了就置1,所以也是一部分数据没发送出去

那么判断uart发送完成就要用到TC标志

//在uart中断回调中,使用TC标志来判断发送完成
if(__HAL_UART_GET_FLAG(&huart6, UART_FLAG_TC) != RESET)
		{
			__HAL_UART_CLEAR_FLAG(&huart6, UART_FLAG_TC);
			HAL_GPIO_WritePin(GPIOC, Pc8_RS485_CTL, GPIO_PIN_RESET);
		}

需要注意的是:TC标志使能会在关DMA时被关掉,不清楚是为什么,需要重新开启

我的处理方法是在HAL_UART_DMAStop(&huart6);后面重新开启TCIE

			HAL_UART_DMAStop(&huart6);//停止DMA,为了重新设置DMA发送多少数据
			huart6.Instance->CR1 |= USART_CR1_TCIE;

关于HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)

uart使用DMA接收后或发送后,需要关掉DMA重新配置,才能再次发送或接收

但是HAL库给出的 UART_DMAStop函数,会吧发送和接收的DMA都关掉

/**
  * @brief Stops the DMA Transfer.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
{
  uint32_t dmarequest = 0x00U;
  /* The Lock is not implemented on this API to allow the user application
     to call the HAL UART API under callbacks HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback():
     when calling HAL_DMA_Abort() API the DMA TX/RX Transfer complete interrupt is generated
     and the correspond call back is executed HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback()
     */

  /* Stop UART DMA Tx request if ongoing */
  dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAT);
  if ((huart->gState == HAL_UART_STATE_BUSY_TX) && dmarequest)
  {
    ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);

    /* Abort the UART DMA Tx stream */
    if (huart->hdmatx != NULL)
    {
      HAL_DMA_Abort(huart->hdmatx);
    }
    UART_EndTxTransfer(huart);
  }

  /* Stop UART DMA Rx request if ongoing */
  dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
  if ((huart->RxState == HAL_UART_STATE_BUSY_RX) && dmarequest)
  {
    ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

    /* Abort the UART DMA Rx stream */
    if (huart->hdmarx != NULL)
    {
      HAL_DMA_Abort(huart->hdmarx);
    }
    UART_EndRxTransfer(huart);
  }

  return HAL_OK;
}

可以看到,函数先判断DMA发送和接收的使能标志是否置1,如果置1就关掉

这样如果同时用到DMA发送和接收,就会造成比如 发送前要重新设置时,接收同时被关掉,所以当用到发送和接收时,要把HAL_UART_DMAStop这个函数拆掉