stm32串口DMA方式发送数据

2023-05-16

该文档介绍stm32 uart1通过DMA方式发送和接收数据,代码示例基于ucos-ii操作系统。

该文档参考https://wenku.baidu.com/view/d44ef1380975f46526d3e1b5.html 中内容,如有侵权,请告知删除!

  • DMA发送数据

    • 在数据发送缓冲区内放好要发送的数据(此数据缓冲区的首地址必须要在DMA初始化时写入到DMA配置中去)
    • 将数据缓冲区内要发送的数据字节数传给DMA通道(串口发送和接收不是同一个通道)
    • 开启DMA,一旦开启,则DMA开始发送数据,
    • 等待数据发送完成标志!
    • 判断数据发送完成:
      • 启动DMA并发送完成后,产生DMA发送完成中断,在DMA中断服务函数中执行以下操作:
        1. 清DMA发送完成标志
        2. 关闭串口发送DMA通道
        3. 给前台(应用)程序设置一个软件标志位,说明数据发送完成。
  • DMA接收数据
    串口接收DMA在初始化时就处于开启状态,一直等待数据的到来,串口中断IDLE在串口一直没有数据时,是不会产生的,产生的条件是:当清除IDLE标志位后,必须有接收到第一个数据后才触发,一旦接收的数据断流,即产生IDLE中断。
    这里判断接收数据完成是通过串口的空闲方式实现,即当串口的数据流停止后,就会产生IDLE中断,在中断里做

    • 关闭串口接收的DMA通道。一是防止又有数据接收到,产生干扰;二是便于DMA重新配置赋值。
    • 清除DMA中断标志位
    • 从DMA寄存器中获取接收到的数据字节数(对于上层应用很有必要)
    • 重新设置DMA下次需要接收到的数据字节数(必须大于预期的接收值长度,否则计数减到0时又会复位,覆盖接收缓冲区中的数据,导致数据丢失!)
    • 开启DMA通道,等待下一次的数据接收
    • 可以设置信号量,通知应用程序数据接收完成,传递接收的数据长度,便于应用程序对数据的处理。

有待思考的
!!! 此处的接收缓冲区时来自哪里? uart1的数据缓冲区,还是DMA的接收数据缓冲区,最大支持多少个字节???

!!! DMA发送数据,网上也有看到将配置封装在发送函数中,如
uart1_dma_send_data(uint8_t* buf, uint32_t len),这个好处是,可以变化mem地址(buf数组,以及长度可以适当配置变化),不错!

!!! DMA接收函数,应用DMA中断判断接收完成只能依赖于DMA配置中字节长度,支持DMA传输完成,传输过半,传输错误,但对于不固定长度接收数据,this is a question.
注意:

  • DMA外设和DMA通道有对应关系,需要参考stm32手册
    这里写图片描述
    这里写图片描述
  • DMA传输数据时,需要明确传输的字节数目
  • DMA使能情况下,不能配置传输字节数到DMA寄存器,所以也不会产生DMA数据传输(因为传输字节数目为0)

    1. hal_uart_dma.c
#include "includes.h"
/*
    DMA方式介绍:

    DMA使用流程:
    1. 配置
        外设端:
            - 串口引脚GPIO配置
            - 串口功能参数配置(数据位格式,波特率等),此处还需要**配置对应的DMA请求允许**。
            - 如果有中断,还需要配置中断优先级
        DMA端:
            - DMA功能配置
                - 初始化DMA通道DMA_Init(DMA1_Channel4,&DMA_InitStructure);
                。 DMA源(Memory)/目的(外设)地址
                。 DMA的传输方向
                。 DMA的buffer size
                。 DMA外设(DISABLE)/Memory(ENABLE)地址自增使能配置
                。 DMA传输字节格式(支持byte,half-word,word)
                。 DMA传输方式(Normal和Circle)
                。 DMA传输优先级(共有四种,)
                。 DMA m2m使能/失能配置(此处不是用于m2m所以配置为DISABLE)
                - 清除中断标志位
                - 此处应DISABLE DMA通道,否则配置完成即会产生DMA数据传输(非期望数据)
                - 使能DMA发送完成中断

            - DMA中断服务函数
                发送数据完成后,即关闭DMA通道,发送信号量到应用程序

外设部分配置
    2. 使能(使用)
        应用程序需要发送数据时:
        - 将要发送的数据准备好,并且要知道发送多少单位数据(发送字节数)
        - 配置DMA要发送的字节数,使能DMA即可.
*/


