当前位置: 首页 > article >正文

STM32DMA串口传输实验(标准库)

一. 引子

        闲来暂时没有事情,所以写一篇STM32的教程,因为身边有DMA的源码,所以就直接开始写一篇DMA的教程

二. DMA机制

1.  什么是DMA

        首先DMA的官方描述是(Direct Memory Access,中文存储器访问)是一种让外设和内存直接传输数据的机制,而不需要CPU参与数据搬运,从而提高效率。
        在STM32中,DMA可以从外设-->传递到内存(存储器)直接传输数据,例如(串口、ADC、SPI等外设);也可以从存储器到存储器直接传输数据;也可以支持支持从外设到外设。

2. DMA的工作原理

        在没有使用DMA的情况下,CPU需要手动读取数据并存入存储器。例如,CPU想要读取ADC转换的数据,就首先需要读取ADC的数据寄存器,然后再存入RAM中,再继续执行其他的任务,这样的任务就显得非常麻烦,也会在一定程度上消耗CPU的算力

        但是如果假如加入了DMA,CPU就可以完全不用参与这个过程,DMA会自动把数据搬运到RAM,CPU就可以有更多时间去完成其他的事情完成其他的任务,提高系统的性能。所以它的又是有数据传输的更快,不受CPU执行效率的影响。并且可以实现无缝的数据传输,比如连续ADC采样或高速串口通信

        DMA的具体功能我将在代码配置中一步步完成

三. 硬件分析

        此次实验我们使用型号为STM32C8T6的开发板,我们需要找到开发板上串口对应的引脚,查找到是PA9和PA10

        那么在之后的代码中我们就需要完成对PA9以及PA10的引脚初始化

4. 代码实现

        这里先引用几个必要的库,以及对于系统时钟的使能

	#include "stm32f10x.h"                  // Device header
	#include "stm32f10x_gpio.h"
	#include "Delay.h"
	#include "stm32f10x_usart.h"
	#include "stdio.h"
	#include "misc.h"
	#include "stdlib.h"
	#include "time.h"

void usart_rcc_init()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHB2Periph_DMA,ENABLE);
}

         RCC是STM32的复位和时钟控制模块,负责管理系统手中和外设时钟的使能,在STM32中外设通过总线(APB1、APB2、AHB)连接到内核,APB(Advanced Peripheral Bus),通常用来初始化低速外设如,GPIO、UART、SPI、I2C、定时器

        我们也可以通过Cubemx去理解,系统时钟是72Mhz,AHB经过了1分频相当于还是72Mhz没有改变,但是分配到APB1中进行了2分频只剩下32Mhz,也就是有些情况下,APB时间总线可能会在系统时钟的基础上进行分频


        A

        根据查找在标准库文件中,APB2注释中也列出了需要通过此函数初始化的外设,那么这里我们先将GPIOA和USART1外设进行初始化

     

         AHB(Advanced High Bus)一般初始化高速模块例如CPU、DMA、SRAM、网口等等。这里看到下方注释也说明DMA是通过AHB进行时钟的初始阿虎过AHB外设时钟初始化将DMA外设进行初始化          

         那么上面完成的是最基本的时钟初始化,下一步就是外设的初始化,那么我们先完成PA9、PA10串口引脚的初始化
        这里对物理引脚初始化是为了告诉芯片PA9和PA10不是普通IO,而是给USART1用的,还有一点就是TX发送引脚为什么要设置为,复用推挽输出模式,首先就是复用是告诉芯片这是给串口使用的
        其次推挽输出模式,可以主动拉高(通过PMOS管)或拉低(通过NMOS)电平,确保信号稳定,适合发送数据USART_TX需要主动驱动信号线,推挽模式能快速切换电平,保证数据传输的完整性

        那么RX接收引脚(PA10)设置为浮空输入模式,作为串口接收引脚,需要读取外部设备发送的信号,必须配置为输入模式。浮空说明引脚内部不连接上拉或下拉带你组,电平完全由外部信号决定,避免电平冲突,如果内部启动上拉或者下拉,可能会与外部信号电平冲突(例如外部设备已经提供上拉)

