18:(标准库)DMA二:DMA+串口收发数据

news2025/3/9 10:43:50

DMA+串口收发数据

  • 1、DMA+串口发送数据
  • 2、DMA中断+串口接收定长数据包
  • 3、串口空闲中断+DMA接收不定长数据包
  • 4、串口空闲中断+DMA接收不定长数据包+DMA发送数据包

1、DMA+串口发送数据

在这里插入图片描述
当串口的波特率大于115200时,可以通过DMA1进行数据搬运,以防止数据的丢失。如上图所示:UART1的Tx发送请求使用DMA1的通道4,UART1的Rx接收数据请求使用DMA1的通道5。
①串口发送时:当UART1的发送数据寄存器TDR中没有数据时,就会向DMA1的通道4申请数据搬运,DMA1将缓冲区的数据搬运到TDR数据寄存器中,然后串口将数据发送出去。

②串口接收时:当UART1的接收数据寄存器RDR中有数据时,就会向DMA1的通道5申请数据搬运,DMA1将数据从RDR寄存器中搬运到缓冲区中。

【注意】数据的搬运和数据的发送的过程都不需要CPU参与,CPU只参与串口UART1和DMA1通道1的配置
①UART.c文件的代码如下:

#include "UART.h"

uint8_t Buff[Buffer_Size];//定义数据缓冲区
/**
 * 串口1的初始化函数
 */
void UART1_Init(void)
{
    /* 开启串口的UART1的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* 开启串口的GPIO的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* 配置串口1的引脚 */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;// 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 配置串口1的模式 */
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 不使用硬件流控制
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 收发模式
    USART_InitStruct.USART_Parity = USART_Parity_No;// 无奇偶校验位
    USART_InitStruct.USART_StopBits = USART_StopBits_1;// 1个停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;// 8个数据位
    USART_Init(USART1, &USART_InitStruct);
    
    /* 使能串口DMA发送请求 */
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    
    /* 使能串口1 */
    USART_Cmd(USART1, ENABLE);
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include "stm32f10x.h"
#include "stdio.h"

#define Buffer_Size 256
extern uint8_t Buff[Buffer_Size];//定义数据缓冲区

void UART1_Init(void);

#endif

③MyDMA.c文件的代码如下:

#include "MyDMA.h"
#include "UART.h"
/**
 * DMA1的通道4的初始化 
 */
void DMA1_Init(void)
{
    /* 1、使能DMA1的时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2、配置DMA1的通道1 */
    DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);    //“外设站点”的起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,8位
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       //外设站点地址是否自增,这里选择不自增,因为搬运到数据寄存器TDR中
	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Buff;                 //“内存站点”起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //数据宽度,8位
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                //目的站点地址是否自增,自增
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;                     //搬运方向的选择(目的地选择),这里选择内存站点--->外设站点:DMA_DIR_PeripheralDST
	DMA_InitStruct.DMA_BufferSize = 0;                                  //传输计数器的大小,代表搬运数据的个数,先置为0
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                          //是否自动重装,这里选择不自动重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                           //是否软件触发,这里选择不是,由硬件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                  //优先级,这里选择中等
	DMA_Init(DMA1_Channel4,&DMA_InitStruct);                            //配置DMA1的通道4
	
//	DMA_Cmd(DMA1_Channel4,ENABLE);                                      //使能DMA1的通道4
	DMA_Cmd(DMA1_Channel4,DISABLE);                                     //先失能DMA1的通道4
}
   
/**
 * DMA1开启搬运函数
 */
void UART1_DMA1_Transport(uint16_t DataNumber)
{
    /* 1、失能DMA1 */
    DMA_Cmd(DMA1_Channel4,DISABLE); 
    
    /* 2、先设置传输计数器的计数值 */
    DMA_SetCurrDataCounter(DMA1_Channel4, DataNumber);
    
    /* 3、使能DMA1 */
    DMA_Cmd(DMA1_Channel4,ENABLE);   
    
    /* 4、等待搬运完成 */
    while(!DMA_GetFlagStatus(DMA1_FLAG_TC4));   //等待DMA1通道4全部搬运完成
	DMA_ClearFlag(DMA1_FLAG_TC4);               //手动清除标志位 
}