//#define DMA_USART1_DR_Base      (USART1_BASE + 0x4) //0x40013804
#define DMA_USART1_DR_Base          0x40013804

//1. 串口1端口配置
void hal_debug_gpio_config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    //DBTX  PA9  uart1_tx
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    //DBRX  PA10   uart1_rx
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA,&GPIO_InitStructure);

}
//2. 串口功能配置
void hal_debug_func_config(void)
{
    USART_InitTypeDef  USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_Init(USART1,&USART_InitStructure);
    //config uart DMA request
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//enable usart1 dma send request
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //enable usart1 dma recieve request
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //enable usart1 idle interrupt
    USART_Cmd(USART1,ENABLE);
}
//
void hal_debug_nvic_config(void){
    NVIC_InitTypeDef  NVIC_InitStructure;
    //uart1 interrupt
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    //dma interrupt
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;   //
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     //
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);
}

//3. 串口初始化
void hal_debug_init(void)
{
    hal_debug_gpio_config();

    hal_debug_func_config();

    hal_debug_nvic_config();

}
//////////////////////DMA config/////////////////////////////////
//1. uart dma configs
void uart_dma_init(void){
    DMA_InitTypeDef DMA_InitStructure;

    //Tx DMA CONFIG
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //enable DMA1 clock
    DMA_Cmd(DMA1_Channel4,DISABLE);                                     //close DMA Channel
    DMA_DeInit(DMA1_Channel4);

    DMA_InitStructure.DMA_PeripheralBaseAddr = DMA_USART1_DR_Base;  //(uint32_t)(&USART1->DR)
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Tx_Buf;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = USART1_TX_BSIZE; 
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 

    DMA_Init(DMA1_Channel4,&DMA_InitStructure);
    DMA_ClearFlag(DMA1_FLAG_GL4);  // clear all DMA flags
    //DMA_Cmd(DMA1_Channel4,ENABLE); // close DMA Channel
    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //open DMA send inttrupt

    //Rx DMA CONFIG 
    DMA_Cmd(DMA1_Channel5, DISABLE);   //                       
    DMA_DeInit(DMA1_Channel5);  

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Rx_Buf;        
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                    
    DMA_InitStructure.DMA_BufferSize = USART1_RX_BSIZE;                     
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; 
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;               
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                           

    DMA_Init(DMA1_Channel5, &DMA_InitStructure);            
    DMA_ClearFlag(DMA1_FLAG_GL5);                              
    DMA_Cmd(DMA1_Channel5, ENABLE);  
}
///////////////////////uart dma send//////////////////////
void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_FLAG_TC4)==SET)
    {
            DMA_ClearFlag(DMA1_FLAG_GL4);        
            DMA_Cmd(DMA1_Channel4, DISABLE);  
            OSMboxPost(mbLumModule_Tx, (void*)1); 
    }
}
void uart_dma_send_enable(uint16_t size)
{
    DMA1_Channel4->CNDTR = (uint16_t)size; 
    DMA_Cmd(DMA1_Channel4, ENABLE);       
}

void uart1_dma_send_data(void)
{
    uint8_t err;
        uint16_t i;
    uint16_t USART1_Tx_Index=0;
        for(i=0;i<8;i++){
            USART1_Tx_Buf[USART1_Tx_Index++]=i;
        }
    uart_dma_send_enable(USART1_Tx_Index);
    OSMboxPend(mbLumModule_Tx, 5000, &err);
}
///////////////////////uart dma send//////////////////////

