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时,可以通过DMA1进行数据搬运,以防止数据的丢失。如…...
【C++】 算术操作符与数据类型溢出详解
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯C 算术操作符详解基本算术操作符整数除法与取模行为类型转换在算术运算中的作用自增与自减操作符 💯数值溢出:当值超出类型范围时数据类型的取值范围…...
柔性芯片:实现万物互联的催化剂
物联网 (IoT) 市场已经非常成熟,麦肯锡预测,物联网将再创高峰,到 2030 年将达到 12.5 万亿美元的估值。然而,万物互联 (IoE) 的愿景尚未实现,即由数十亿台智能互联设备组成,提供大规模洞察和效率。 究竟是…...
FFmpeg 简介与编译
1. ffmpeg 简介: FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移…...
低代码与微服务融合在医疗集团中的补充应用探究
摘要 本论文深入探讨了低代码与微服务融合在医疗系统集群中的应用。分析了其优势,包括提高开发效率、降低技术门槛、灵活适应需求变化和易于维护扩展等;阐述了面临的挑战,如数据安全与隐私保护、技术应用复杂性等;并展望了其在医…...
速盾:高防cdn的搜索引擎回源是什么?
高防CDN(Content Delivery Network)是一种用于加速网站访问速度和增加安全性的服务,它通过将静态和动态内容缓存在全球分布的服务器上,从而将用户请求的响应时间降至最低,并提供有效的防御攻击的能力。在实际使用过程中…...
减少电路和配电系统谐波的五种方法
K 级变压器 ANSI 标准 C57.110-1986 定义了 K 系数来评估电路消耗多少谐波电流并确定该谐波电流的热效应。根据电路 K 系数,变压器按 K 等级制造。值得注意的是,K 级变压器不会减少谐波。K 等级表示变压器承受谐波有害影响的相对能力。K级变压器增加了铁…...
基于Java Springboot Vue3图书管理系统
一、作品包含 源码数据库设计文档万字全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue3、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA 数据库&#x…...
vue-cli项目质量约束配置
同步发布于我的网站 🚀 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优化,接下来我们继续编辑器创建资源的UGUI优化 UI篇(UGUI) 优化UGUI应从哪些方面入手? 可以从CPU和GPU两方面考虑,CPU方面,避免触发或减少Canvas的Rebuild和Rebatch,…...
【docker】docker build上下文
什么是 Docker Build 上下文? 在 Docker 中,构建上下文(Build Context) 是指在执行 docker build 命令时,Docker 会发送给 Docker 引擎的所有文件和目录的集合。构建上下文包含了 Dockerfile 和用于构建镜像的所有文件…...
ESLint 配置文件全解析:格式、层叠与扩展(3)
配置文件系统处于一个更新期,存在两套配置文件系统,旧的配置文件系统适用于 v9.0.0 之前的版本,而新的配置文件系统适用于 v9.0.0之后的版本,但是目前还处于 v8.x.x 的大版本。 配置文件格式 在 ESLint 中,支持如下格…...
org.apache.commons.lang3包下的StringUtils工具类的使用
前言 相信平时在写项目的时候,一定使用到StringUtils.isEmpty();StringUtils.isBlank();但是你真的了解他们吗? 也许你两个都不知道,也许你除了isEmpty/isNotEmpty/isNotBlank/isBlank外,并不知道还有isAnyEmpty/isNon…...
HarmonyOS4+NEXT星河版入门与项目实战(23)------组件转场动画
文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、实现效果4、总结1、控件图解 这里我们用一张完整的图来汇整 组件转场动画的用法格式、属性和事件,如下所示: 2、案例实现 这里我们对上一节小鱼游戏进行改造,让小鱼在游戏开始的时候增加一个转场动画,让小鱼自…...
十一、快速入门go语言之接口和反射
文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 📅 2024年5月9日 📦 使用版本为1.21.5 接口 十、Java类的封装和继承、多态 - 七点半的菜市…...
智能化图书馆导航系统方案之系统架构与核心功能设计
hello~这里是维小帮,点击文章最下方获取图书馆导航系统解决方案!如有项目需求和技术交流欢迎大家私聊我们~撒花! 针对传统图书馆在图书查找困难、座位紧张、空间导航不便方面的问题,本文深入剖析了基于高精度定位、3D建模、图书搜…...
学习嵩山版《Java 开发手册》:编程规约 - 命名风格(P13 ~ P14)
概述 《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结,他旨在提升开发效率和代码质量 《Java 开发手册》是一本极具价值的 Java 开发规范指南,对于提升开发者的综合素质和代码质量具有重要意义 学习《Java 开发手册》是一个提升 Jav…...
Qt关于padding设置不起作用的的解决办法
观察以下的代码: 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 语言嵌套循环的格式: for [condition | ( init; condition; increment ) | Range] {for [condition | ( init; con…...
Python Web 开发:FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计
Python Web 开发:FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计 目录 🚀 HTTP 协议概述🌐 HTTP 请求与响应的工作原理🛠️ RESTful API 设计理念🗂️ JSON 格式数据的传输与解析 1. 🚀 HTTP 协议概…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...
mq安装新版-3.13.7的安装
一、下载包,上传到服务器 https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.13.7/rabbitmq-server-generic-unix-3.13.7.tar.xz 二、 erlang直接安装 rpm -ivh erlang-26.2.4-1.el8.x86_64.rpm不需要配置环境变量,直接就安装了。 erl…...
【见合八方平面波导外腔激光器专题系列】用于干涉光纤传感的低噪声平面波导外腔激光器2
----翻译自Mazin Alalus等人的文章 摘要 1550 nm DWDM 平面波导外腔激光器具有低相位/频率噪声、窄线宽和低 RIN 等特点。该腔体包括一个半导体增益芯片和一个带布拉格光栅的平面光波电路波导,采用 14 引脚蝶形封装。这种平面波导外腔激光器设计用于在振动和恶劣的…...
