当前位置: 首页 > 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 协议概…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...