void DMA_Gpio_Init()
{
    GPIO_InitTypeDef GPIO_InitStructTypeDef;
    
    GPIO_InitStructTypeDef.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructTypeDef.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructTypeDef.GPIO_Speed = GPIO_Speed_50Mhz;

    GPIO_Init(GPIOA,&GPIO_InitStructTypdef);

    GPIO_InitStructTypeDef.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructTypeDef.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructTypeDef.GPIO_Speed = GPIO_Speed_50Mhz;

    GPIO_Init(GPIOA,&GPIO_InitStructTypdef);
}

        接下来开始初始化串口,还是先创建一个结构体变量,然后配置一下结构体参数,第一个参数设置波特率为115200,禁用硬件流控制,然后启用串口的Rx(接收)和Tx(发送)模式 ,设置校验位为无校验,设置停止位为1,然后再设置数据位为8位,最后应用配置到USART1外设,使能USART1模块

        这里都是最基本的串口设置就不过多讲解,但是有一个参数就是数据发送位,我们这里是设置位8位的,这个参数会影响到DMA的数据传输,这里先暂时提及一下,之后我将在DMA中做详细的补充

void DMA_Gpio_Init()
{
    GPIO_InitTypeDef GPIO_InitStructTypeDef;
    
    GPIO_InitStructTypeDef.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructTypeDef.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructTypeDef.GPIO_Speed = GPIO_Speed_50Mhz;

    GPIO_Init(GPIOA,&GPIO_InitStructTypdef);

    GPIO_InitStructTypeDef.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructTypeDef.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructTypeDef.GPIO_Speed = GPIO_Speed_50Mhz;

    GPIO_Init(GPIOA,&GPIO_InitStructTypdef);



    USART_InitTypeDef USART_InitStructTypeDef;
    
    USART_InitStructTypeDef.USART_BaudRate = 115200;
    USART_InitStructTypeDef.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructTypeDef.USART_Mode = USART_Mode_RX|USART_Mode_TX;
    USART_InitStructTypeDef.USART_Parity = USART_Parity_No;
    USART_InitStructTypeDef.USART_StopBits = USART_StopBits_1;
    USART_InitStructTypeDef.USART_WordLength = USART_WordLength_8b;

    USART_Init(USART1,&USART_InitStructTypeDef);
    USART_Cmd(USART1,ENABLE);
}

        接下来就是使用DMA的初始化,首先定义两个结构体变量,一个用于DMA的发送,一个用于DMA接收,这是DMA的结构体意味着如下就是我们需要配置的参数

        那么下面会解释如下参数,然后一个一个配置,首先我们先开始配置接收DMA的通道

        那么首先是uint32_t DMA_PeripheralBaseAddr;,它的作用是指定DMA传输的外设数据寄存器地址,告诉DMA从“哪里”取数据,把数据送到哪里
        例如从外设到内存(如接收数据),DMA从外设的地址读取外设数据,然后再存入到内存。若是从内存到外设(如发送数据),DMA将内存数据写入这个地址对应的外设寄存器

        那我们举一个实际的例子,我们这次的实验是USART接收数据那就是从外设到内存

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;

        USART1_DR是串口的数据存储寄存器USART接收到一个字节的数据时,硬件会自动将该数据存入此寄存器。所以当这里DMA接收通道(RX)配置位外设到内存方向时,DMA会从USART1->DR寄存器读取数据,并搬运到内存的接收缓冲区(之后我讲解)
        并且每个USART接收到新数据(触发DMA请求),DMA会自动完成一次传输,无需CPU干预,这段代码最明确的意思就是外设数据的来源是USART->DR寄存器,请从这里读取数据

         下一个参数,决定了DMA传输的内存端操作位置,名字叫做内存基地址,作用在于指定DMA传输的内存缓冲区地址,即告诉DMA将外设数据存入该地址指向的内存区域
        从外设到内存(接收数据),DMA将外设数据存入该地址指向的内存区域。从内存到外设,DMA从该地址指向的内存区域读取数据,发送到外设

        如果是接收通道的DMA,那么从外设到内存,DMA从我们刚刚选择的外设寄存器(USART->DR),读取接收到的数据,存储到内存缓冲区,这里我们就创建一个数组作为内存缓冲区
        反之如果是发送通道的DMA,USART发送数据(内存->外设),DMA从内存缓冲区(tx_buffer)读取待发送的数据,写入到外设寄存器(USART1->DR)

