STM32DMA功能详解

2023-05-16

目录

一、DMA的基本介绍

1. 什么是DMA (DMA的基本定义)

2. DMA传输参数

3. DMA的主要特征

二、DMA功能框图

1. DMA请求

2. 通道

3. 仲裁器

三、DMA 数据配置

 1. 从哪里来到哪里去

外设到存储器:

存储器到外设:

存储器到存储器:

 2. 要传多少,单位是什么

3. 什么时候传输完成

四、DMA初始化结构体详解

五、 程序编写


一、DMA的基本介绍

1. 什么是DMA (DMA的基本定义)

        DMA,全称Direct Memory Access,即直接存储器访问,是单片机的一个外设。

        DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。这里的存储器可以是片内的SRAM(默认存放变量)或者是FLASH(默认存放常量,被const修饰的全局变量可以看成是常量类型),而外设指的其实是外设的数据寄存器。但它们本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。

        我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,DMA的主要功能是用来搬数据,在传输数据的时候,CPU就可以不被占用用来干其他事情,对于实时性要求比较高的场合,我们可以利用DMA来减小CPU的负担

    因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B不经过CPU的处理,通过DMA解决大量数据转移过度消耗CPU资源的问题。

2. DMA传输参数

        我们知道,数据传输,首先需要的是

  1. 数据的源地址
  2. 数据传输位置的目标地址
  3. 传递数据多少的数据传输量
  4. 进行多少次传输的传输模式 DMA所需要的核心参数,便是这四个

当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。

也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。

3. DMA的主要特征

每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推);

独立数据源和目标数据区的传输宽度(字节、半字、全字)[即1个字节、2个字节、4个字节],模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐;

支持循环的缓冲器管理;

每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求;如果配置了中断使能,就会触发中断,然后通过中断标志位判断引起中断的事件类型,通常我们用来监测DMA是否传输完成。

存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输;

闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;

可编程的数据传输数目:最大为65535。


二、DMA功能框图

以外设与SRAM通过DMA1传输数据为例,功能框图中主要有三部分内容:

1. DMA请求

如果外设要想通过 DMA 来传输数据,必须先给 DMA 控制器发送 DMA 请求,DMA 收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。

不同的 DMA 控制器的通道对应着不同的外设请求,这决定了我们在软件编程上该怎么设置,比如ADC1对应DMA1的通道1,串口USART1的发送对应DMA1的通道4,具体见 DMA 请求映像表(图上没有的说明不具备DMA请求功能):
图片来源:STM32F10xxx参考手册>>>10 DMA控制器(DMA)>>>表59 各个通道的DMA1请求一览

 图片来源:STM32F10xxx参考手册>>>10 DMA控制器(DMA)>>>表60 各个通道的DMA2请求一览

​​​​​​​2. 通道

