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

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

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_PeripheralDSTDMA_InitStruct.DMA_BufferSize = 0;                                  //传输计数器的大小,代表搬运数据的个数,先置为0DMA_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的通道4DMA_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_PeripheralSRCDMA_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_PeripheralDSTDMA_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的通道5DMA_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_PeripheralSRCDMA_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的通道5DMA_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_PeripheralDSTDMA_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的通道4DMA_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){ }
}

在这里插入图片描述

相关文章:

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

DMA串口收发数据 1、DMA串口发送数据2、DMA中断串口接收定长数据包3、串口空闲中断DMA接收不定长数据包4、串口空闲中断DMA接收不定长数据包DMA发送数据包 1、DMA串口发送数据 当串口的波特率大于115200时&#xff0c;可以通过DMA1进行数据搬运&#xff0c;以防止数据的丢失。如…...

【C++】 算术操作符与数据类型溢出详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;C 算术操作符详解基本算术操作符整数除法与取模行为类型转换在算术运算中的作用自增与自减操作符 &#x1f4af;数值溢出&#xff1a;当值超出类型范围时数据类型的取值范围…...

柔性芯片:实现万物互联的催化剂

物联网 (IoT) 市场已经非常成熟&#xff0c;麦肯锡预测&#xff0c;物联网将再创高峰&#xff0c;到 2030 年将达到 12.5 万亿美元的估值。然而&#xff0c;万物互联 (IoE) 的愿景尚未实现&#xff0c;即由数十亿台智能互联设备组成&#xff0c;提供大规模洞察和效率。 究竟是…...

FFmpeg 简介与编译

1. ffmpeg 简介&#xff1a; FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0c;为了保证高可移…...

低代码与微服务融合在医疗集团中的补充应用探究

摘要 本论文深入探讨了低代码与微服务融合在医疗系统集群中的应用。分析了其优势&#xff0c;包括提高开发效率、降低技术门槛、灵活适应需求变化和易于维护扩展等&#xff1b;阐述了面临的挑战&#xff0c;如数据安全与隐私保护、技术应用复杂性等&#xff1b;并展望了其在医…...

速盾:高防cdn的搜索引擎回源是什么?

高防CDN&#xff08;Content Delivery Network&#xff09;是一种用于加速网站访问速度和增加安全性的服务&#xff0c;它通过将静态和动态内容缓存在全球分布的服务器上&#xff0c;从而将用户请求的响应时间降至最低&#xff0c;并提供有效的防御攻击的能力。在实际使用过程中…...

减少电路和配电系统谐波的五种方法

K 级变压器 ANSI 标准 C57.110-1986 定义了 K 系数来评估电路消耗多少谐波电流并确定该谐波电流的热效应。根据电路 K 系数&#xff0c;变压器按 K 等级制造。值得注意的是&#xff0c;K 级变压器不会减少谐波。K 等级表示变压器承受谐波有害影响的相对能力。K级变压器增加了铁…...

基于Java Springboot Vue3图书管理系统

一、作品包含 源码数据库设计文档万字全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue3、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA 数据库&#x…...

vue-cli项目质量约束配置

同步发布于我的网站 &#x1f680; package.json scriptslint-stageddevDependencies git-hooksno-eslintdevDependencies - scssdevDependencies - lessengines pre-commit.eslintrc.js.stylelintrc scssless vue.config.jsREADME.md package.json scripts "scripts&…...

第七课 Unity编辑器创建的资源优化_UI篇(UGUI)

上期我们学习了简单的Scene优化&#xff0c;接下来我们继续编辑器创建资源的UGUI优化 UI篇&#xff08;UGUI&#xff09; 优化UGUI应从哪些方面入手&#xff1f; 可以从CPU和GPU两方面考虑&#xff0c;CPU方面&#xff0c;避免触发或减少Canvas的Rebuild和Rebatch&#xff0c…...

【docker】docker build上下文

什么是 Docker Build 上下文&#xff1f; 在 Docker 中&#xff0c;构建上下文&#xff08;Build Context&#xff09; 是指在执行 docker build 命令时&#xff0c;Docker 会发送给 Docker 引擎的所有文件和目录的集合。构建上下文包含了 Dockerfile 和用于构建镜像的所有文件…...

ESLint 配置文件全解析:格式、层叠与扩展(3)

配置文件系统处于一个更新期&#xff0c;存在两套配置文件系统&#xff0c;旧的配置文件系统适用于 v9.0.0 之前的版本&#xff0c;而新的配置文件系统适用于 v9.0.0之后的版本&#xff0c;但是目前还处于 v8.x.x 的大版本。 配置文件格式 在 ESLint 中&#xff0c;支持如下格…...

org.apache.commons.lang3包下的StringUtils工具类的使用

前言 相信平时在写项目的时候&#xff0c;一定使用到StringUtils.isEmpty()&#xff1b;StringUtils.isBlank();但是你真的了解他们吗&#xff1f; 也许你两个都不知道&#xff0c;也许你除了isEmpty/isNotEmpty/isNotBlank/isBlank外&#xff0c;并不知道还有isAnyEmpty/isNon…...

HarmonyOS4+NEXT星河版入门与项目实战(23)------组件转场动画

文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、实现效果4、总结1、控件图解 这里我们用一张完整的图来汇整 组件转场动画的用法格式、属性和事件,如下所示: 2、案例实现 这里我们对上一节小鱼游戏进行改造,让小鱼在游戏开始的时候增加一个转场动画,让小鱼自…...

十一、快速入门go语言之接口和反射

文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 &#x1f4c5; 2024年5月9日 &#x1f4e6; 使用版本为1.21.5 接口 十、Java类的封装和继承、多态 - 七点半的菜市…...