///////////////////////uart dma recv//////////////////////
void uart1_dma_recv_data(void)
{
    uint16_t index = 0;
    DMA_Cmd(DMA1_Channel5, DISABLE);      
    DMA_ClearFlag(DMA1_FLAG_GL5);          
    //index = USART1_RX_BSIZE - DMA_GetCurrDataCounter(DMA1_Channel5); 
    DMA1_Channel5->CNDTR = USART1_RX_BSIZE;   
    DMA_Cmd(DMA1_Channel5, ENABLE);       

    //OSMboxPost(mbLumModule_Rx,USART1_Rx_Buf);
}
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)  
    {
        uart1_dma_recv_data();
        USART_ClearITPendingBit(USART1,USART_IT_IDLE);      
    }
}
///////////////////////uart dma recv//////////////////////

2. app_test.c
该文件为app_test线程,用于1s发送一次串口数据,其中发送串口数据中应用了信号量,如果没有等待到信号量,最大等待时间为5s(加上此处的1s周期则为6s)。

#include "includes.h"

OS_STK gTest[APP_TEST_STK_SIZE];

uint8_t USART1_Tx_Buf[USART1_TX_BSIZE] = {0};
OS_EVENT *gSemEvent1 = NULL;
OS_EVENT *mbLumModule_Tx = NULL;

void app_test(void* p_arg){
    (void)p_arg;
    //create new event
    gSemEvent1 = OSSemCreate(2);
    mbLumModule_Tx = OSMboxCreate((void*)0);
    while(1){
        //OSSemPost(gSemEvent1); 
        OSTimeDly(1000);
        //printf("hello world!\r\n");
        uart1_dma_send_data();
    }
}
  1. ucos_main.c

该函数用于创建ucos任务,一个main函数,一个创建任务的任务函数。
其中app_test任务就是这边uart DMA发送数据的任务。

#include "includes.h"

/*
    1. 主函数
         用于启动ucos-ii操作系统,启动第一个启动任务
    2. 启动任务启动其他的任务
*/
static OS_STK gTaskStartStk[APP_TASK_START_STK_SIZE];               //定义栈 
static void App_TaskStart(void *p_arg);
static void app_task_create (void);