uint8_t rx_buffer[100];

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;

       下一个参数,uint32_t DMA_DIR,这个参数是设置DMA的传输方向,指定我们之前所说的是从外设到内存,还是从内设到外设

        有两个可以选择的参数,DMA_DIR_PeripheraISRC,外设为源(数据从外设寄存器传输到内存);DMA_DIR_PeripheralDST,外设为目标(数据从内存传输到外设的内存)

         这里我们首先配置的是DMA接收通道所以刚开始的时候,方向必须是从外设到内存。如果之后配置的是发送通道那么就是从内存到外设。

        所以要注意的是如果方向设置为DMA_DIR_PeripheraISRC(数据从外设到内存),外设地址就必须是数据来源。(例如接收寄存器)
        那么反之设置为DMA_DIR_PeripheralDST(数据从内存到外设),外设地址必须是数据目标(例如发送寄存器)

uint8_t rx_buffer[100];

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;

        继续下一个参数,这个参数决定了DMA残次传输的数据总量,名字叫做缓冲区大小,指定DMA传输的数据单元数量
        数据单元的只是之后会讲解,在这里的意思是,如果是从外设到内存,说明DMA会从外设(如USART1)接收100个数据单元,存入内存缓冲区(就是我们rx_buffer数组)
        相反如果是发送数据,内存到外设,DMA会从内存缓冲区tx_buffer,发送全部数据,然后写入到USART->DR寄存器

        需要注意的点在于,必须确保DMA_BufferSize必须小于或者等于实际缓冲区长度,若rx_buffer[50],被定义为50字节,但DMA_BufferSize =100 ,就会导致内存越界,程序崩溃,因为我们的缓冲区大小是100,所以我们的参数也设置为100就可以了

uint8_t rx_buffer[100];		

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;

     下一个参数,DMA_InitStructTypeDef,叫做外设地址递增模式,决定了外设地址在传输过程中是否自动递增

        可以选择两个值,DMA_PeripheralInc_Enable,启用递增,每次传输后外设地址自动增加。(适用于连续的外设寄存器)以及DMA_PeripheralInc_Disable,禁用递增,外设地址固定(适用于单个外设寄存器,如USART的DR寄存器)

        使能外设地址递增,传输数据到连续的内存缓冲区,例如数组

        但是如果内存地址是固定的,所有数据写入同一内容地址,例如实时更新某个状态变量

         内存地址递增由内存数据宽度决定,DMA_MemoryDataSize_Byte,八位地址+1,DMA_MemoryDataSize_HalfWor内d,16位表示地址加2,这个参数在后面会讲

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;

        这里有一个参数DMA_PeripheralInc,我们并没有使用到,他是配置DMA的外设自增,这里为什么我们不配置DMA的外设地址自增。
        在这里我们的串口发送和接收共用同一个数据寄存器USART->DR,发送时,数据从内存写入USART->DR,通过USART模块将数据发送出去。接收的时候将接收到的数据存入USART1->DR,由DMA读取到内存。
        无论是发送还是接收,外设地址始终指向USART->DR,因此我们不使用外设地址自增

       下面可以结合图片,理解一下在这里为什么需要内存地址自增,

         下一个参数,这个参数是结构体的关键参数,决定了外设端每次传输的数据单元宽度,名字叫做外设数据宽度
        指定外设端每次传输的数据单元大小。我们由三个参数可以选择DMA_PeripheralDataSize_Byte,8位(单字节),一般适用于USART、SPI等单字节传输场景。DMA_PeripheralDataSize_HalfWord,16位(2字节),一般适用于ADC、TIM的16位数据寄存器。DMA_PeripheralDataSize_Word,3位(四字节),适用于大块数据传输(如内存到内存)

        但是这个参数有一个很重要的点,外设数据宽度必须和外设寄存器实际宽度一致,USART的DR寄存器为8位,所以每次传输1字节的数据

        代码如下

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructTypeDef_RX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

        那么继续配置下一个参数,下面一个参数决定内存端每次传输的数据大小,正好和上面的外设数据端的数据宽度对应

        也有三种参数可以选择,但是内存数据宽度由缓冲区数据类型决定和外设无关,在我们这里的内存缓冲区就是数组uint8_t,所以这里就配置内存宽度为8位就可以

          但是如果配置不正确,例如下麦呢就会导致数组中的元素会被分成2此传输,数据错乱

        所以代码如下

	
DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructTypeDef_RX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructTypeDef_RX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;

        那么下一个参数,是设置DMA传输模式,可以选择DMA_Mode_Normal,DMA_Mode_Circular,分别为普通模式和循环模式

        普通模式是传输完成,DMA_Buffersize个数据后停止,需要我们手动去重启重新发送
        循环模式,在传输完成DMA_Buffersize个数据后自动从头开始循环传输

        在这里我们就选择循环模式,实验就不必在意具体模式

        代码如下

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructTypeDef_RX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructTypeDef_RX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;
DMA_InitStructTypeDef_RX.DMA_Mode = DMA_Mode_Circular;  //循环模式

        最后两个参数就一起介绍了

        DMA_Priority是用于设置DMA的传输优先级,这里的话就设置为中等优先级就可以,下面是可以选择参数

        下面的DMA_M2M,指的是是否在内存和内存数据之间传输,但是这里不需要,所以直接Didsable就可以,代码如下

DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructTypeDef_RX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructTypeDef_RX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;
DMA_InitStructTypeDef_RX.DMA_Mode = DMA_Mode_Circular;  //循环模式
DMA_InitStructTypeDef_RX.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructTypeDef_RX.DMA_M2M = DMA_M2M_Disable;

        以上我们就完成了所以接收通道的DMA配置初始化,那么下一步还有传输通道的初始化,因为每个结构体的具体作用上面已经讲解了,所以这里传输通道就不过多讲解了,可以参照下面的代码进行配置

DMA_InitStructTypeDef_TX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructTypeDef_TX.DMA_MemoryBaseAddr = (uint32_t)tx_buffer;
DMA_InitStructTypeDef_TX.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructTypeDef_TX.DMA_BufferSize = 100;
DMA_InitStructTypeDef_TX.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructTypeDef_TX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructTypeDef_TX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;
DMA_InitStructTypeDef_TX.DMA_Mode = DMA_Mode_Normal;  
DMA_InitStructTypeDef_TX.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructTypeDef_TX.DMA_M2M = DMA_M2M_Disable;

        最后,我将整个函数的代码都展示出来,完整的GPIO,串口,DMA的初始化就统一写在一个函数里面就可以了

