stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

这篇具有很好参考价值的文章主要介绍了stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作用:

空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。空闲中断的优点在于省去了帧头帧尾的检测,进入中断程序即意味着已经接收到一组完整数据,仅需及时对数据处理或将数据转移出缓冲区即可。

串口空闲中断在串口无数据接收的情况下,是不会产生的,产生的条件是当清除空闲标志位后,必须有接收到第一个数据后,才开始触发,一旦接收的数据断流,没有接收到数据,即产生空闲中断

简单说:

不用频繁进中断,省cpu力气

有些地方没写完,后续补上,里边操作系统是freertos,这个不是必须的

串口初始化

注意点:

一定要有串口电路,TTL转串口电路,串口接线正常

保证驱动CH340已安装

初始化完成清空串口缓冲区,保证无初始化乱码

初始化保证清空一下中断或者其他标志位,避免触发

中断优先级尽量低一点,避免影响其他业务

windows回车记住是\r\n

时钟和gpio保证初始化正确

#define DEBUG_USART                        USART3
#define DEBUG_USART_TX_AF                  GPIO_AF_USART3
#define DEBUG_USART_RX_AF                  GPIO_AF_USART3
#define DEBUG_USART_CLK_EN()               RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE)
#define DEBUG_USART_TX_CLK_EN()            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE)        
#define DEBUG_USART_RX_CLK_EN()            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE)
#define DEBUG_USART_IRQn                   USART3_IRQn
#define ENTER_PRI                          '\r'
#define IS_TIME_OUT(x) while((x)) {}
#define IRQ_DEBUG_IDE           (5)

/**
 * @description: debug 初始化结构体
 * @detail: 
 * @param {ULONG} ulBound
 * @return {*}
 * @author: lkc
 */
STATIC VOID Bsp_Debug_InitType(ULONG ulBound)
{
    DEBUG_USART_CLK_EN();
    DEBUG_USART_TX_CLK_EN();
    DEBUG_USART_RX_CLK_EN();

    USART_InitTypeDef USART_InitStructure;

    USART_InitStructure.USART_BaudRate = ulBound;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    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(DEBUG_USART, &USART_InitStructure); 

    USART_Cmd(DEBUG_USART, ENABLE);

    USART_ClearFlag(DEBUG_USART, USART_FLAG_TC);

    /* 清空发送缓冲区 */
    while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TC) == RESET)
    {
        USART_ReceiveData(DEBUG_USART);
    }

    return;
}

/**
 * @description: debug 中断
 * @detail: 
 * @return {*}
 * @author: lkc
 */
STATIC VOID Bsp_Debug_Nvic(VOID)
{
    /* 空闲中断使用命令行 */
    NVIC_InitTypeDef NVIC_InitStre;
    NVIC_InitStre.NVIC_IRQChannel = DEBUG_USART_IRQn;
    NVIC_InitStre.NVIC_IRQChannelPreemptionPriority = IRQ_DEBUG_IDE;
    NVIC_InitStre.NVIC_IRQChannelSubPriority = 0x0;
    NVIC_InitStre.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStre);

    /* 空闲中断,接收到一连串数据触发一次 */
    USART_ITConfig(DEBUG_USART, USART_IT_IDLE, ENABLE);
    USART_ClearITPendingBit(DEBUG_USART, USART_IT_IDLE);

    return;
}

/**
 * @Description: 串口初始化
 * @author: lkc
 * @Date: 2023-02-17 23:06:43
 * @param {int} baud
 * @return {*}
 */
VOID Bsp_Usart_DebugInit(ULONG ulBound)
{
    Bsp_Debug_InitType(ulBound);
    Bsp_Debug_Nvic();

    return;
}

gpio初始化

注意:

  1. pin和source不是一个东西

  1. source复用的时候不能和pin一样直接全部与|上复用

#define DEBUG_PORT              DEBUG_USART_TX_GPIO_PORT
#define DEBUG_PIN               DEBUG_USART_TX_PIN | DEBUG_USART_RX_PIN 
#define DEBUG_USART_TX_SOURCE              GPIO_PinSource10
#define DEBUG_USART_RX_SOURCE              GPIO_PinSource11
/**
 * @description: 输出模式 推挽
 * @detail: 
 * @param {GPIO_TypeDef*} GPIOx
 * @param {GPIO_InitTypeDef*} GPIO_InitStruct
 * @return {*}
 * @author: lkc
 */
VOID Bsp_Gpio_ModeAF(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin, uint16_t GPIO_PinSource, uint8_t GPIO_AF)
{
    /* gpio时钟默认前边初始化 */

    GPIO_InitTypeDef  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    /* 定时器复用使用浮空.其他复用未知 */
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOx, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF);

    return;
}
/**
 * @description: 
 * @detail: 
 * @return {*}
 * @author: lkc
 */