④MyDMA.h文件的代码如下:

#ifndef __MyDMA_H
#define __MyDMA_H
#include "stm32f10x.h"

void DMA1_Init(void);
void UART1_DMA1_Transport(uint16_t DataNumber);

#endif

⑤主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "MyDMA.h"

#define DataNumber 10 //定义需要发送的数据个数

int main(void)
{
    for(uint8_t i = 0; i<DataNumber; i++)//先向缓冲区里面填入数据
    {
        Buff[i] = i;
    }
    
    UART1_Init();
    DMA1_Init();
    UART1_DMA1_Transport(DataNumber);     //开始搬运数据 
    
	while(1)
	{ 
        
	}
}

在这里插入图片描述

2、DMA中断+串口接收定长数据包

①UART.c文件的代码如下:

#include "UART.h"

uint8_t Buff[Buffer_Size];  //定义数据缓冲区
uint16_t Length = 10;       //定义定长数据包长度
uint8_t Flag = 0;           //传输完成标志位
/**
 * 串口1的初始化函数
 */
void UART1_Init(void)
{
    /* 开启串口的UART1的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* 开启串口的GPIO的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* 配置串口1的引脚 */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;        // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 配置串口1的模式 */
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 不使用硬件流控制
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                // 收发模式
    USART_InitStruct.USART_Parity = USART_Parity_No;                            // 无奇偶校验位
    USART_InitStruct.USART_StopBits = USART_StopBits_1;                         // 1个停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;                    // 8个数据位
    USART_Init(USART1, &USART_InitStruct);
    
    /* 使能串口DMARx接收请求 */
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    
    /* 使能串口1 */
    USART_Cmd(USART1, ENABLE);
}

/**
 * 串口发送多个字节的数据
 */
void USART_SendArray(uint8_t *array, uint16_t len)
{
    /* 发送一组数据 */
    for (uint16_t i = 0; i < len; i++)
    {
        USART_SendChar(array[i]);
    }
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include "stm32f10x.h"
#include "stdio.h"

#define Buffer_Size 256
extern uint8_t Buff[Buffer_Size];//定义数据缓冲区
extern uint16_t Length;
extern uint8_t Flag;

void UART1_Init(void);
void USART_SendArray(uint8_t *array, uint16_t len);

#endif

③MyDMA.c文件的代码如下:

#include "MyDMA.h"
#include "UART.h"
/**
 * DMA1通道5的初始化
 */

void DMA1_Init(void)
{
    /* 1、使能DMA1的时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2、配置DMA1的通道1 */
    DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);    //外设站点的起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,8位
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       //外设站点地址是否自增,这里选择不自增,因为搬运到数据寄存器TDR中
	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Buff;                 //内存站点起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //数据宽度,8位
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                //目的站点地址是否自增,自增
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                     //搬运方向的选择(目的地选择),这里选择外设站点--->内存站点:DMA_DIR_PeripheralSRC
	DMA_InitStruct.DMA_BufferSize = Length;                             //传输计数器的大小,代表搬运数据的个数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                        //是否自动重装,这里选择自动重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                           //是否软件触发,这里选择不是,由硬件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                  //优先级,这里选择中等
	DMA_Init(DMA1_Channel5,&DMA_InitStruct);                            //配置DMA1的通道5
    
    /* 3、使能DMA1通道5搬运完成中断和NVIC */
	DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
	DMA_Cmd(DMA1_Channel5,ENABLE);                                      //使能DMA1的通道5
}
   
/**
 * DMA1开启搬运函数
 */