智能化图书馆导航系统方案之系统架构与核心功能设计

hello~这里是维小帮&#xff0c;点击文章最下方获取图书馆导航系统解决方案&#xff01;如有项目需求和技术交流欢迎大家私聊我们~撒花&#xff01; 针对传统图书馆在图书查找困难、座位紧张、空间导航不便方面的问题&#xff0c;本文深入剖析了基于高精度定位、3D建模、图书搜…...

学习嵩山版《Java 开发手册》:编程规约 - 命名风格(P13 ~ P14)

概述 《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结&#xff0c;他旨在提升开发效率和代码质量 《Java 开发手册》是一本极具价值的 Java 开发规范指南&#xff0c;对于提升开发者的综合素质和代码质量具有重要意义 学习《Java 开发手册》是一个提升 Jav…...

Qt关于padding设置不起作用的的解决办法

观察以下的代码&#xff1a; MyWidget::MyWidget(QWidget *parent): QWidget{parent},m_btn(new QToolButton(this)) {this->setFixedSize(500,500);m_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);m_btn->setIcon(QIcon("F:tabIcon/person-white.s…...

Golang教程第10篇(语言循环语句-语言循环嵌套)

Go 语言循环嵌套 Go 语言循环语句Go 语言循环语句 Go 语言允许用户在循环内使用循环。接下来我们将为大家介绍嵌套循环的使用。 语法 以下为 Go 语言嵌套循环的格式&#xff1a; for [condition | ( init; condition; increment ) | Range] {for [condition | ( init; con…...

Python Web 开发:FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计

Python Web 开发&#xff1a;FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计 目录 &#x1f680; HTTP 协议概述&#x1f310; HTTP 请求与响应的工作原理&#x1f6e0;️ RESTful API 设计理念&#x1f5c2;️ JSON 格式数据的传输与解析 1. &#x1f680; HTTP 协议概…...

uniapp实现组件竖版菜单

社区图片页面 scroll-view scroll-view | uni-app官网 (dcloud.net.cn) 可滚动视图区域。用于区域滚动。 需注意在webview渲染的页面中&#xff0c;区域滚动的性能不及页面滚动。 <template><view class"pics"><scroll-view class"left"…...

osg、osgearth源码编译(二)

如果比较懒&#xff0c;也可以不看这篇文章&#xff0c;网上应该有很多编译好的库。也可以找我要。 本人还是建议学会编译&#xff0c;因为其他人电脑上编译好的&#xff0c;可能在你的电脑环境上&#xff0c;出现这样那样奇怪的问题&#xff0c;所以&#xff0c;最好还是自己能…...

从单一设备到万物互联:鸿蒙生态崛起的未来之路

目录 一、引言&#xff1a;开启智能时代的钥匙 二、鸿蒙生态概述&#xff1a;跨设备协同的核心价值 三、开发者机遇与挑战&#xff1a;抓住鸿蒙崛起的机会 四、鸿蒙生态崛起的前景&#xff1a;万物互联的未来 五、开发者在鸿蒙生态中的实践机遇与挑战 1. 跨设备开发的机遇…...

Kotlin的object修饰符定义类似Java的静态类/静态方法

Kotlin的object修饰符定义类似Java的静态类/静态方法 //类似Java的static类 object StaticCls {//类似Java静态变量private var num 0//类似Java的静态方法fun updateVal(n: Int) {num n}fun getVal(): Int {return num} }class MyTest() {fun setVal() {StaticCls.updateVal…...

html 中的 <code>标签

定义和用途 <code> 标签是HTML中的一个内联元素&#xff0c;用于定义计算机代码片段。当你需要在网页内容中展示代码&#xff0c;比如编程语言代码&#xff08;如JavaScript、Python、Java等&#xff09;、命令行指令、标记语言代码&#xff08;如HTML、XML等&#xff09…...

【Oracle11g SQL详解】GROUP BY 和 HAVING 子句:分组与过滤

GROUP BY 和 HAVING 子句&#xff1a;分组与过滤 在 Oracle 11g 中&#xff0c;GROUP BY 子句用于根据一个或多个列对查询结果进行分组&#xff0c;而 HAVING 子句用于对分组后的结果进行过滤。这两者常结合聚合函数使用&#xff0c;用以实现复杂的数据统计和分析。本文将系统…...

SSE基础配置与使用

什么是 Server-Sent Events (SSE) **Server-Sent Events (SSE) **是一种轻量的服务器向客户端推送消息的机制&#xff0c;基于 HTTP 协议实现单向通信&#xff0c;适用于需要实时更新的场景。 与 WebSocket 不同&#xff0c;SSE 只允许服务器向客户端发送数据&#xff0c;因此…...

Android -- 简易音乐播放器

Android – 简易音乐播放器 播放器功能&#xff1a;* 1. 播放模式&#xff1a;单曲、列表循环、列表随机&#xff1b;* 2. 后台播放&#xff08;单例模式&#xff09;&#xff1b;* 3. 多位置同步状态回调&#xff1b;处理模块&#xff1a;* 1. 提取文件信息&#xff1a;音频文…...

【开源免费】基于Vue和SpringBoot的技术交流分享平台(附论文)

博主说明&#xff1a;本文项目编号 T 053 &#xff0c;文末自助获取源码 \color{red}{T053&#xff0c;文末自助获取源码} T053&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…...

Python异步编程新写法:asyncio模块的最新实践

Python异步编程新写法&#xff1a;asyncio模块的最新实践 引言1. 异步编程基础2. 旧写法的问题3. 最新的写法4. 代码解析5. 最佳实践6. 总结7. 参考资料 引言 在现代编程中&#xff0c;异步编程已经成为提高程序性能和响应能力的重要手段。Python的asyncio模块为开发者提供了一…...