stm32 usart 单线半双工串口 DMA发送 最后一个字节发不出来问题
生活随笔
收集整理的這篇文章主要介紹了
stm32 usart 单线半双工串口 DMA发送 最后一个字节发不出来问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近遇到一個小問題,感覺很有意思便記下來分享一下 ,順便也為日后類似的問題提供點思路:
使用stm32串口發送數據 ,串口是單線半雙工模式,要求數據發送前串口變成發送模式,發送完后立即變回接收模式,發送操作使用DMA來發送,程序開了串口中斷和DMA中斷,串口中斷主要是用于接收與解析數據,DMA中斷是想用于操作串口收發模式切換,發現DMA發送時最后一個數據老是發不出來。
經過分析定位發現是因為DMA將傳輸完成后,串口其實還沒有將所有的問題發送出去(至少還有一個字節沒發出去),然后修改操串口收發模式切換時間點將該問題解決,具體為:串口在DMA傳輸前由接收模式變成發送模式,在DMA傳輸完成中斷中開啟串口發送完成中斷,在串口發送完成中斷中將串口工作模式從發送模式變回接收模式,問題完解決,相關配置代碼如下:
串口配置:
void Usart_Init( void ) {USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;//GPIO clock enableRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // USART1時鐘使能//USART1_TXGPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 復用推挽輸出GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);//USART1_RXGPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空輸入GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);USART_InitStructure.USART_BaudRate = 115200; // 串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一個停止位USART_InitStructure.USART_Parity = USART_Parity_No; // 無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收發模式USART_Init(USART1, &USART_InitStructure); // 初始化串口1USART_HalfDuplexCmd(USART1, ENABLE); // 使能變雙工DMA_Usart_Init();NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // USART1中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;// 搶占優先級2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子優先級1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能NVIC_Init(&NVIC_InitStructure); // 根據指定的參數初始化NVIC寄存器USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟串口1接收中斷USART_Cmd(USART1, ENABLE); // 使能串口1USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 開啟串口USART1的DMA發送}DMA配置:
void DMA_Usart_Init(void) {DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //開啟DMA1時鐘DMA_Cmd(DMA1_Channel4, DISABLE);//關閉DMA1的通道4 Usart1_TX對應通道4DMA_DeInit(DMA1_Channel4);//恢復缺省值DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);//設置串口發送數據寄存器DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMATXBUFF1;//設置發送緩沖區首地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//設置外設位目標,內存緩沖區->外設寄存器DMA_InitStructure.DMA_BufferSize = 10;//需要發送的字節數,這里可以設置為0,因為在實際要發送時還會重新設置DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設地址不做增加調整,調整不調整是DMA自動實現的DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//內存緩沖區地址增加調整DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外設數據寬度8位,1個字節DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//內存數據寬度8位,1個字節DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//單次傳輸模式DMA_InitStructure.DMA_Priority = DMA_Priority_Low;//優先級配置DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//關閉內存到內存的DMA模式DMA_Init(DMA1_Channel4, &DMA_InitStructure);//寫入配置NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;//DMA1中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ;//搶占優先級2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子優先級1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化NVIC寄存器DMA_ClearFlag(DMA1_FLAG_GL4);//清除DMA所有標志 GL4:通道4全局標志 TC4:傳輸完成 HT4:傳輸過半 TE4:傳輸錯誤DMA_Cmd(DMA1_Channel4, DISABLE);//關閉DMA1的通道4DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);//開啟發送DMA通道中斷}串口中斷:
void USART1_IRQHandler(void) {if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // 接收數據寄存器非空標志{if(RxNum1 > (RXBUFFSIZE1 - 1)){RxNum1 = 0;}RXBUFF1[RxNum1++] = USART_ReceiveData(USART1);}else if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)// 發送完成標志{USART_ITConfig(USART1, USART_IT_TC, DISABLE);USART_ClearFlag(USART1,USART_FLAG_TC);USART1_RX_EN_TX_DIS();}else{}}?
DMA中斷:
void DMA1_Channel4_IRQHandler(void) {if(DMA_GetITStatus(DMA1_FLAG_TC4) != RESET) // DMA1通道4發送完成標志{Usart1TxBusy = 0;DMA_Cmd(DMA1_Channel4, DISABLE); // 關閉DMA1的通道4DMA_ClearFlag(DMA1_FLAG_GL4); // 清除DMA1的通道4的所有中斷標志,因為只有發送完成標志,所以直接全部清除USART_ClearFlag(USART1,USART_FLAG_TC); // 在關中斷前先清中斷標志,防止是串口上一個數據發送時的中斷標志USART_ITConfig(USART1, USART_IT_TC, ENABLE);// DMA1傳輸完成后使能Usart1發送完成中斷} }?
總結
以上是生活随笔為你收集整理的stm32 usart 单线半双工串口 DMA发送 最后一个字节发不出来问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一起学nRF51xx 20 - 移植S
- 下一篇: 一起学nRF51xx 21 - 蓝牙项