DMA有两个DMA控制器一共有12个通道(DMA1有7个通道, DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。

3. 仲裁器

当发生多个 DMA 通道请求时,响应处理的顺序问题就由仲裁器管理。仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级

第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,
DMA1 控制器拥有高于 DMA2 控制器的优先级。
 


三、DMA 数据配置

使用 DMA,最核心就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的数据的单位是什么,要传多少数据,是一次传输还是循环传输等等。

 1. 从哪里来到哪里去

我们知道 DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。具体的方向 DMA_CCR 位 4 DIR 配置: 0 表示从外设到存储器, 1 表示从存储器到外设。这里面涉及到的外设地址由 DMA_CPAR 配置,存储器地址由 DMA_CMAR 配置。

外设到存储器:

        当我们使用从外设到存储器传输时,以 ADC 采集为例。 DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址

存储器到外设:

        当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。 DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址

存储器到存储器:

        当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。 DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把 DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式

 2. 要传多少,单位是什么

当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么。

以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由 DMA_CNDTR 配置,这是一个 32 位的寄存器,前16位为数据传输数量,一次最多只能传输 65535 个数据。

要想数据传输正确,源和目标地址存储的数据宽度必须一致,串口数据寄存器是 8 位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCR 的 PSIZE[1:0] 配置,可以是 8/16/32 位,存储器的数据宽度由 DMA_CCR 的 MSIZE[1:0] 配置,可以是 8/16/32 位。

在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储器的地址指针由 MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定。

3. 什么时候传输完成

数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个 DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器 DMA_ISR 的详细描述。

传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCR 寄存器的 CIRC 循环模式位控制。
 


四、DMA初始化结构体详解

标准库函数对每个外设都建立了一个初始化结构体 xxx_InitTypeDef(xxx 为外设名称),
结构体成员用于设置外设工作参数,并由标准库函数 xxx_Init()调用这些设定参数进入设置
外设相应的寄存器, 达到配置外设工作环境的目的。
结构体 xxx_InitTypeDef 和库函数 xxx_Init 配合使用是标准库精髓所在,理解了结构体
xxx_InitTypeDef 每个成员意义基本上就可以对该外设运用自如。结构体 xxx_InitTypeDef 定
义 在 stm32f10x_xxx.h( 后 面 xxx 为 外 设 名 称 ) 文 件 中 , 库 函 数 xxx_Init 定 义 在
stm32f10x_xxx.c 文件中,编程时我们可以结合这两个文件内注释使用。
DMA_ InitTypeDef 初始化结构体
 

1 typedef struct
2 {
3     uint32_t DMA_PeripheralBaseAddr; // 外设地址
4     uint32_t DMA_MemoryBaseAddr; // 存储器地址
5     uint32_t DMA_DIR; // 传输方向
6     uint32_t DMA_BufferSize; // 传输数目
7     uint32_t DMA_PeripheralInc; // 外设地址增量模式
8     uint32_t DMA_MemoryInc; // 存储器地址增量模式
9     uint32_t DMA_PeripheralDataSize; // 外设数据宽度
10     uint32_t DMA_MemoryDataSize; // 存储器数据宽度
11     uint32_t DMA_Mode; // 模式选择
12     uint32_t DMA_Priority; // 通道优先级
13     uint32_t DMA_M2M; // 存储器到存储器模式
14 } DMA_InitTypeDef;

1、DMA_PeripheralBaseAddr:外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。

2、DMA_Memory0BaseAddr:存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自定义存储区的首地址。

3、DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的 DIR[1:0] 位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。

4、DMA_BufferSize:设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。

5、DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。

6、DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以要使能存储器地址自动递增功能。

7、DMA_PeripheralDataSize:外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定 DMA_CCR 寄存器的 PSIZE[1:0] 位的值。外设数据寄存器会限制数据宽度,如串口的数据寄存器只有8位,所以我们定义的数据宽度也是8位。当使用存储器到存储器时,一般与数组的元素大小相关。

8、DMA_MemoryDataSize:存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定 DMA_CCR 寄存器的 MSIZE[1:0] 位的值。当外设和存储器之间传数据时,两边的数据宽度应该设置为一致大小。所以最终还是看外设数据寄存器的宽度。

9、DMA_Mode: DMA 传输模式选择,可选一次传输或者循环传输,它设定 DMA_CCR 寄存器的 CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所以使用循环传输模式。

10、DMA_Priority:软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0] 位的值。 DMA 通道优先级只有在多个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。

11、DMA_M2M:存储器到存储器模式,使用存储器到存储器时用到,设定 DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。外设到存储器模式不需要设置。


五、 程序编写

1. DMA配置步骤

1. 使能DMA控制器(DMA1 或 DMA2)时钟,DMA是挂载在AHB总线上的外设

RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, ENABLE); 

2. 初始化 DMA 通道,包括配置通道、外设和内存地址、传输数据量等,就是上一节介绍的结构体

DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); //@param:指定DMA 请求映像表对应的通道;DMA结构体指针变量

3. 使能外设DMA功能(到DMA请求映射对应外设的库文件里找到DMA使能函数)

USART_DMACmd( USART1, USART_DMAReq_Tx, ENABLE); //@param:串口1;发送还是接收;使能DMA

4. 开启DMA的通道传输

DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, ENABLE); //@param:指定DMA 请求映像表对应的通道;使能

5. 查询DMA传输状态

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//@param:指定要检查的DMA通道状态标志位

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);//@param:指定DMA 请求映像表对应的通道

DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //@param:指定DMA 请求映像表对应的通道;数据量(最大为65535Byte)

USART_DMACmdDMA_Cmd的区别是,DMA_Cmd是DMA通道的使能函数,DMA通常会结合外设来使用,外设DMA必须要使能即USART_DMACmd要开启,外设DMA开起来才能开启DMA通道传输即DMA_Cmd,因为我们的外设的请求对应到DMA通道,所以DMA才能开启。

