本例程在TM4C123GH6PM单片机上,模拟STM32的DMA+IDLE中断,实现串口的接收功能,已在EK-TM4C123GXL评估板上验证通过。
原理如下:1、串口0通过DMA的pingpang模式接收,DMA的transfer size为1字节,即每个字节都会触发DMA中断。
2、串口使能DMA时,无法正常使用ReceiveTimeOut中断,所以本例程使用Timer0来实现“接收超时中断”,也即STM32的IDLE中断。
3、串口发送也是通过DMA,DMA的transfer size也是为1字节。
#include串口调试助手截图如下:#include #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_uart.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/pin_map.h" #include "driverlib/rom.h" #include "driverlib/sysctl.h" #include "driverlib/uart.h" #include "driverlib/udma.h" #include "driverlib/timer.h" #include "string.h" //***************************************************************************** // The error routine that is called if the driver library encounters an error. //***************************************************************************** #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line){} #endif #if defined(ewarm) #pragma data_alignment=1024 uint8_t ui8ControlTable[1024]; #elif defined(ccs) #pragma DATA_ALIGN(ui8ControlTable, 1024) uint8_t ui8ControlTable[1024]; #else uint8_t ui8ControlTable[1024] __attribute__ ((aligned(1024))); #endif #define UART_RXBUF_SIZE 64 #define UART_TXBUF_SIZE 64 #define DMA_TRANS_SIZE 1 static char g_ui8RxBuf[UART_RXBUF_SIZE]; static char g_ui8TxBuf[UART_TXBUF_SIZE]; static uint32_t g_ui32RxPriCnt = 0; static uint32_t g_ui32RxAltCnt = 0; static uint8_t g_ui8RxDoneFlag = 0; static uint8_t g_ui8TxDoneFlag = 0; static uint8_t g_ui8TxBeginFlag = 0; static uint32_t g_ui32uDMAErrCount = 0; static uint32_t g_ui32TimerCount = 0; void uDMAErrorHandler(void) { uint32_t ui32Status; ui32Status = ROM_uDMAErrorStatusGet(); if(ui32Status) { ROM_uDMAErrorStatusClear(); g_ui32uDMAErrCount++; } } void UARTIntHandler(void) { uint32_t ui32Status; uint32_t ui32Mode; ui32Status = ROM_UARTIntStatus(UART0_BASE, true); ROM_UARTIntClear(UART0_BASE, ui32Status); ui32Mode = ROM_uDMAChannelModeGet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT); if(ui32Mode == UDMA_MODE_STOP) { g_ui32RxPriCnt++; ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf + 2*g_ui32RxPriCnt, DMA_TRANS_SIZE); // buffer只有64字节,这里限制一下,防止溢出 if (g_ui32RxPriCnt > 30) { g_ui32RxPriCnt = 30; } } ui32Mode = ROM_uDMAChannelModeGet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT); if(ui32Mode == UDMA_MODE_STOP) { g_ui32RxAltCnt++; ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf+1 + 2*g_ui32RxAltCnt, DMA_TRANS_SIZE); if (g_ui32RxAltCnt > 30) { g_ui32RxAltCnt = 30; } } if( (ui32Status&UART_INT_TX) == UART_INT_TX ){ ROM_uDMAChannelDisable(UDMA_CH9_UART0TX); ROM_UARTDMADisable(UART0_BASE, UART_DMA_TX); ROM_UARTIntDisable(UART0_BASE, UART_INT_TX); g_ui8TxDoneFlag = 1; return; } // 重新开始计时 ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, 1400); ROM_TimerEnable(TIMER0_BASE, TIMER_A); } // 用作接收超时中断 void Timer0IntHandler(void) { ROM_TimerDisable(TIMER0_BASE, TIMER_A); ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); ROM_IntMasterDisable(); g_ui8RxDoneFlag = 1; g_ui32TimerCount++; ROM_IntMasterEnable(); } void UARTDMAInit(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); ROM_uDMAEnable(); ROM_uDMAControlBaseSet(ui8ControlTable); ROM_GPIOPinConfigure(GPIO_PA0_U0RX); ROM_GPIOPinConfigure(GPIO_PA1_U0TX); ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1); ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE)); // 1. 不使用FIFO,即FIFO深度为1字节。 // ROM_UARTFIFODisable(UART0_BASE); // 2. RX使用14个字节的FIFO(不存在UART_FIFO_TX8_8的设置项,最高只能设置14字节) // 由于DMA_TRANS_SIZE为1字节,所以正常是用不上多余的FIFO的; // 但是特殊情况下,如果传输速度超过DMA中断的处理能力, // FIFO就可以多缓存14个字节,一定程度上能避免数据丢失。 ROM_UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX7_8); ROM_UARTFIFOEnable(UART0_BASE); ROM_UARTEnable(UART0_BASE); ROM_UARTDMAEnable(UART0_BASE, UART_DMA_RX | UART_DMA_TX); ROM_IntMasterEnable(); ROM_IntEnable(INT_UART0); // 使用DMA时,RT中断无法正常使用,所以用Timer0代替RT中断 // ROM_UARTIntEnable(UART0_BASE, UART_INT_RT); // DMA_RX配置 ROM_uDMAChannelAssign(UDMA_CH8_UART0RX); ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0RX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1); ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1); ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf, DMA_TRANS_SIZE); ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf+1, DMA_TRANS_SIZE); ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_UART0RX, UDMA_ATTR_HIGH_PRIORITY); ROM_uDMAChannelEnable(UDMA_CH8_UART0RX); } void TimerInit(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT); // 16MHz,115200bps,大约1byte的时间,超时就会中断 ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, 1400); ROM_IntEnable(INT_TIMER0A); ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); } void UARTDMATx(const char *pui8Buffer, uint32_t ui32Count){ // 发送结束中断,EndOfTransfer ROM_UARTTxIntModeSet(UART0_BASE, UART_TXINT_MODE_EOT); ROM_UARTIntEnable(UART0_BASE, UART_INT_TX); ROM_UARTDMAEnable(UART0_BASE, UART_DMA_TX); ROM_uDMAChannelAssign(UDMA_CH9_UART0TX); ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1); ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, (void *)pui8Buffer, (void *)(UART0_BASE + UART_O_DR), ui32Count); ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_UART0TX, UDMA_ATTR_HIGH_PRIORITY); ROM_uDMAChannelEnable(UDMA_CH9_UART0TX); } void UARTCmdDeal(){ if (g_ui8RxDoneFlag == 0 || g_ui8TxBeginFlag == 1) return; int8_t i8frameLen = (g_ui32RxAltCnt + g_ui32RxPriCnt) < UART_RXBUF_SIZE ? (g_ui32RxAltCnt + g_ui32RxPriCnt) : UART_RXBUF_SIZE; memset(g_ui8TxBuf, 0, UART_TXBUF_SIZE); strncpy(g_ui8TxBuf, g_ui8RxBuf, i8frameLen); UARTDMATx(g_ui8TxBuf, strlen(g_ui8TxBuf)); g_ui8TxBeginFlag = 1; // 等待发送结束,这里最好改成非阻塞的等待方式 while (g_ui8TxDoneFlag == 0); } void UARTDMARestore(){ if(g_ui8TxDoneFlag == 0) return; g_ui8TxBeginFlag = 0; g_ui8TxDoneFlag = 0; g_ui8RxDoneFlag = 0; g_ui32RxPriCnt = 0; g_ui32RxAltCnt = 0; ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf, DMA_TRANS_SIZE); ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(UART0_BASE + UART_O_DR), g_ui8RxBuf+1, DMA_TRANS_SIZE); // 如果收到的指令是奇数个字节,则alternate control structure会被选中, // 那么接下来的指令就会从g_ui8RxBuf+1的位置开始放置。 // 因此需要禁止UDMA_ATTR_ALTSELECT,让primary control structure被选中。 ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0RX, UDMA_ATTR_ALTSELECT); } int main(void) { // 外部16MHz的晶振 ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // 使用内部16MHz的时钟源,经过倍频后,是80MHz,也单片机支持的最高频率 // 使用这个频率的话,Timer0的中断计数要改为6944,大约1byte的时长 // ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_INT); UARTDMAInit(); TimerInit(); while(1) { UARTCmdDeal(); UARTDMARestore(); } }
截图中红色下划线标注响应内容。
由截图可见,本例程可以在1ms内完成响应,而且没有任何丢包。