//void UART1_DMA1_Transport(uint16_t DataNumber)
//{
//    /* 1、失能DMA1 */
//    DMA_Cmd(DMA1_Channel4,DISABLE); 
//    
//    /* 2、先设置传输计数器的计数值 */
//    DMA_SetCurrDataCounter(DMA1_Channel4, DataNumber);
//    
//    /* 3、使能DMA1 */
//    DMA_Cmd(DMA1_Channel4,ENABLE);   
//    
//    /* 4、等待搬运完成 */
//    while(!DMA_GetFlagStatus(DMA1_FLAG_TC4));   //等待通道4搬运完成
//	  DMA_ClearFlag(DMA1_FLAG_TC4);               //手动清除标志位 
//}

/**
 * DMA1通道5传输完成的中断服务函数
 */
void DMA1_Channel5_IRQHandler(void)
{
    if(DMA_GetFlagStatus(DMA1_FLAG_TC5))
    {
        DMA_ClearFlag(DMA1_FLAG_TC5);   //清除通道5的标志位
        Flag = 1;
    }      
}

④主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "MyDMA.h"

int main(void)
{
    UART1_Init();
    DMA1_Init();
 
	while(1)
	{ 
        if(Flag)
        {  
            Flag = 0;
            USART_SendArray(Buff, Length); 
        }       
	}
}

在这里插入图片描述

3、串口空闲中断+DMA接收不定长数据包

①UART.c文件的代码如下:

#include "UART.h"

uint8_t Buff[Buffer_Size];  //定义数据缓冲区
uint8_t Flag = 0;           //传输完成标志位
uint16_t Index = 0;         //定义接收到的数据个数
/**
 * 串口1的初始化函数
 */
void UART1_Init(void)
{
    /* 开启串口的UART1的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* 开启串口的GPIO的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* 配置串口1的引脚 */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;        // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 配置串口1的模式 */
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 不使用硬件流控制
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                // 收发模式
    USART_InitStruct.USART_Parity = USART_Parity_No;                            // 无奇偶校验位
    USART_InitStruct.USART_StopBits = USART_StopBits_1;                         // 1个停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;                    // 8个数据位
    USART_Init(USART1, &USART_InitStruct);
    
    /* 使能串口DMARx接收请求 */
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    
    /* 使能串口IDLE空闲中断和NVIC */
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    /* 使能串口1 */
    USART_Cmd(USART1, ENABLE);
}

/**
 * 串口发送一个字节的数据
 */
void USART_SendChar(uint8_t ch)
{
    /* 发送一个字节的数据 */
    USART_SendData(USART1, ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/**
 * 串口发送一个字符串的数据
 */
void USART_SendString(uint8_t *str)
{
    /* 发送多个字节的数据 */
    while (*str!= '\0')
    {
        USART_SendChar(*str++);
    }
}

/**
 * 串口发送多个字节的数据
 */
void USART_SendArray(uint8_t *array, uint16_t len)
{
    /* 发送一组数据 */
    for (uint16_t i = 0; i < len; i++)
    {
        USART_SendChar(array[i]);
    }
}

/**
 * 对printf函数进行重定向
 */
int fputc(int ch, FILE *f)
{
    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    
    /* 发送一个字节的数据 */
    USART_SendData(USART1, (uint8_t)ch);
    return ch;
}

/**
 * 串口1的空闲中断服务函数
 */
void USART1_IRQHandler(void)
{
    uint8_t Receive_Data;
    if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE))
    {   
        Receive_Data = USART1->SR;
        Receive_Data = USART1->DR; //清除中断标志位IDLE
        
//      Index = Buffer_Size - DMA_GetCurrDataCounter(DMA1_Channel5);//获取接收到的数据个数
        Index = Buffer_Size -(DMA1_Channel5->CNDTR);                //获取接收到的数据个数
        Flag = 1;
        
        /* 重新给DMA传输计数器设置值:让第二个数据包从缓冲区第一位开始存储 */
        DMA_Cmd(DMA1_Channel5,DISABLE);                             
        DMA_SetCurrDataCounter(DMA1_Channel5, Buffer_Size); 
        DMA_Cmd(DMA1_Channel5,ENABLE);                              //使能DMA1的通道5
    }
}