通过前4步就能启动一次DMA传输,但是在DMA传输过程当中我们通常会去查询DMA的传输状态,比如传输完成与否(DMA_GetFlagStatus),或者获取当前传输的剩余数据量大小(DMA_GetCurrDataCounter),或者重新调整当前DMA通道传输的数据量大小(DMA_SetCurrDataCounter

2. 软件编程

FLASH to SRAM 把内部 FLASH 的数据传输到内部的 SRAM 中
把 flash 定义为外设,sram 定义为存储器

bsp_dma_mtm.h

#ifndef _BSP_DMA_MTM_H_
#define _BSP_DMA_MTM_H_

#include "stm32f10x.h"

#define   BUFFER_SIZE     32

void MtM_DMA_Config(void);
uint8_t BuffCmp(const uint32_t* pBuffer, uint32_t* pBufferl, uint16_t BufferLength);


#endif  /*_BSP_DMA_MTM_H_*/

bsp_dma_mtm.c

#include "bsp_dma_mtm.h"

//常量存储在 flash 中
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
                                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                    0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};

//变量存储在ram中
uint32_t aDST_Buffer[BUFFER_SIZE];

void MtM_DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;			//flash为外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;					//自定义数组地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;								//方向为 外设为源地址
    
    DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;					//外设地址增量模式
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;							//存储器地址自动递增功能
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;			//32位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;					//32位
    
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;									//发送一次
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
    
    DMA_Init(DMA1_Channel6, &DMA_InitStructure);
    DMA_ClearFlag(DMA1_FLAG_TC6);													//通道随便选
    DMA_Cmd(DMA1_Channel6, ENABLE);
}

uint8_t BuffCmp(const uint32_t* pBuffer, uint32_t* pBufferl, uint16_t BufferLength)
{
    uint8_t ret = 0;
    while(BufferLength--)
    {
        if(*pBuffer != *pBufferl)
        {
            ret = 0;
            break;
        }
        pBuffer++;
        pBufferl++;
        ret = 1;
    }
    
    return ret;
}

main.c

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_dma_mtm.h"
#include "delay.h"

extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];

int main(void)
{
    uint8_t Transfer_Status = 0;
    
    delay_init();
    LED_GPIO_Config();
    
    LED_G(ON);
    LED_R(OFF);
    LED_B(OFF);
    delay_ms(1000);
    
    MtM_DMA_Config();
    Transfer_Status = BuffCmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
    
    while(DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET);
    
    if(Transfer_Status == 0)
    {
        LED_G(ON);
        LED_B(OFF);
        LED_R(OFF);
    }
    else
    {
        LED_B(ON);
        LED_G(OFF);
        LED_R(OFF);  
    }
    
    while(1)
    {
        
    } 
}


SRAM to USART 把内部 sram 的数据传输到外部的 usart 中
usart 为外设,sram 定义为存储器

bsp_dma_mtm.h

#ifndef _BSP_DMA_MTP_H_
#define _BSP_DMA_MTP_H_

#include "stm32f10x.h"
#include <stdio.h>

void USART1_Config(void);
void USART1_DMA_Config(void);

#define   SENDBUFF_SIZE     5000

#endif  /*_BSP_DMA_MTP_H_*/

bsp_dma_mtm.c

#include "bsp_dma_mtp.h"

uint8_t SendBuff[SENDBUFF_SIZE];

//内存到外设                                    
void USART1_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &USART_InitStructure);
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&NVIC_InitStructure);
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE); 
}
                                    
void USART1_DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(USART1_BASE + 0x04);		//数据寄存器(USART_DR)
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;								//方向  sram 到 串口
    
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure); 
    //要把 ram 中接收到的数据放到 USART1_TX 中发送出去, USART1_TX 对应的就是 DMA1_Channel4(通道4)
    DMA_ClearFlag(DMA1_FLAG_TC4);
    DMA_Cmd(DMA1_Channel4, ENABLE);													//查表,USART1_TX
}

