参考网上分享:https://www.amobbs.com/thread-5535672-2-1.html
RS485使用DMA发送,切换收发状态,有以下几种实现方式:
- 开启DMA的“发送完成中断”,在DMA的发送完成中断中,切换收发的状态。但是,这会导致最后的2个字节发送不出去,这是因为:DMA的“发送完成中断”出现在刚发送倒数第二个字节的起始位置,这个时候切换485的收发,若接收端不是奇校验的话 将会误收到0xFF 最后第一肯定也出不去。
网上提供的解决办法是:①在DMA的TC中断里面 加大于两个字节的延时 这是OK的。 ②在DMA的TC中断里面 开启USART的“发送完成中断” 去USART的中断里面去处理 这是OK的。 - 看数据手册,可以利用串口的“发送完成中断TC”实现。开启USART的DMA,开启DMA传输通道,开启USART的“发送完成中断TC”
具体实现:
配置USART和DMA ,DMA中断不用开。
准备发送的时候,
- 切换为485发送 RS485EN_TX();-
- 配置好DMA。传输的数据,与长度。
- 开启USART的传输完成TC中断
- 使能DMA =======等串口TC中断就行。
- 中断服务函数中,失能TC 切换485接收。
开始传输:
//-----尝试不开启DMA TC中断 直接直观传输
void USART2_SendMsg(INT8U *msg,INT8U len)
{
SET_RS485_SEND; //高发送
DMA_ClearFlag(DMA1_FLAG_TC7); //清DMA发送完成标志
USART_ClearITPendingBit(USART2,USART_IT_TC);
DMA_Cmd(DMA1_Channel7, DISABLE); //停止DMA
DMA1_Channel7->CMAR = (INT32U) msg; //源地址
DMA1_Channel7->CNDTR = len;//重设传输长度
USART_ITConfig(USART2,USART_IT_TC,ENABLE);
DMA_Cmd(DMA1_Channel7, ENABLE); //启动DMA
}
传输完成后:
void USART2_IRQHandler(void)
{
/*
...................
*/
if(0x00000040 & (USART2->SR))//(USART_GetITStatus(USART2,USART_IT_TC))
{
USART2->SR &= ~0x00000040;//USART_ClearITPendingBit(USART2,USART_IT_TC);
OSSemPost(Sem_Usart2Send);
SET_RS485_RECV; //发送完毕 改成接收状态
USART2->CR1 &= ~0x00000040;//USART_ITConfig(USART2,USART_IT_TC,DISABLE);
//OSIntExit();
return;
}
/*
...................
*/
}
这里说一下,我由于使用的时候,在发送的时候没有“清除发送完成标志”,导致传输第一个字节后,就进入了TC中断,切换了485状态。
代码:
RS485EN_TX(); //485切换为发送模式
__HAL_UART_DISABLE_IT(&UART3_Handler,USART_IT_RXNE); /* 禁止接收中断 */
__HAL_UART_ENABLE_IT(&UART3_Handler,USART_IT_TC); /* 使能发送完成中断 */
if(__HAL_DMA_GET_FLAG(&UART3TxDMA_Handler,DMA_FLAG_TCIF3_7)) //等待传输完成
{
__HAL_DMA_CLEAR_FLAG(&UART3TxDMA_Handler,DMA_FLAG_TCIF3_7);//清除DMA1_Steam3传输完成标志
HAL_UART_DMAStop(&UART3_Handler); //传输完成以后关闭串口DMA
}
时序:
修改后,在前面添加清除发送完成标志TC”后,既可以。
代码:
RS485EN_TX(); //485切换为发送模式
__HAL_UART_CLEAR_FLAG(&UART3_Handler,UART_FLAG_TC);
if(__HAL_DMA_GET_FLAG(&UART3TxDMA_Handler,DMA_FLAG_TCIF3_7)) //等待传输完成
{
__HAL_DMA_CLEAR_FLAG(&UART3TxDMA_Handler,DMA_FLAG_TCIF3_7);//清除DMA1_Steam3传输完成标志
HAL_UART_DMAStop(&UART3_Handler); //传输完成以后关闭串口DMA
}
__HAL_UART_DISABLE_IT(&UART3_Handler,USART_IT_RXNE); /* 禁止接收中断 */
__HAL_UART_ENABLE_IT(&UART3_Handler,USART_IT_TC); /* 使能发送完成中断 */
MYDMA_USART_Transmit(&UART3_Handler,(u8*)USART3_TX_BUF,DRIVER_FRAME_LENGTH); //启动传输
传输完成后:
//串口3中断服务程序
void USART3_IRQHandler(void)
{
uint8_t Res=0;
uint16_t u16CheckCrcTemp = 0;
if((__HAL_UART_GET_FLAG(&UART3_Handler,UART_FLAG_TC)!=RESET))
{
__HAL_UART_CLEAR_FLAG(&UART3_Handler,UART_FLAG_TC);
RS485EN_RX();
__HAL_UART_ENABLE_IT(&UART3_Handler,USART_IT_RXNE);
__HAL_UART_DISABLE_IT(&UART3_Handler,USART_IT_TC);
}
if((__HAL_UART_GET_FLAG(&UART3_Handler,UART_FLAG_RXNE)!=RESET))
{
__HAL_UART_CLEAR_FLAG(&UART3_Handler,UART_FLAG_RXNE);
Res = USART3->DR;
g_us485AlarmCnt=0; //485通信5s报警检测
if(USART3_RX_STA & 0x4000)
{
++USART3_RX_STA;
USART3_RX_BUF[USART3_RX_STA & 0xfff] = Res;
if((USART3_RX_STA & 0xfff) == DRIVER_FRAME_LENGTH-1) //接收帧尾
{
u16CheckCrcTemp = *(uint16_t*)(USART3_RX_BUF + DRIVER_FRAME_LENGTH - 2);
if(u16CheckCrcTemp == crc_16((unsigned char *)USART3_RX_BUF,DRIVER_FRAME_LENGTH-2))
{
if(USART3_RX_BUF[2]==0xff || USART3_RX_BUF[2]==g_ulaGdBuf[INDEX_ID]) //是否是正确的ID
{
run_driver_cmd();
}
}
USART3_RX_STA = 0;
}
}
else if(USART3_RX_STA & 0x8000) //接收一帧数据的第二个数据 ---0xef 2017.1.13
{
if(Res != 0xfb)
USART3_RX_STA = 0;
else
{
USART3_RX_BUF[(++USART3_RX_STA)&0xfff] = Res;
USART3_RX_STA |= 0x4000;
}
}
else if(Res == 0xbf) //接收一帧数据的第一个数据 ---0xfb 2017.1.13
{
USART3_RX_BUF[USART3_RX_STA & 0xfff] = Res;
USART3_RX_STA |= 0x8000;
}
}
}
时序为: