串口1+dma中断 接收和发送
本实验中的串口1+dma中断是基于串口1的IDLE空闲中断实现的
额外说一句IDLE中断和RXNE中断有什么区别呢
假设上位机给MCU的USART1发送8Byte
1.IDLE:只进入一次中断->最后一个Byte的最后一个bit发送完毕之后,mcu开始计时,假设3ms(这个时间怎么计算,最后讲解)内没有收到任何数据->进入IDLE中断
2.RXNE:进入8次中断,收到每一个Byte后的停止位->进入中断
言归正传
针对STM32F429的USART1的RX dma & TX dma功能。
第一步,初始化DMA->为什么要先初始化DMA,后面会讲到
//下面的定义句柄不要忘记
DMA_HandleTypeDef UART1RxDMA_Handler; //定义句柄
u8 USART1_DMATX_FLAG=0;
void U1_RX_DMA_Config(void )
{
__HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能
__HAL_LINKDMA(&UART1_Handler, hdmarx, UART1RxDMA_Handler);//将rx和dma连接
//Rx DMA配置
UART1RxDMA_Handler.Instance = DMA2_Stream5;
UART1RxDMA_Handler.Init.Channel = DMA_CHANNEL_4;
UART1RxDMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
UART1RxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;
UART1RxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE;
UART1RxDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
UART1RxDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
UART1RxDMA_Handler.Init.Mode = DMA_CIRCULAR;
UART1RxDMA_Handler.Init.Priority = DMA_PRIORITY_LOW;
UART1RxDMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_DeInit(&UART1RxDMA_Handler);
HAL_DMA_Init(&UART1RxDMA_Handler);
__HAL_DMA_ENABLE_IT(&UART1RxDMA_Handler, DMA_IT_TC); //开启DMA传输完成中断
HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 3, 1);
HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
}
//DMA接收中断处理
void DMA2_Stream5_IRQHandler(void)
{
if(__HAL_DMA_GET_FLAG(&UART1RxDMA_Handler, DMA_FLAG_TCIF1_5)) //DMA2_Steam2传输完成
{
__HAL_DMA_CLEAR_FLAG(&UART1RxDMA_Handler, DMA_FLAG_TCIF1_5);//清DMA2_Steam2传输完成标志
HAL_UART_DMAStop(&UART1_Handler); //传输完成以后关闭串口DMA
}
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);//重新开启DMA接收
HAL_DMA_IRQHandler(&UART1RxDMA_Handler);//调用中断处理公共函数
}
void U1_TX_DMA_Config(void )
{
__HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能
__HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //将DMA与USART1联系起来(发送DMA)
//Tx DMA配置
UART1TxDMA_Handler.Instance=DMA2_Stream7; //数据流选择
UART1TxDMA_Handler.Init.Channel=DMA_CHANNEL_4; //通道选择
UART1TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设
UART1TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
UART1TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式
UART1TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度:8位
UART1TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
UART1TxDMA_Handler.Init.Mode=DMA_NORMAL; //外设普通模式
UART1TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
UART1TxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
UART1TxDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
UART1TxDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //存储器突发单次传输
UART1TxDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设突发单次传输
HAL_DMA_DeInit(&UART1TxDMA_Handler);
HAL_DMA_Init(&UART1TxDMA_Handler);
__HAL_DMA_ENABLE_IT(&UART1TxDMA_Handler, DMA_IT_TC);
HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 3, 2);
HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
//回调函数
huart->State=HAL_UART_STATE_READY;
}
//DMA发送中断处理
void DMA2_Stream7_IRQHandler(void)
{
if(__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7)) //DMA2_Steam7传输完成
{
USART1_DMATX_FLAG=1;
__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7);
HAL_UART_DMAStop(&UART1_Handler);
}
//调用中断处理公共函数
HAL_DMA_IRQHandler(&UART1TxDMA_Handler);
}
void DMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
USART1_DMATX_FLAG=0;
HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//开启DMA传输
huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA发送
while(USART1_DMATX_FLAG==0);
}
第二步,初始化USART1
#define USART_REC_LEN 200
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
UART_HandleTypeDef UART1_Handler; //UART句柄
u8 USART1_RX_CNT = 0;
u8 USART1_RX_OK = 0;
UART_HandleTypeDef UART1_Handler; //UART句柄
void uart_init1(u32 bound1)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound1; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1
GPIO_Initure.Pin=GPIO_PIN_9; //PA9 TX
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLDOWN; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10 RX
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); //抢占优先级3,子优先级3
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
__HAL_UART_DISABLE_IT(&UART1_Handler,UART_IT_TC);
__HAL_UART_ENABLE_IT(&UART1_Handler,UART_IT_IDLE); //开启接收中断
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
}
void USART1_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_IDLE) != RESET) //判断是否为IDLE中断
{
__HAL_UART_CLEAR_IDLEFLAG(&UART1_Handler);
HAL_UART_DMAStop(&UART1_Handler);
USART1_RX_CNT = USART_REC_LEN - UART1RxDMA_Handler.Instance->NDTR;
USART1_RX_OK = 1; //标记接收完成
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
}
HAL_UART_IRQHandler(&UART1_Handler);
}
第三步,调用初始化程序
int main(void)
{
HAL_Init();
Stm32_Clock_Init(360,25,2,8);
delay_init(180);
LED_Init();
U1_TX_DMA_Config();
U1_RX_DMA_Config();
uart_init1(115200);
while (1)
{
if(USART1_RX_OK)
{
USART1_RX_OK=0;
LED_PI0=!LED_PI0;
DMA_USART_Transmit(&UART1_Handler,(uint8_t*)temp_response,230+9);
}
}
}
完工
后续1
接下来说说IDLE的超时时间是怎么计算的
假设串口波特率为115200,这代表串口的发送速率为115200bit/s
发送1bit所需时间=1s/115200bit/s = 8.68us
一般来说,超过3-5倍的“发送1bit所需要的时间”即为超时 满打满算,我们算10倍,就是86us 。
后续2
为什么先初始化dma,后初始化串口
参考 https://blog.csdn.net/Anchrx/article/details/118657397
链接: https://blog.csdn.net/Anchrx/article/details/118657397