void dma_gpio_init()
	{
		GPIO_InitTypeDef GPIO_InitStructTypeDef;
		USART_InitTypeDef USART_InitStructTypeDef;
		DMA_InitTypeDef DMA_InitStructTypeDef_TX;
		DMA_InitTypeDef DMA_InitStructTypeDef_RX;
		
		GPIO_InitStructTypeDef.GPIO_Mode =  GPIO_Mode_AF_PP;
		GPIO_InitStructTypeDef.GPIO_Pin  =  GPIO_Pin_9;
		GPIO_InitStructTypeDef.GPIO_Speed =  GPIO_Speed_50MHz;
		
		GPIO_Init(GPIOA,&GPIO_InitStructTypeDef);
		
		GPIO_InitStructTypeDef.GPIO_Mode =  GPIO_Mode_IN_FLOATING;
		GPIO_InitStructTypeDef.GPIO_Pin  =  GPIO_Pin_10;
		GPIO_InitStructTypeDef.GPIO_Speed =  GPIO_Speed_50MHz;
		
		GPIO_Init(GPIOA,&GPIO_InitStructTypeDef);
		
		USART_InitStructTypeDef.USART_BaudRate						= 115200;
		USART_InitStructTypeDef.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
		USART_InitStructTypeDef.USART_Mode                = USART_Mode_Rx|USART_Mode_Tx;
		USART_InitStructTypeDef.USART_Parity              = USART_Parity_No;
		USART_InitStructTypeDef.USART_StopBits            = USART_StopBits_1;
		USART_InitStructTypeDef.USART_WordLength          = USART_WordLength_8b;
		
		USART_Init(USART1,&USART_InitStructTypeDef);
		USART_Cmd(USART1,ENABLE);
		
		DMA_InitStructTypeDef_RX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
		DMA_InitStructTypeDef_RX.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
		DMA_InitStructTypeDef_RX.DMA_DIR = DMA_DIR_PeripheralSRC;
		DMA_InitStructTypeDef_RX.DMA_BufferSize = 100;
		DMA_InitStructTypeDef_RX.DMA_MemoryInc = DMA_MemoryInc_Enable;
		DMA_InitStructTypeDef_RX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
		DMA_InitStructTypeDef_RX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;
		DMA_InitStructTypeDef_RX.DMA_Mode = DMA_Mode_Circular;  //循环模式
		DMA_InitStructTypeDef_RX.DMA_Priority = DMA_Priority_Medium;
		DMA_InitStructTypeDef_RX.DMA_M2M = DMA_M2M_Disable;
		
		DMA_InitStructTypeDef_TX.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
		DMA_InitStructTypeDef_TX.DMA_MemoryBaseAddr = (uint32_t)tx_buffer;
		DMA_InitStructTypeDef_TX.DMA_DIR = DMA_DIR_PeripheralDST;
		DMA_InitStructTypeDef_TX.DMA_BufferSize = 100;
		DMA_InitStructTypeDef_TX.DMA_MemoryInc = DMA_MemoryInc_Enable;
		DMA_InitStructTypeDef_TX.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
		DMA_InitStructTypeDef_TX.DMA_MemoryDataSize =  DMA_MemoryDataSize_Byte;
		DMA_InitStructTypeDef_TX.DMA_Mode = DMA_Mode_Normal;  
		DMA_InitStructTypeDef_TX.DMA_Priority = DMA_Priority_Medium;
		DMA_InitStructTypeDef_TX.DMA_M2M = DMA_M2M_Disable;
		
		DMA_Init(DMA1_Channel5,&DMA_InitStructTypeDef_RX);
		DMA_Init(DMA1_Channel4,&DMA_InitStructTypeDef_TX);
		
		USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
		USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
		
			
		DMA_Cmd(DMA1_Channel5,ENABLE);
		DMA_Cmd(DMA1_Channel4,ENABLE);
	}
	

        最后几段代码就是,DMA初始化配置生效,然后将串口和DMA进行使能,所有的配置就完成了

        接下来编写主函数的代码


	int main()
	{
		char tx_buffer[100] = {0};
		
		int offset = 0;
		
		for(int i =0; i<100;i++)
		{
			 offset += sprintf(tx_buffer + offset, " %d",i+1);
				if(offset >= sizeof(tx_buffer))
				{
					break;
				}
		}
		
		usart_rcc_init();
		dma_gpio_init();
		
		while(1)
		{
			Delay_ms(500);
		}

	}

        如上代码的意思就是将数组中的数据进行初始化,然后在里面填充0-100的100个数字,然后将填充的数据转换为字符串,写入到数组中,offset变量用于跟踪已经写入缓冲区的字节数,确保把会超过缓冲区的大小

        然后调用时钟的初始化,以及刚刚所有的外设的初始化,在 循环中加入延时是为了,不让数据传输的太快,不然串口终端可能会卡死

        至此串口DMA数据传输实验我们就完成了,因为没有具体的项目只是针对DMA的教程做了一个简单的实验,实际DMA在项目中的应用非常的广泛和适用,所以之后的使用就可以在项目中尝试使用一下,欢迎各位读者在阅读完文章之后的指正以及讨论


http://www.kler.cn/a/578612.html

相关文章:

  • 本地搭建DeepSeek R1模型 + 前端
  • MySQL 表的字段数量和单行存储容量受存储引擎、数据类型、行结构等多因素限制
  • ORB-SLAM2源码学习(六):相机跟踪(局部地图跟踪和关键帧创建)
  • 【向量模型】 开源通用向量模型BGE (BAAI General Embedding)
  • Vue代理与Vuex模块化学习
  • 环形链表问题的探究与代码实现
  • 【论文精读】GaussReg: Fast 3D Registration with Gaussian Splatting
  • MyBatis SQL 映射文件的作用和结构
  • Java 大视界 -- Java 大数据在智能体育赛事运动员表现分析与训练优化中的应用(122)
  • 忘记dedecms后台超级管理员账号和密码的解决方案
  • crewai框架出现SSLError
  • 请谈谈 HTTP 中的安全策略,如何防范常见的Web攻击(如XSS、CSRF)?
  • 2025-03-09 学习记录--C/C++-PTA 练习11-4 字符定位(最后一次找到的字符)
  • 音视频入门基础:RTP专题(16)——RTP封装音频时,音频的有效载荷结构
  • 同为科技智能PDU在数据中心场景的应用与解决方案
  • 垂直领域大模型优化:从“通用”到“专精”——打造医疗、金融、法律领域的AI专家
  • 【RAG】文本分割的粒度
  • es-使用easy-es时如何指定索引库
  • 【Java篇】数据类型与变量:窥见程序的天地万象
  • 设计模式 一、软件设计原则