stm32串口DMA方式发送数据

发布者:Radiant777最新更新时间:2018-09-09 来源: eefocus关键字:stm32  串口  DMA方式  发送数据 手机看文章 扫描二维码
随时随地手机看文章
  • 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)

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();

    }

}


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

}


关键字:stm32  串口  DMA方式  发送数据 引用地址:stm32串口DMA方式发送数据

上一篇:向STM32串口发送数据的标准函数
下一篇:如何下载stm32系列单片机的固件库

推荐阅读最新更新时间:2024-03-16 16:13

STM32 网络通信Web Server中 SSI与CGI的应用解析
本次主要解析STM32网络通信中WebServer应用,从网页界面的编写到浏览器与STM32之间进行通信的数据来说明SSI与CGI的原理及应用,并对GET与POST指令进行应用解析。 硬件和软件环境: 1.硬件环境:STM32F407,网卡芯片LAN8720,其他部分参考正点原子的407探索者开发板。 2.软件环境:keil5,LWIP1.4.1,主要是基于正点原子STM32F407探索者的第六十章网络通信实验程序。 一、程序流程解析 为了方便查看浏览器与STM32之间的数据通信,建议程序中使用固定IP的方式,如192.168.1.101,建议使用软件Wireshark来查看网络数据。 首先是打开Wireshark,选
[单片机]
<font color='red'>STM32</font> 网络通信Web Server中 SSI与CGI的应用解析
stm32 TIM定时器[操作寄存器+库函数]
stm32配备了2个高级定时器TIM1和TIM8,4个通用定时器 TIM2,TIM3,TIM4和TIM5,还有两个基本定时器TIM6和TIM7。 高级定时器常用于电机控制,因为其加入了死区控制,紧急制动,定时器同步等高级特性。基本定时器可以为数模转化器提供准确的时间基准。 stm32的通用定时器由一个通过可编程预分频器驱动的16位自动装载计数器构成。通用定时器可以用于测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。 通用计时器的使用,需要先配置一个时基单元,就是设定一个基准时间,确定计数一次耗去的时间,可以设定在几个微妙到几个毫秒之间。 通用定时器的都有4个独立通道(TIMx_CH1~4),这
[单片机]
<font color='red'>stm32</font> TIM定时器[操作寄存器+库函数]
STM32单片机串口通讯代码
简介:在STM32开发中,串口是我们最常用的接口。通过串口,我们很方便地把数据输出到电脑,方便我们进行程序调试。下面我们来看看STM32的串口通讯代码。 要实现串口通讯,我们要进行下面几个步骤: 首先:要打开GPIO口的时钟和串口模块时钟。在圆点博士小四轴中,我们用的是GPIOA和COM1模块。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 其次:要指定GPIO口,即确定哪些IO是用于串口通讯的。记得使用GPIO_Mode_AF_PP模式
[单片机]
stm32 UCGUI 完美移植
UCGUI是一种嵌入式应用中的图形支持系统。它设计用于为任何使用LCD图形显示的应用提供高效的独立于处理器及LCD控制器的图形用户接口,它适用单任务或是多任务系统环境, 并适用于任意LCD控制器和CPU下任何尺寸的真实显示或虚拟显示。 它的设计架构是模块化的,由不同的模块中的不同层组成,由一个LCD驱动层来包含所有对LCD的具体图形操作。UCGUI可以在任何的CPU上运行,因为它是100%的标准C代码编写的。 类似程序还有国产的一个MINIGUI ( http://www.minigui.com/zhcn/ ),MiniGUI 是一个自由软件项目。其目标是提供一个快速、稳定、跨操作系统的图形用户界面
[单片机]
<font color='red'>stm32</font> UCGUI 完美移植
STM32的SYSTICK_Init()配置
void SYSTICK_Init(void) { /* SysTick end of count event each 1ms with input clock equal to 4.5MHz (HCLK/8, default) SysTick_SetReload(4500); /* Enable SysTick interrupt SysTick_ITConfig(ENABLE); /* Enable the SysTick Counter SysTick_CounterCmd(SysTick_Counter_Enable); } 系统时钟定时器的周期与驱动的时钟频率和Reload值相关。
[单片机]
STM32之系统中断处理
点击(此处)折叠或打开 /******************************************************************************* * Function Name : SysTickHandler * Description : This function handles SysTick Handler. * Input : None * Output : None * Return : None **************************************************************************
[单片机]
<font color='red'>STM32</font>之系统中断处理
判断STM32 GPIO输入口的输入状态(高电平或低电平)
在学习STM32中的过程中,经常会遇到“高电平有效”,“低电平有效”等字眼,初看时很多时候就会从字面上理解,认为高电平有效的意思就是有效电平是高电平,低电平有效的意思就是有效电平是低电平的意思。而实际上,这样的理解是有误的。下面咱们以STM32的定时器中输出比较通道为例: 这幅图实际上就是一个pwm波产生的过程,对定时器不了解的可以去查阅相关手册,现在我们先看图中标号1的输出模式控制器,这里模式是指pwm模式,他的意思就是可以通过配置寄存器TIMx_CCMR1的OC1M两位,来选择pwm的模式,但是关于模式选择,手册中有这样一句话:在向下计数时,一旦TIMx_CNT TIMx_CCR1时通道1为无效电平(OC1REF=0),
[单片机]
判断<font color='red'>STM32</font> GPIO输入口的输入状态(高电平或低电平)
基于STM32实现串口的两个分案解析
首先总结一下串口232,422,485 串口232:可双向传输,全双工,最大速率20Kbps,负逻辑电平,-15V~-3V逻辑“1”,+3V~+15V逻辑“0”。 串口422:可双向传输,4线全双工,2线单工。 串口485:可双向传输,4线全双工,2线单工,最大速率10Mb/s,差分信号,发送端:+2V~+6V逻辑“1”,-2V~-6V逻辑“0”,接收端:+200mV逻辑“1”,-200mV逻辑“0”。 对于串口的实现有以两个方案: 方案一,和原子的《例说STM32》一样,首先接收,然后处理,没有消息验证处理,这样就会出现消息覆盖,消息出错后死机,无法明确区分命令,无法及时应答握手信号。方案二,借鉴uC/OSII的消息队列,进
[单片机]
基于<font color='red'>STM32</font>实现<font color='red'>串口</font>的两个分案解析
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved