IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> TM4C123GH6PM 串口0+DMA+接收超时中断 -> 正文阅读

[嵌入式]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 <stdint.h>
#include <stdbool.h>
#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内完成响应,而且没有任何丢包。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:49:03  更:2022-05-18 17:49:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 0:46:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码