主题思想:
接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置DMA接收,把地址指向DMA接受数组的开始。
发送:DMA发送时,置位标记,需等发送完成中断里清零标记,防止连续调用DMA发送,造成上次没发完,这次直接更改了DMA发送的指针。导致发送不完整。
一、CubeMX配置(图不是一个工程的,仅作参考)
- 配置串口,可配置多个串口,方式相同
- 配置完成后,根据使用习惯生成代码
二、改写代码(代码是F103,用了Uart1和Uart3)
- 打开MDK,修改DMA处void MX_DMA_Init(void) 里,无需DMA接收中断,注释掉DMA接收中断,具体注释掉通道几,根据芯片来
void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Channel2_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn); /* DMA1_Channel3_IRQn interrupt configuration */ // HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0); // HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn); /* DMA1_Channel4_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn); /* DMA1_Channel5_IRQn interrupt configuration */ // HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0); // HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn); }
-
在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h)
/* USER CODE BEGIN Private defines */
#define RECEIVELEN 64
#define USART_DMA_SENDING 1//发送未完成
#define USART_DMA_SENDOVER 0//发送完成
#define MODBUD_SENDMODE 1
typedef struct
{
uint8_t Receive:1;//空闲接收标记
uint8_t Send:1;//发送完成标记
uint16_t RxLen;//接收长度
uint8_t RxBuf[RECEIVELEN];//DMA接收缓存
}UART_T;
extern UART_T xtUart1,xtUart3;
/* USER CODE END Private defines */
/* USER CODE BEGIN Prototypes */
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length);
void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length);
void UsartReceive_IDLE(UART_HandleTypeDef *huart);
/* USER CODE END Prototypes */
3. 然后在usart.c里需要添加空闲中断处理函数,以及DMA发送函数等。(usart.c)
1)首先定义数据类型:
/* USER CODE BEGIN 0 */
UART_T xtUart1,xtUart3;
/* USER CODE END 0 */
2) 然后定义空闲中断处理函数和DMA发送函数 ,串口的Idle中断函数名需要申明到uart.h文件里,方便中断文件函数里调用
/* USER CODE BEGIN 1 */
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
//DMA发送函数
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
if(Length == 0) return;
while(xtUart1.Send == USART_DMA_SENDING){}
xtUart1.Send = USART_DMA_SENDING;
HAL_UART_Transmit_DMA(&huart1, pdata, Length);
}
void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length)
{
if(Length == 0 || xtUart3.Send == USART_DMA_SENDING)
return;
while(xtUart3.Send == USART_DMA_SENDING){}
TXMODE_485();
xtUart3.Send = USART_DMA_SENDING;
HAL_UART_Transmit_DMA(&huart3, pdata, Length);
}
//DMA发送完成中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_DMA_DISABLE(huart->hdmatx);
if(huart->Instance == huart1.Instance)
xtUart1.Send = USART_DMA_SENDOVER;
else if(huart->Instance == huart3.Instance)
{
xtUart3.Send = USART_DMA_SENDOVER;
RXMODE_485();
LED1ON();
}
}
static void HAL_UART_DMAStopRX(UART_HandleTypeDef *huart)
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
HAL_DMA_Abort(huart->hdmarx);
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
}
//串口接收空闲中断
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{
// HAL_UART_DMAStop(huart);
HAL_UART_DMAStopRX(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
if(huart->Instance == huart1.Instance)
{
xtUart1.RxLen = RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
xtUart1.Receive=1;
HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);
}
else if(huart->Instance == huart3.Instance)
{
xtUart3.RxLen = RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
xtUart3.Receive=1;
HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
}
}
LED1OFF();
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
// __HAL_UART_GET_IT_SOURCE(huart,UART_IT_ERR);
__HAL_UART_CLEAR_PEFLAG(huart);
//UART_FLAG_ORE/UART_FLAG_NE/UART_FLAG_FE/UART_FLAG_PE
// if(__HAL_UART_GET_FLAG(huart,UART_FLAG_ORE))
// __HAL_UART_CLEAR_OREFLAG(huart);
// if(__HAL_UART_GET_FLAG(huart,UART_FLAG_NE))
// __HAL_UART_CLEAR_NEFLAG(huart);
// if(__HAL_UART_GET_FLAG(huart,UART_FLAG_FE))
// __HAL_UART_CLEAR_FEFLAG(huart);
// if(__HAL_UART_GET_FLAG(huart,UART_FLAG_PE))
// __HAL_UART_CLEAR_PEFLAG(huart);
}
/* USER CODE END 1 */
4. 在中断文件里添加(当然,上面的空闲中断处理函数需要声明)(stm32fxxxit.c)
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
UsartReceive_IDLE(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
5. 主函数里,打开空闲中断,初始化DMA接收
/* USER CODE BEGIN 2 */
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_TC);
__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_TC);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);
HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
/* USER CODE END 2 */
6. 然后就可以在while(1)里处理数据了
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(xtUart1.Receive)//如果产生了空闲中断
{
xtUart1.Receive=0;//清零标记
Usart1SendData_DMA(xtUart1.RxBuf,xtUart1.RxLen);//串口打印收到的数据。
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
修改的代码就到这里了。但是HAL库版本更迭,可能函数名会不同。
来源:oschina
链接:https://my.oschina.net/u/3954913/blog/3127399