②UART.h文件的代码如下:

#ifndef __UART_H
#define __UART_H
#include "stm32f10x.h"
#include "stdio.h"

#define Buffer_Size 256
extern uint8_t Buff[Buffer_Size];//定义数据缓冲区
extern uint8_t Flag;
extern uint16_t Index;

void UART1_Init(void);
void USART_SendChar(uint8_t ch);
void USART_SendString(uint8_t *str);
void USART_SendArray(uint8_t *array, uint16_t len);

#endif

③MyDMA.c文件的代码如下:

#include "MyDMA.h"
#include "UART.h"
/**
 * DMA1通道5的初始化
 */

void DMA1_Init(void)
{
    /* 1、使能DMA1的时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2、配置DMA1的通道1 */
    DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);    //外设站点的起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,8位
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       //外设站点地址是否自增,这里选择不自增,因为搬运到数据寄存器TDR中
	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Buff;                 //内存站点起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //数据宽度,8位
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                //目的站点地址是否自增,自增
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                     //搬运方向的选择(目的地选择),这里选择外设站点--->内存站点:DMA_DIR_PeripheralDST
	DMA_InitStruct.DMA_BufferSize = Buffer_Size;                        //传输计数器的大小,代表搬运数据的个数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                          //是否自动重装,这里选择不自动重装,接收一个数据包,在空闲中断里面重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                           //是否软件触发,这里选择不是,由硬件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                  //优先级,这里选择中等
	DMA_Init(DMA1_Channel5,&DMA_InitStruct);                            //配置DMA1的通道5
    
	DMA_Cmd(DMA1_Channel5,ENABLE);                                      //使能DMA1的通道5
}

④主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "MyDMA.h"

int main(void)
{
    UART1_Init();
    DMA1_Init();
 
	while(1)
	{ 
        if(Flag)
        {  
            Flag = 0;
            USART_SendArray(Buff, Index);   
        }       
	}
}

在这里插入图片描述

4、串口空闲中断+DMA接收不定长数据包+DMA发送数据包

①MyDMA.c文件的代码如下:

#include "MyDMA.h"
#include "UART.h"
/**
 * DMA1通道5的初始化
 */
void DMA1_Init(void)
{
    /* 1、使能DMA1的时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2、配置DMA1的通道5,接收数据 */
    DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);    //外设站点的起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,8位
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       //外设站点地址是否自增,这里选择不自增,因为搬运到数据寄存器TDR中
	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Buff;                 //内存站点起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //数据宽度,8位
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                //目的站点地址是否自增,自增
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                     //搬运方向的选择(目的地选择),这里选择外设站点--->内存站点:DMA_DIR_PeripheralSRC
	DMA_InitStruct.DMA_BufferSize = Buffer_Size;                        //传输计数器的大小,代表搬运数据的个数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                          //是否自动重装,这里选择不自动重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                           //是否软件触发,这里选择不是,由硬件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                  //优先级,这里选择中等
	DMA_Init(DMA1_Channel5,&DMA_InitStruct);                            //配置DMA1的通道5
    
    DMA_Cmd(DMA1_Channel5,ENABLE);                                      //使能DMA1的通道5
    
    /* 2、配置DMA1的通道4,发送数据 */
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);    //外设站点的起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度,8位
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       //外设站点地址是否自增,这里选择不自增,因为搬运到数据寄存器TDR中
	
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Buff;                 //内存站点起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //数据宽度,8位
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                //目的站点地址是否自增,自增
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;                     //搬运方向的选择(目的地选择),这里选择内存站点--->外设站点:DMA_DIR_PeripheralDST
	DMA_InitStruct.DMA_BufferSize = 0;                                  //传输计数器的大小,代表搬运数据的个数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                          //是否自动重装,这里选择不自动重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                           //是否软件触发,这里选择不是,由硬件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Low;                     //优先级,这里选择低
	DMA_Init(DMA1_Channel4,&DMA_InitStruct);                            //配置DMA1的通道4
    
    DMA_Cmd(DMA1_Channel4,DISABLE);                                     //失能DMA1的通道4
}

