栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

TM4C123GH6PM 串口0+DMA+接受超时中断

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

TM4C123GH6PM 串口0+DMA+接受超时中断

本例程在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内完成响应,而且没有任何丢包。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/879132.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号