用到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中断使能会被关掉
那么就能知道,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这个函数拆掉