void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
    USART_SendData(pUSARTx, data);
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)
{
    uint8_t temp_h, temp_l;
    temp_h = (data & 0xff00) >> 8;
    temp_l = data & 0xff;
    
    USART_SendData(pUSARTx, temp_h);
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    
    USART_SendData(pUSARTx, temp_l);
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t* array, uint8_t num)
{
    for(uint8_t i = 0; i < num; i++)
    {
        Usart_SendByte(pUSARTx, array[i]); 
    }
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

void Usart_SendString(USART_TypeDef* pUSARTx, uint8_t* str)
{
    do
    {
        Usart_SendByte(pUSARTx, *(str++)); 
        
    }while((*str) != '\0');
    
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

int fputc(int ch, FILE* f)
{
    USART_SendData(USART1, (uint8_t)ch);
    
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
        
    return ch;
}

int fgetc(FILE* f)
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
        
    return (int)USART_ReceiveData(USART1);
}


void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        USART_SendData(USART1, USART_ReceiveData(USART1));
    }
}

main.c

#include "stm32f10x.h"
#include "bsp_dma_mtp.h"
#include "delay.h"

extern uint8_t SendBuff[SENDBUFF_SIZE];

int main(void)
{
    USART1_Config();
    USART1_DMA_Config();
    delay_init();
    
    for(uint16_t i = 0; i < SENDBUFF_SIZE; i++)
    {
        SendBuff[i] = 'x';
    }
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    
    while(1)
    {
        
    } 
}

文章来源:16、STM32——DMA详解_叫我谢布斯的博客-CSDN博客_stm32dma是什么

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

STM32DMA功能详解 的相关文章

  • ovs-ofctl

    文章目录 64 toc ovs ofctl语法COMMANDSOpenFlow Switch Management Commands OpenFlow Switch Flow Table CommandsGroup Table Comman
  • 【牛客网华为机试】HJ46 截取字符串

    题目 描述 输入一个字符串和一个整数k xff0c 截取字符串的前k个字符并输出 本题输入含有多组数据 输入描述 xff1a 1 输入待截取的字符串 2 输入一个正整数k xff0c 代表截取的长度 输出描述 xff1a 截取后的字符串 示
  • 【牛客网华为机试】HJ50 四则运算

    题目 描述 输入一个表达式 xff08 用字符串表示 xff09 xff0c 求这个表达式的值 保证字符串中的有效字符包括 0 9 43 xff0c 且表达式一定合法 输入描述 xff1a 输入一个算术表达式 输出描述 xff1a 得到计算
  • 【机器学习实战 Task1】 (KNN)k近邻算法的应用

    1 背景 1 1 k近邻算法的概述 xff08 1 xff09 k近邻算法的简介 k 近邻算法是属于一个非常有效且易于掌握的机器学习算法 xff0c 简单的说就是采用测量不同特征值之间距离的方法对数据进行分类的一个算法 xff08 2 xf
  • 【Python量化分析100例】Day1-使用Tushare获取数据

    1 背景 Tushare平台是目前使用python学习量化投资比较好用的而且是免费的一个数据获取平台 主要实现对金融数据从数据采集 清洗加工 到 数据存储的过程 xff0c 能够为金融分析人员提供快速 整洁 和多样的便于分析的数据 xff0
  • python报错:TypeError: Cannot interpret ‘<attribute ‘dtype‘ of ‘numpy.generic‘objects>‘as a data type

    原因 xff1a pandas和matplotlib版本不匹配 方案1 conda更新一下所有包 conda update n base conda 方案2 pip 更新最新版pandas pip install pandas upgrad
  • 【阿里云天池算法挑战赛】零基础入门NLP - 新闻文本分类-Day2-数据读取与数据分析

    一 赛题解析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day1 赛题理解 202xxx的博客 CSDN博客 二 数据读取 下载完成数据后推荐使用anaconda xff0c python3 8进行数据读取与模型训练 首先安装需
  • 【阿里云天池算法挑战赛】零基础入门NLP - 新闻文本分类-Day3-基于机器学习的文本分类

    一 赛题解析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day1 赛题理解 202xxx的博客 CSDN博客 二 数据读取与数据分析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day2 数据读取与数据分析 202xx
  • 【清华大学】2021元宇宙研究报告

    关于元宇宙的详细介绍 xff1b 来源 xff1a 软件定义世界 xff08 SDX xff09
  • MAC OS M1 安装MySQL

    xff08 1 xff09 进入mysql官网下载mysql http www mysql com downloads xff08 2 xff09 选择对应下载版本 xff08 3 xff09 选择直接下载 xff08 4 xff09 下载
  • PX4开源飞控位置控制解析

    位置控制的总体思路为串级PID控制算法 xff0c 内环为对速度的PID控制 xff0c 外环为对位置的比例控制 xff0c 被控对象为四旋翼无人机 xff0c 被控量为四旋翼无人机输出的推力矢量和 xff0c 将测量到的位置与速度进行反馈
  • ONOS意图框架

    1 意图基本概念 Intent是用于描述应用需求的不可变模型对象 xff0c ONOS核心根据其改变网络行为 在最低级别上 xff0c 可以用以下方式描述意图 即意图的组成 xff1a 1 Network Resource xff1a 一组
  • prometheus-4种metric类型说明

    前提了解 prometheus中存储的数据都是时序型 xff0c 其数据模型都是如下 xff1a metric name label 61 value value timestamp 下文中说的数据类型只是客户端的封装 prometheus
  • ROS2 中常用坐标系

    ROS2 中常用坐标系 frame id xff1a 用来告诉你 xff0c 发布的数据是来自哪一个坐标系的 在使用ROS进行定位导航等操作时 xff0c 我们经常会遇到各种坐标系 每种坐标系都有明确的含义 理论上坐标系的名称可以是随意的
  • 国密SM2算法

    目录 1 前言 2 基础参数 3 密钥对生成 4 签名算法 4 1 预处理1 4 2 预处理2 4 3 生成签名 4 4 签名验证 4 5 签名验证原理 5 参考资料 1 前言 比原链的智能合约支持国密算法的函数 SM2是国密标准的椭圆曲线
  • STM32——ARM与STM32之间的联系

    ARM与STM32之间的联系 stm32是基于ARM内核的一种控制器 xff0c 是包含与被包含的关系 ARM xff08 STM32 xff09
  • VTK坐标转换

    VTK坐标转换
  • 软件工程师校招面试救急包

    LeetCode牛人总结 xff08 手撕代码前看看 xff0c 抱佛脚 xff09 https github com labuladong fucking algorithm blob master README md 剑指offer x
  • unix环境高级编程——UNIX体系架构

    本期主题 xff1a unix环境高级编程 UNIX体系架构 文件IO 0 初始UNIX1 系统调用2 库函数2 1 C语言的运行库 3 shell 0 初始UNIX 这里略过unix的历史不讲 xff0c 网上有比较详细的资料 我们可以将
  • CVPR2019:无监督深度追踪

    本文提出了一种无监督的视觉跟踪方法 与使用大量带注释数据进行监督学习的现有方法不同 xff0c 本文的CNN模型是在无监督的大规模无标签视频上进行训练的 动机是 xff0c 强大的跟踪器在向前和向后预测中均应有效 xff08 即 xff0c