VOID Bsp_Usart3_Gpio(VOID)
{
    // TODO 复用source不能| ,PIN 和 PIN SOURCE有区别,一个是引脚 一个是复用资源
    Bsp_Gpio_ModeAF(DEBUG_PORT, DEBUG_PIN, DEBUG_USART_TX_SOURCE, GPIO_AF_USART3);
    Bsp_Gpio_ModeAF(DEBUG_PORT, DEBUG_PIN, DEBUG_USART_RX_SOURCE, GPIO_AF_USART3);

    return;
}
/**
 * @description: GPIO初始化
 * @detail: 
 * @return {*}
 * @author: lkc
 */
void Bsp_Gpio_Init(void)
{
    /* 初始化时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /* 串口gpio */
    Bsp_Usart3_Gpio();

    return;
}

DMA初始化

注意:

USART_DMACmd一定要指定串口

/* 串口接收中断 */
UCHAR gucURxBuf[USART_RX_BUF_SIZE] = {0};
/**
 * @description: dma1 初始化
 * @detail: 
 * @return {*}
 * @author: lkc
 */
VOID Bsp_Dma1_Init(VOID)
{
    /* 每个dma数据流和通道对应不同的外设 */
    DMA_DeInit(DMA1_Stream1);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

    DMA_InitTypeDef DMA_InitStructure;
    while (DMA_GetCmdStatus(DMA1_Stream1) != DISABLE){} /* 检测dma是否之前配置过 */
    DMA_InitStructure.DMA_Channel = DMA_Channel_4; 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(DEBUG_USART->DR); /* 外设地址 */
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)gucURxBuf; /* 内存地址 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; /* 外设到内存 */
    DMA_InitStructure.DMA_BufferSize = (uint32_t)32; /* 一次性传输大小 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* 外设地址不变 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* memory地址自增 */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; /* 外设地址数据单位 */
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /* memory地址数据单位 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; /* 注意! 这里一定要使用循环模式,否则收到一包之后就停止了 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* 优先级 */
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;       
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; /* 存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。*/
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; /* 外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。 */
    DMA_Init(DMA1_Stream1, &DMA_InitStructure); 

    return;
}

/**
 * @description: 串口接受dma
 * @detail: 
 * @return {*}
 * @author: lkc
 */
VOID Bsp_Dma_UsartRx(VOID)
{
    Bsp_Dma1_Init();

    DMA_SetCurrDataCounter(DMA1_Stream1, USART_RX_BUF_SIZE);
    DMA_Cmd(DMA1_Stream1, ENABLE);

    // TODO 注意 如果是使能dma一定要加这个
    USART_DMACmd(DEBUG_USART, USART_DMAReq_Rx, ENABLE);

    return;
}

串口中断

注意点:

/**
 * @description: 串口3中断
 * @detail: 
 * @return {*}
 * @author: lkc
 */
extern QueueHandle_t queueCmdHandle;
ULONG ulRxLen = 0;
VOID Bsp_Usart_Handler(VOID)
{
    if (RESET != USART_GetITStatus(DEBUG_USART, USART_IT_IDLE))
    {
        /* 个人认为是清空缓冲,取出数据 */
        DEBUG_USART->SR;
        DEBUG_USART->DR;

        /* 清空空闲中断 */
        USART_ClearITPendingBit(DEBUG_USART, USART_IT_IDLE);
        /* 关闭dma */
        DMA_Cmd(DMA1_Stream1, DISABLE);
        /* 本帧数据长度=DMA准备的接收数据长度-DMA已接收数据长度 */
        ulRxLen = USART_RX_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Stream1); 

        /* 此时知道接收的长度以及Buf,直接传一个队列 */
        osMessageQueuePut(queueCmdHandle, &ulRxLen, NULL, 0);
        DMA_ClearFlag(DMA1_Stream1, DMA_FLAG_TCIF4 | DMA_FLAG_FEIF4 | DMA_FLAG_DMEIF4 |
                DMA_FLAG_TEIF4 | DMA_FLAG_HTIF4);//清除DMA2_Steam7传输完成标志
        IS_TIME_OUT(DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);
        
        DMA_SetCurrDataCounter(DMA1_Stream1, USART_RX_BUF_SIZE);
        DMA_Cmd(DMA1_Stream1, ENABLE);
    }
    
    return;
}

/**
 * @description: 串口3中断,用于命令行
 * @detail: 
 * @return {*}
 * @author: lkc
 */
void USART3_IRQHandler( void )
{
    Bsp_Usart_Handler();
    return;
}

处理传递字符串

注意点文章来源地址https://www.toymoban.com/news/detail-774116.html

/**
 * @description: 命令行任务
 * @detail: 
 * @return {*}
 * @author: lkc
 */