/**
 * DMA1通道5的传输计数器重装设置
 */
void DMA1_Chanael5_Count(uint16_t DataNumber)
{
    DMA_Cmd(DMA1_Channel5,DISABLE);                             
    DMA_SetCurrDataCounter(DMA1_Channel5, DataNumber); 
    DMA_Cmd(DMA1_Channel5,ENABLE);                                      //使能DMA1的通道5
}

/**
 * DMA1通道4的传输计数器重装设置
 */
void DMA1_Chanael4_Count(uint16_t DataNumber)
{
    DMA_Cmd(DMA1_Channel4,DISABLE);                             
    DMA_SetCurrDataCounter(DMA1_Channel4, DataNumber); 
    DMA_Cmd(DMA1_Channel4,ENABLE);                                      //使能DMA1的通道5
}

②MyDMA.h文件的代码如下:

#ifndef __MyDMA_H
#define __MyDMA_H
#include "stm32f10x.h"

void DMA1_Init(void);
void DMA1_Chanael5_Count(uint16_t DataNumber);
void DMA1_Chanael4_Count(uint16_t DataNumber);

#endif

③UART.c文件的代码如下:

#include "UART.h"
#include "MyDMA.h"

uint8_t Buff[Buffer_Size];  //定义数据缓冲区
uint16_t Index = 0;         //定义接收到的数据个数
/**
 * 串口1的初始化函数
 */
void UART1_Init(void)
{
    /* 开启串口的UART1的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* 开启串口的GPIO的时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* 配置串口1的引脚 */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;        // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 配置串口1的模式 */
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 不使用硬件流控制
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                // 收发模式
    USART_InitStruct.USART_Parity = USART_Parity_No;                            // 无奇偶校验位
    USART_InitStruct.USART_StopBits = USART_StopBits_1;                         // 1个停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;                    // 8个数据位
    USART_Init(USART1, &USART_InitStruct);
    
    /* 使能串口DMARx接收DMATx发送和请求 */
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    
    /* 使能串口IDLE空闲中断和NVIC */
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    /* 使能串口1 */
    USART_Cmd(USART1, ENABLE);
}

/**
 * 串口发送一个字节的数据
 */
void USART_SendChar(uint8_t ch)
{
    /* 发送一个字节的数据 */
    USART_SendData(USART1, ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/**
 * 串口发送一个字符串的数据
 */
void USART_SendString(uint8_t *str)
{
    /* 发送多个字节的数据 */
    while (*str!= '\0')
    {
        USART_SendChar(*str++);
    }
}

/**
 * 串口发送多个字节的数据
 */
void USART_SendArray(uint8_t *array, uint16_t len)
{
    /* 发送一组数据 */
    for (uint16_t i = 0; i < len; i++)
    {
        USART_SendChar(array[i]);
    }
}

/**
 * 对printf函数进行重定向
 */
int fputc(int ch, FILE *f)
{
    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    
    /* 发送一个字节的数据 */
    USART_SendData(USART1, (uint8_t)ch);
    return ch;
}

/**
 * 串口1的空闲中断服务函数
 */
void USART1_IRQHandler(void)
{
    uint8_t Receive_Data;
    if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE))
    {   
        Receive_Data = USART1->SR;
        Receive_Data = USART1->DR; //清除中断标志位IDLE
        
//      Index = Buffer_Size - DMA_GetCurrDataCounter(DMA1_Channel5);//获取接收到的数据个数
        Index = Buffer_Size -(DMA1_Channel5->CNDTR);                //获取接收到的数据个数
        DMA1_Chanael4_Count(Index);                                 //将数据发送出去
        DMA1_Chanael5_Count(Buffer_Size);                           //启动第二轮的数据接收搬运
    }
}