随机推荐

  • 各种智能优化算法比较与实现(matlab版)

    各种智能优化算法比较与实现 xff08 matlab版 xff09 一 方法介绍 1免疫算法 xff08 Immune Algorithm xff0c IA xff09 1 1算法基本思想 免疫算法是受生物免疫系统的启发而推出的一种新型的智
  • 我与人工智能的故事

    本文作者 xff1a 诸葛越 前 言 人工智能的三次浪潮 2018年年初 xff0c 招聘季正如火如荼地进行 xff0c 而 数据科学家 和 算法工程师 绝对算得上热门职业 人工智能 机器学习 深度学习 建模 卷积神经网络 等关键词 xff
  • ovn-architecture

    参考 文章目录 1 Name2 Description2 1 Information Flow in OVN OVN中的信息流向 2 2 Chassis Setup2 3 Logical Networks2 4 Life Cycle of
  • IDEA创建maven项目添加jar包依赖出错

    Problem1 xff1a 由于网络原因无法下载jar包 解决方法 xff1a 在maven的settings xml文件的 lt mirrors gt 标签中配置阿里镜像 lt mirror gt lt id gt nexus aliy
  • 判断一个字符串是否为回文字符串

    题目 输入一个字符串 xff0c 判断该字符串中除去空格之后的字符串是否为回文字符串 要求 xff1a 不可使用 Java 已实现的方法替换空格 xff0c 不可消耗额外的空间 代码实现 此处为判断方法的实现 public static b
  • Spring中的 JdbcTemplate和声明式事务控制

    Spring中的 JdbcTemplate和声明式事务控制 JdbcTemplate概述 JdbcTemplate的作用 xff1a 他就是用于和数据库交互的 xff0c 实现CRUD操作 如何创建该对象 在dao的实现类中定义并用set方
  • SpringMVC(一)

    SpringMVC xff08 一 xff09 SpringMVC的基本概念 三层架构 表现层业务层持久层 MVC模型 Model xff08 模型 xff09 xff1a 通常就是指我们的数据模型 xff0c 一般情况下用于封装数据 Vi
  • SpringMVC(二)

    SpringMVC xff08 二 xff09 响应数据和结果视图 返回值分类 xff1a 字符串voidModelAndView 对象 xff1a 是 spring 提供的一个对象 xff0c 可以用来调整具体的 JSP 视图 span
  • SpringMvc(三)

    SpringMvc xff08 三 xff09 SSM 整合可以使用多种方式 xff0c 一般会选择 XML 43 注解 的方式 整合的思路 xff1a 搭建整合环境先把spring 的配置搭建完成再使用 spring 整合SpringMV
  • 工厂方法模式(Factory Method)--多态工厂的实现

    工厂方法模式 xff08 Factory Method xff09 多态工厂的实现 定义 xff1a 定义一个用于创建对象的接口 xff0c 让子类决定实例化哪一个类 xff0c 工厂方法使一个类的实例化延迟到其子类 类图 xff1a 外链
  • 无人机自主定位导航避障VINS+fast_planner实测~

    厦大研一研究的一个项目 xff0c 将项目开发用到的技术和难点在这记录一下 常更新 xff0c 先把框架写好 xff0c 有空的时候就过来更新 xff0c 要是有漏的或者有错误的地方 xff0c 请大佬指点 因为采用的是TX2 xff0c
  • rs_D455相机内外参标定+imu联合标定

    IMU标定 lt launch gt lt node pkg 61 34 imu utils 34 type 61 34 imu an 34 name 61 34 imu an 34 output 61 34 screen 34 gt lt
  • GVINS论文简明解读

    VIN与GNSS融合的必要性 VIN系统只工作在局部坐标系下 x y z yaw不可观 里程计存在不可避免的漂移 而GNSS系统可提供无漂移的全局定位 VIN与GNSS融合的难点 不同于cmaera与imu此类的外参标定 GNSS坐标与VI
  • onos2.5.2编译安装

    onos编译安装 Ubuntu18 04 1 前置下载安装 1 1 前置包安装 参考docker file sudo apt get install y ca certificates zip python python3 git bzip
  • C++和C的区别(汇总)

    1 C是面向过程的语言 xff0c 而C 43 43 是面向对象的语言 2 C和C 43 43 动态管理内存的方法不一样 xff0c C是使用malloc free函数 xff0c 而C 43 43 除此之外还有new delete关键字
  • PX4学习笔记—通过串口发送自定义数据

    最近因为项目需要实现一个通过pixhawk串口收发自定义数据的功能 搜索发现 xff0c 博客上大神们FantasyJXF FreeApe的博文已经详细介绍了通过pixhawk串口读取自定义数据 xff0c 这部分功能实现后就可以将自己开发
  • 一步步入门搭建SpringSecurity OAuth2(密码模式)

    什么是OAuth2 xff1f 是开放授权的一个标准 xff0c 旨在让用户允许第三方应用去访问改用户在某服务器中的特定私有资源 xff0c 而可以不提供其在某服务器的账号密码给到第三方应用 大概意思就是比如如果我们的系统的资源是受保护的
  • STM32驱动SG90舵机与HC_SR04超声波模块

    前言 一 项目涉及的内容 项目简介 二 模块实操 1 SysTick系统定时器模块 2 SG90 舵机模块 3 HC SR04 超声波模块 4 main函数 总结 前言 这篇文章的内容主要对一个超声波 43 舵机小项目进行总结 xff0c
  • STM32基于IIC协议的OLED模块的使用

    前言 一 项目涉及的内容 项目简介 二 模块实操 1 IIC模块 1 1 IIC协议格式 1 2 开始信号与停止信号 1 3 写数据 1 3 1 硬件IIC代码编写 1 3 2 软件模拟IIC代码编写 2 OLED板块 前言 本篇文章对使用
  • STM32DMA功能详解

    目录 一 DMA的基本介绍 1 什么是DMA xff08 DMA的基本定义 xff09 2 DMA传输参数 3 DMA的主要特征 二 DMA功能框图 1 DMA请求 2 通道 3 仲裁器 三 DMA 数据配置 1 从哪里来到哪里去 外设到存