extern Shell shell;
void Cmd_Task(void *argument)
{
    PRINTF("Cmd Task Init\r\n");
    userShellInit();

    STATIC ULONG ulRxDataLen;
    ULONG i = 0;
    /* 创建队列用于接收DMA */
    queueCmdHandle = osMessageQueueNew(CMD_QUEUE_LENGTH, CMD_QUEUE_SIZE, NULL);

    while (1)
    {
        /* 等待队列 */
        osMessageQueueGet(queueCmdHandle, &ulRxDataLen, NULL, portMAX_DELAY);

        for (i = 0; i < ulRxDataLen; i++)
        {
            /* 这个地方为调用字符处理的地方 */
            shellHandler(&shell, gucURxBuf[i]);
        }
    }
}

到了这里,关于stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据

    问题:类似K线与蓝牙接收模块,要求由原来的接收串口中断改为DMA接收。据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一个数据(比如1BYTE)就会进入空闲中断(k线发现这种情况),考虑到这样进入中断的频率和以前串口接收中断的频率差不多,所以放弃此方案,

    2024年02月09日
    浏览(20)
  • STM32基于HAL库的串口接受中断和空闲中断

    在通信方面。UART由于全双工通信,可以同时接受数据和发送数据而被广泛使用。 而接受数据则又有很多种方法 比如: 1根据结束符判断,数据是字符串形式,所以一般串口接受的接受符就是 \\\"rn\\\"  换成16进制ascil码显示就是 0X0D   0X0A (对应rn)  2定时器中断,设计\\\"喂狗信号量

    2023年04月08日
    浏览(20)
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况

    2024年01月23日
    浏览(25)
  • STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据

           在STM32每个串口的内部都有两个寄存器:发送数据寄存器(TDR)/发送移位寄存器,当我们调用HAL_UART_Transmit 把数据发送出去时,CPU会将数据依次将数据发送到数据寄存器中,移位寄存器中的数据会根据我们设置的比特率传化成高低电平从TX引脚输出。待发送移位寄存器中发

    2024年02月07日
    浏览(21)
  • 嵌入式开发--STM32用DMA+IDLE中断方式串口接收不定长数据

    之前讲过用 利用IDLE空闲中断来接收不定长数据 ,但是没有用到DMA,其实用DMA会更加的高效,MCU也可以腾出更多的性能去处理应该做的事情。 IDLE顾名思义,就是空闲的意思,即当监测到串口空闲超过1个串口的数据帧时,会使状态寄存器(SR或ISR)的IDLE位置位,如果此时控制

    2024年04月17日
    浏览(32)
  • STM32使用DMA传输UART空闲中断中接收的数据遇到的问题以及解决方法

    STM32使用DMA传输UART空闲中断中接收的数据遇到的问题以及解决方法 CubeMX配置 串口配置:使用默认配置(传输数据长度为8 Bit,奇偶检验无,停止位为1 Bit, 接收和发送都使能),因为我的是LIN项目所以使用的时串口的LIN模式,一般就是异步通信 打开DMA传输 打开串口接收中断

    2024年02月05日
    浏览(21)
  • STM32 cubemx+串口空闲中断+DMA双缓冲

            写这篇文章是为了记录下之前做过的项目中用到的一部分关键技术,之前做过的项目中涉及到 采用最小开销来实时接收遥控器数据、能够准确验证传输过来数据的准确性 ,减小误差率,要求能稳定适用于不同的环境。 目录 1、为什么要用到串口空闲中断? 2、为

    2024年02月09日
    浏览(18)
  • 关于STM32用DMA传输UART空闲中断中接收的数据时无法接收数据问题以及解决办法

             串口1相关的设置及printf函数的使用,这里没放,建议先实现串口打印功能 可以参考:使用STM32 CUBE IDE配置STM32F7 用DMA传输多通道ADC数据_stm32cubeide 配置adc_一只小白啊的博客-CSDN博客         普通模式和循环模式的区别在于,普通模式下,DMA只会接收一次数据,

    2024年02月05日
    浏览(25)
  • STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    能够点进这篇文章的小伙伴肯定是对STM32串口DMA空闲中断接收数据感兴趣的啦,今天用这一功能实现串口解析航模遥控器sbus信号时,查阅了很多网友发布的文章(勤劳的搬运工~),包括自己之前写过一篇博客 STM32_HAL库_CubeMx串口DMA通信(DMA发送+DMA空闲接收不定长数据)。本文

    2024年02月09日
    浏览(27)
  • GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

    前言 串口功能在单片机开发中,是比较常用的外设,熟练使用串口功能也是驱动开发必备的技能之一。 DMA 是一种CPU辅助手段,可以在CPU不参与的情况下,是做一些辅助CPU的事情,如通常的数据搬运。 在没有DMA之前,数据读取时,需要CPU的处理,在多任务处理时,增加资源紧

    2023年04月13日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包