④主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "MyDMA.h"

int main(void)
{
    UART1_Init();
    DMA1_Init();
 
	while(1)
	{ 
     
	}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2250797.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

加载不同本地gltf模型,模型内容不更新的解决方案

相关链接 http://mars3d.cn/editor-vue.html?keyex_6_2_2&idlayer-graphic/draw/draw-model 问题内容 加载本地gltf模型的时候&#xff0c;不clear图层&#xff0c;再打开其他本地gltf&#xff0c;gltf的内容就不更新 重现步骤 进入官网示例&#xff0c;贴入以下代码…

可视化建模以及UML期末复习篇----相关软件安装

作为一个过来人&#xff0c;我的建议是别过来。 一、可视化建模 <1>定义: 官方&#xff1a;一种使用图形符号来表示系统结构和行为的建模技术。 我&#xff1a;其实说白了就是把工作流程用图形画出来。懂不&#xff1f; <2>作用: 提高理解和分析复杂系统的能力。促…

AI开发 - GPT之魂 用Python 演示chatGPT的自注意力机制 - 机器学习

自注意力机制&#xff08;Self-Attention&#xff09;就是让模型在处理每个词时&#xff0c;学会“关注重点”&#xff0c;而不是平均地对每个词一视同仁。这种机制让 GPT 能更聪明地理解句子的上下文和语义之间的关系。 自注意力机制是 GPT 的核心&#xff0c;它帮助模型在理解…

Web 表单开发全解析:从基础到高级掌握 HTML 表单设计

文章目录 前言一、什么是 Web 表单?二、表单元素详解总结前言 在现代 Web 开发中,表单 是用户与后端服务交互的重要桥梁。无论是用户登录、注册、搜索,还是提交反馈,表单都无处不在。在本文中,我们将从基础入手,全面解析表单的核心知识点,并通过示例带你轻松掌握表单开…

HCIE:详解OSPF,从基础到高级特性再到深入研究

目录 前言 一、OSPF协议基本原理 简介 基本原理 OSPF路由器类型 OSPF网络类型 OSPF报文类型和封装 OSPF邻居的建立的维护 DR和BDR的选举 伪节点 LSDB的更新 OSPF的配置 二、OSPF的高级特性 虚连接&#xff08;Virtual-Link&#xff09; OSPF的LSA和路由选择 OSPF…

C#读取本地图像的方法总结

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在C#开发C#程序的时候&#xff0c;有时候需要读取本地图像&#xff0c;下面进行详…

scrapy爬虫框架小案例

豆瓣案例 一、scrapy安装二、scrapy的基本使用&#xff08;爬虫项目创建->爬虫文件创建->运行 爬虫项目结构 response的属性和方法&#x1f31f;&#xff09;1、创建项目2、创建爬虫文件3、scrapy项目的结构4、运行爬虫文件5、response的属性和方法&#xff08;爬虫的处…

服务器实现ssh证书登录

1.生成公钥和私钥 ssh-keygen -t rsa 提示默认生成位置为/root/.ssh/id_rsa ,直接回车。(也可以自己修改) 提示输入证书的密码&#xff0c;可以留空&#xff0c;建议输入&#xff0c;如果输入了&#xff0c;则需要再次确认&#xff0c;记住这个证书密码&#xff08;证书再加…

css:转换

转换 移动 /* transform: translate(100px, 200px); */transform: translateX(100px);transform: translateY(100px); /*一个意思*/ 如果后面跟百分数的意思是移动盒子自身x/y方向长度的百分比&#xff0c;可以用作子绝父相控制盒子水平居中垂直居中 translate里的xy值是相对…

音视频相关的一些基本概念

音视频相关的一些基本概念 文章目录 音视频相关的一些基本概念RTTH264profile & levelI帧 vs IDRMP4 封装格式AAC封装格式TS封装格式Reference RTT TCP中的RTT指的是“往返时延”&#xff08;Round-Trip Time&#xff09;&#xff0c;即从发送方发送数据开始&#xff0c;到…

Flink--API 之Transformation-转换算子的使用解析

目录 一、常用转换算子详解 &#xff08;一&#xff09;map 算子 &#xff08;二&#xff09;flatMap 算子 &#xff08;三&#xff09;filter 算子 &#xff08;四&#xff09;keyBy 算子 元组类型 POJO &#xff08;五&#xff09;reduce 算子 二、合并与连接操作 …

工业公辅车间数智化节能头部企业,蘑菇物联选择 TDengine 升级 AI 云智控

小T导读&#xff1a;在工业节能和智能化转型的浪潮中&#xff0c;蘑菇物联凭借其自研的灵知 AI 大模型走在行业前沿&#xff0c;为高能耗设备和公辅能源车间提供先进的 AI 解决方案。此次采访聚焦于蘑菇物联与 TDengine 的合作项目&#xff0c;通过 AI 云智控平台的建设&#x…

TensorFlow实战:黄文坚版Python代码详解

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;本书由黄文坚撰写&#xff0c;深入介绍了TensorFlow的使用方法。TensorFlow是谷歌开发的开源库&#xff0c;用于数值计算和机器学习&#xff0c;特别是在深度学习方面。书中通过丰富的实例和详细解释&#xff0c…

Java项目中加缓存

Java项目中加缓存 1.更新频率低&#xff1b;但读写频率高的数据很适合加缓存&#xff1b; 2.可以加缓存的地方很多&#xff1a;浏览器的缓存&#xff1b;CDN的缓存&#xff1b;服务器的缓存&#xff1b; 本地内存&#xff1b;分布式远端缓存&#xff1b; 加缓存的时候不要…

Elasticearch索引mapping写入、查看、修改

作者&#xff1a;京东物流 陈晓娟 一、ES Elasticsearch是一个流行的开源搜索引擎&#xff0c;它可以将大量数据快速存储和检索。Elasticsearch还提供了强大的实时分析和聚合查询功能&#xff0c;数据模式更加灵活。它不需要预先定义固定的数据结构&#xff0c;可以随时添加或修…

PyMOL操作手册

PyMOL 操作手册 The man will be silent, the woman will be tears. – itwangyang ​ 翻译整理&#xff1a;itwangyanng 2024 年 11月 29 日 目录 初识 PyMOL… 5 0.1 安装 PyMOL… 5 0.1.1 Windows 系统开源版 PyMOL 的安装… 5 0.1.2 教育版 PyMOL 的下载安装……

RabbitMQ原理架构解析:消息传递的核心机制

文章目录 一、RabbitMQ简介1.1、概述1.2、特性 二、RabbitMQ原理架构三、RabbitMQ应用场景3.1、简单模式3.2、工作模式3.3、发布订阅3.4、路由模式3.5 主题订阅模式 四、同类中间件对比五、RabbitMQ部署5.1、单机部署5.2、集群部署&#xff08;镜像模式&#xff09;5.3、K8s部署…

《白帽子讲Web安全》15-16章

《白帽子讲Web安全》15-16章 《白帽子讲Web安全》15章15、Web Server配置安全15.1、Apache安全15.2、Nginx安全15.3、jBoss远程命令执行15.4、Tomcat远程命令执行15.5、HTTP Parameter Pollution15.6、小结 第四篇 互联网公司运营安全《白帽子讲Web安全》16章16、互联网业务安全…

使用R语言进行美国失业率时空分析(包括绘图)

今天写一篇利用R语言&#xff0c;针对面板数据的简单分析与绘图。让我们直接开始把。 一、数据准备 这次的示例数据非常简单&#xff0c;只有一个shp格式的美国区县矢量数据&#xff0c;我们在QGIS中打开数据查看一下它的属性表。事实上我们需要的数据都在属性表的字段中。 二…

计算机毕业设计Python+LSTM天气预测系统 AI大模型问答 vue.js 可视化大屏 机器学习 深度学习 Hadoop Spark

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…