//1. main
int main(void){
    INT8U  os_err;

    OSInit();   
    os_err = OSTaskCreateExt((void (*)(void *)) App_TaskStart,  /* Create the start task.                               */
                             (void          * ) 0,
                             (OS_STK        * )&gTaskStartStk[APP_TASK_START_STK_SIZE - 1],
                             (INT8U           ) APP_TASK_START_PRIO,
                             (INT16U          ) APP_TASK_START_PRIO,
                             (OS_STK        * )&gTaskStartStk[0],
                             (INT32U          ) APP_TASK_START_STK_SIZE,
                             (void          * )0,
                             (INT16U          )(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));
#if (OS_TASK_NAME_SIZE >= 11)
    OSTaskNameSet(APP_TASK_START_PRIO, (INT8U *)"Start Task", &os_err);
#endif

    OSStart();  
}       
//2. start task
static void App_TaskStart(void *p_arg)
{       
    (void)p_arg;

    /*hal init*/
    hal_dirvers_init();

    //printf("uCos-II V2.86 FM.\r\n");

#if (OS_TASK_STAT_EN > 0)
  OSStatInit();                                         /* Determine CPU capacity.*/
#endif

    //printf("Create App Task.\r\n");   
    /*creat other app*/
    app_task_create();

    while(1){
        OSTimeDly(1000);
        hal_led_toggle(1);   //led_toggle   
    }   
}
/*
Create APP Task
*/
static void app_task_create (void){
    INT8U  os_err;

    //test task
    os_err = OSTaskCreate(app_test,
                                                (void *)0,
                                                &gTest[APP_TEST_STK_SIZE-1],
                                                APP_TEST_PRIO);
    #if (OS_TASK_NAME_SIZE >= 20)
            OSTaskNameSet(APP_TEST_PRIO, (INT8U *)"test task", &os_err);    
    #endif

    //led toggle task
/*****************************************************************************************/ 
    os_err = OSTaskCreate((void (*)(void *))app_led_toggle,
                            (void*)0,
                                                (OS_STK*)&gTaskLedToggle[APP_TASK_LED_STK_SIZE-1],
                                                (INT8U)APP_TASK_LED_PRIO                                             
                         );
    #if (OS_TASK_NAME_SIZE >= 20)
    OSTaskNameSet(APP_TASK_LED_PRIO, (INT8U *)"led_toggle", &os_err);
  #endif
/*****************************************************************************************/                                             

#if 0
    //task 1
/*****************************************************************************************/ 
        os_err = OSTaskCreate(app_task1,
                                                    (void*)0,
                                                    &gTask1[APP_TASK1_STK_SIZE-1],
                                                    APP_TASK1_PRIO);                                                    

/*****************************************************************************************/                                                 
    //task 2
/*****************************************************************************************/ 
        os_err = OSTaskCreate(app_task2,
                                                    (void*)0,
                                                    &gTask2[APP_TASK2_STK_SIZE-1],
                                                    APP_TASK2_PRIO);                                            

/*****************************************************************************************/                                         
    //task 3
/*****************************************************************************************/ 
        os_err = OSTaskCreate(app_task3,
                                                    (void*)0,
                                                    &gTask3[APP_TASK3_STK_SIZE-1],
                                                    APP_TASK3_PRIO);
/*****************************************************************************************/                                                 
#endif
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32串口DMA方式发送数据 的相关文章

随机推荐

  • 字节和bit的大小端简介

    C语言的位域虽然很多人强烈建议不要使用 xff0c 但现有系统里还广泛存在位域的使用 xff0c 所以还是很有必要理清楚的 对big endian和little endian的区别 xff0c 很多人认为是对多字节数据类型而言 其实 xff
  • C++ 数字与char*的转换

    目录 1 数字转char 1 1 sprintf1 2 itoa1 3 ltoa ultoa 2 char 转数字2 1 atoi2 2 atol2 3 atof2 4 strtol2 5 strtoul2 6 strtod 1 数字转ch
  • 【C语言】printf输出16进制: %x %02x %#x

    x即按十六进制输出 xff0c 英文字母小写 xff0c 右对齐 02X有以下变化 xff1a 英文字母变大写 xff0c 如果输出字符不足两位的 xff0c 输出两位宽度 xff0c 右对齐 xff0c 空的一位补0 超过两位的 xff0
  • 【c语言】结构体初始化4中方法

    今天在6轴传感器的驱动代码源文件中看到结构体没见过的写法 xff1a typedef struct uint8 t xlda 1 uint8 t gda 1 uint8 t tda 1 uint8 t not used 01 5 lsm6d
  • 【debug】stm32 指针奇数地址问题导致HardFault

    很久很久没有记录过debug了 xff0c 今天有空就正好记录一下 嵌入式开发 xff0c stm32F407的MCU的开发板 遇到如下问题 xff1a 变量的地址竟然为奇数 xff01 xff01 xff01 导致程序运行到写入该地址的数
  • 【Autosar】学习总结-BSW层

    一 简介 AUTOSAR AUTomotive Open Systems ARchitecture xff0c 汽车开放系统架构 1 优势 xff1a 有利于提高软件复用度 xff0c 尤其是跨平台的复用度 xff1b 便于软件的交换与更新
  • 【PWM】从stm32到pwm到OLED屏幕调光到晚上不要玩手机

    一 前言 最近做项目 xff0c 配置了单片机中PWM波形输出 xff0c 配置单片机中的一个引脚输出PWM波 xff0c 示波器查看这个波形 xff0c 做了实践操作 xff0c 有一些感想 xff0c 将一些了解过的知识和常识结合 xf
  • 算法提高 高精度加法

    1051 算法提高 高精度加法 时间限制 1 Sec 内存限制 256 MB 提交 5 解决 2 提交 状态 讨论版 题目描述 在C C 43 43 语言中 xff0c 整型所能表示的范围一般为 231到231 xff08 大约21亿 xf
  • 【Autosar】学习总结-MCAL

    一 简介 MCAL xff1a 微控制器抽象层 xff1b 位于BSW层中的最下层 xff1b MCAL细分 xff0c 可将驱动分为 xff1a 微控制器驱动 存储器驱动 通信驱动 IO驱动 xff1a 二 MCAL的配置 xff08 E
  • 【2022】年度总结

    一 月报 xff1a 1 一月 二月 初入新公司 xff0c 还在试用期 xff1b 进的外包安卓手机升级项目 xff0c 是一个短期的项目 xff0c 3 4个月左右 xff1b 第一次了解到外包项目原来是这种模式 xff1a 建立黄区
  • 【笔试总结 网络】IP地址分类 划分子网 子网掩码 (相关例题分析)

    xff08 最近做了很多春招公司笔试卷子 xff0c 发现很多是学过的东西 xff0c 看着很熟悉 xff0c 就是不会 很多学过的概念模棱两可 xff0c 这在笔试中很吃亏 说不会吧会点 xff0c 说会吧 xff0c 做不出来 就比如关
  • 网页登录时密码如何传输?

    今天突发奇想想看下一般网站登录时密码是如何传输的 首先是QQMail xff0c gmail xff0c 各大网上银行等对于我非常重要的登录网站 xff1a https https的安全性自然是很高 其次是通常的腾讯微博以及其他腾讯常用的网
  • C/C++中简单数据结构的对齐

    这里只讨论简单的数据结构 xff0c 从MSDN上的例子开始 struct x char a 1 byte int b 4 bytes short c 2 bytes char d 1 byte 在没有对齐的情况下 xff0c x 中的所有
  • yolo数据增强以及批量修改图片和xml名

    记录下打完标签对数据集进行扩增 xff0c 数据增强后的图片及标签名字进行修改 xff0c 重点在代码只需更改文件名就可使用 无论数据增强还是修改名称 xff0c 标签框位置都会跟着改变 xff01 xff01 xff01 前人之鉴 xff
  • 利用select实现服务器和客户端的随时收发

    服务器代码实现 xff1a include lt stdio h gt include lt sys types h gt include lt sys socket h gt include lt arpa inet h gt inclu
  • 采集温度数据,用串口传输到上位机

    这里写目录标题 一 实验要求二 I2C总线通信协议 xff08 一 xff09 概念 xff08 二 xff09 I2C总线特征 xff08 三 xff09 I2C总线协议 xff08 四 xff09 I2C的两种方式 硬件I2C和软件I2
  • rflysim基于simulink控制3.4:硬件在环仿真-e0-1实验

    一 实验1 xff1a Simulink代码自动烧录 要求 xff1a 将设计好的simulink模型 xff0c 生成固件烧录到Pixhawk中 步骤 xff1a 1 设计控制器 例程参考 xff1a e0 2 PSPOfficialEx
  • 如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该 指针在函数体内被意外修改...

    如果参数是指针 xff0c 且仅作输入用 xff0c 则应在类型前加 const xff0c 以防止该 指针在函数体内被意外修改 1 include lt iostream gt 2 3 run this program using the
  • 【ROS进阶】一文搞懂ROS话题通信机制与消息队列

    文章目录 一 话题通信机制解析1 话题通信机制简介2 消息队列分析3 使用技巧 二 实验验证 xff08 一 xff09 subscriber队列长度对数据传输影响 xff08 二 xff09 数据传输时间延迟 三 总结 一 话题通信机制解
  • stm32串口DMA方式发送数据

    该文档介绍stm32 uart1通过DMA方式发送和接收数据 xff0c 代码示例基于ucos ii操作系统 该文档参考https wenku baidu com view d44ef1380975f46526d3e1b5 html 中内容