功能描述:通过STM32F405RGT6的USART1与DMA相结合,方便高效地接收数据。
特别注意:DMA传输时,会根据设置的存储器接收数据的单位(DMA_MemoryDataSize)将FIFO中的值传入接收缓存区(RxBuff)中。当存储器接收数据的单位不是字节时,最后几个字节可能不是这次传输的数据,而是上一次最后传输时存储在FIFO中的数据。
- main.c文件内容如下
#include "stm32f4xx.h" // Device header #include "MyDMA.h" #include "Serial.h" uint8_t data[100] = {0}; //数据接收指针 int main(void) { Serial_Init(); //初始化串口 Serial_SendChinese(USART1, "现在正在进行串口功能测试。"); //发送中文字符串 Serial_SendString(USART1, "\r\n"); //回车换行 while(1) { if(Serial_GetRxFlag() == 1) { memset(data, 0, sizeof(data)); //清空data,避免上一次数据的干扰 Serial_GetRxData(data); //将接收缓冲区的数据复制到data中来 Serial_ClearRxFlag(); //清除接收标志位 //回传接收到的数据和数据大小 Serial_Printf(USART1, "%s\n%d\n", data, Serial_GetRxSize()); } } }
- Serial.c文件的内容如下
#include "Serial.h" uint8_t RxBuff1[Serial_Buffer_Size]={0}; uint16_t RxSize = 0; uint8_t RxFlag1 = 0; /** * @摘要 初始化USART * @参数 无 * @返回值 无 * @说明 无 */ void Serial_Init(void) { //开启GPIOA时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //将PA9配置为复用模式,复用为USART1的TX引脚 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //将PA10配置为复用模式,复用为USART1的RX引脚 GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //定义GPIO初始化结构体 GPIO_InitTypeDef GPIO_InitStructure; //初始化上述结构体 GPIO_StructInit(&GPIO_InitStructure); //配置GPIO模式为复用模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //配置GPIO输出类型为推挽输出 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //配置GPIO输入类型为上拉输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //配置GPIO的速度为快速50MHZ GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; //指定GPIO引脚为Pin9和Pin10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //初始化对应的GPIO GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化DMA MyDMA_Init((uint32_t)&(USART1->DR), (uint32_t)RxBuff1, Serial_Buffer_Size); //开启USART1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //定义USART初始化结构体 USART_InitTypeDef USART_InitStructure; //初始化上述结构体 USART_StructInit(&USART_InitStructure); //配置波特率为115200 USART_InitStructure.USART_BaudRate = 115200; //配置数据长度为8位 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //配置校验位为无 USART_InitStructure.USART_Parity = USART_Parity_No; //配置停止位为1位 USART_InitStructure.USART_StopBits = USART_StopBits_1; //配置串口模式为接收和发送模式 USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //配置硬件流控制为无 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //初始化对应的USART USART_Init(USART1, &USART_InitStructure); //使能USART1的线路空闲中断到NVIC USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //NVIC优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //定义NVIC初始化结构体 NVIC_InitTypeDef NVIC_InitStructure; //选择NVIC通道为USART1_IRQn NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //使能该通道 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //配置抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //配置响应优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //初始化NVIC NVIC_Init(&NVIC_InitStructure); //开启USART1的接收DMA请求 USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //开启USART1 USART_Cmd(USART1, ENABLE); } /** * @摘要 串口发送一个字节数据 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 Byte: 要发送的字节数据 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendByte(USART_TypeDef* USARTx, uint8_t Byte) { uint16_t timeout = 10000; USART_SendData(USARTx, Byte); while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET) { timeout--; if(timeout == 0) return 0; } return 1; } uint8_t Serial_SendArray(USART_TypeDef* USARTx, uint8_t *array, uint16_t length) { uint8_t state = 0; for(uint16_t i=0; i<length; i++) { state = Serial_SendByte(USARTx, array[i]); if(state == 0) return 0; } return 1; } /** * @摘要 串口发送一个字符串 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 str: 要发送的字符串 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendString(USART_TypeDef* USARTx, const char *str) { uint8_t state = 0; for(uint16_t i=0; str[i]!='\0'; i++) { state = Serial_SendByte(USARTx, str[i]); if(state == 0) return 0; } return 1; } /** * @摘要 串口发送一个无符号整数 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 Number:指定要发送的数字,范围:0~4294967295 * @参数 type: 要以什么进制发送上述整数,比如2,8,10,16 * @参数 length:要发送的整数的长度 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendNum(USART_TypeDef* USARTx, uint32_t Number, uint8_t type, uint8_t length) { uint8_t state = 0; uint8_t temp; for(uint8_t i=0; i<length; i++) { temp = (Number/Pow(type,length-i-1))%type; if(temp > 9) state = Serial_SendByte(USARTx, temp-10+'A'); else state = Serial_SendByte(USARTx, temp+'0'); if(state == 0) return 0; } return 1; } /** * @摘要 串口发送一个有符号整数 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 Number:要发送的整数 * @参数 length:要发送的整数的长度 * @参数 ShowSign:是否显示'+'号,ENABLE(显示)或DISABLE(不显示) * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 Number会以十进制格式发送出去 */ uint8_t Serial_SendSignedNum(USART_TypeDef* USARTx, int32_t Number, uint8_t length, FunctionalState ShowSign) { uint8_t state = 0; if(Number < 0) { state = Serial_SendByte(USARTx, '-'); if(state == 0) return 0; state = Serial_SendNum(USARTx, -Number,10,length); if(state == 0) return 0; } else { if(ShowSign == ENABLE) { state = Serial_SendByte(USARTx, '+'); if(state == 0) return 0; state = Serial_SendNum(USARTx, Number,10,length); if(state == 0) return 0; } else { state = Serial_SendNum(USARTx, Number,10,length); if(state == 0) return 0; } } return 1; } /** * @摘要 串口发送一个小数 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 Number:要发送的小数,范围:-4294967295.0~4294967295.0 * @参数 type:要以什么进制发送上述小数,比如2,8,10,16 * @参数 IntLength:指定数字的整数部分长度 * @参数 FraLength:指定数字的小数部分长度,小数进行去尾补0显示 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendFloatNum(USART_TypeDef* USARTx, double Number,uint8_t type, uint8_t IntLength, uint8_t FraLength) { uint8_t state = 0; uint32_t tempNum = Number; uint8_t temp; for(uint8_t i=0; i<IntLength; i++) { temp = (tempNum/Pow(type,IntLength-i-1))%type; if(temp > 9) state = Serial_SendByte(USARTx, temp-10+'A'); else state = Serial_SendByte(USARTx, temp+'0'); if(state == 0) return 0; } state = Serial_SendByte(USARTx, '.'); if(state == 0) return 0; tempNum = (Number-tempNum)*Pow(10,FraLength); for(uint8_t i=0; i<FraLength; i++) { tempNum = tempNum*type; temp = tempNum/Pow(10,FraLength); tempNum = tempNum-tempNum/Pow(10,FraLength)*Pow(10,FraLength); if(temp > 9) state = Serial_SendByte(USARTx, temp-10+'A'); else state = Serial_SendByte(USARTx, temp+'0'); if(state == 0) return 0; } return 1; } /** * @摘要 这是一个可变参数函数,用于格式化发送字符串 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 format:字符串 * @参数 ...:可变参数 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_Printf(USART_TypeDef* USARTx, char* format,...) { uint8_t state = 0; char str[100]; va_list arg; va_start(arg,format); vsprintf(str,format,arg); va_end(arg); state = Serial_SendString(USART1,str); if(state == 0) return 0; return 1; } /** * @摘要 串口发送中文字符串(或一个字符) * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 Chinese:要发送的中文字符串 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendChinese(USART_TypeDef* USARTx, char *Chinese) { uint8_t state = 0; uint8_t cnt = 0, pChinese = 0, SingleChinese[4] = {0}; uint8_t index; for(uint8_t i=0; Chinese[i]!='\0'; i++) { SingleChinese[pChinese] = Chinese[i]; pChinese++; if(pChinese>=Serial_CHN_CHAR_WIDTH) { pChinese = 0; cnt++; for(index=0; strcmp(Serial_Chinese[index].Index, "") != 0; index++) { if(strcmp(Serial_Chinese[index].Index, (const char*)SingleChinese) == 0) break; } state = Serial_SendArray(USARTx, (uint8_t *)Serial_Chinese[index].Data, 3); if(state == 0) return 0; } } return 1; } /** * @摘要 串口发送数据包 * @参数 USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择) * @参数 package:要发送的数据包 * @返回值 0或1:0代表发送失败,1代表发送成功 * @说明 无 */ uint8_t Serial_SendPackage(USART_TypeDef* USARTx, uint8_t* package) { uint8_t state = 0; state = Serial_SendByte(USART1,'@'); //包头 if(state == 0) return 0; state = Serial_SendArray(USART1, package, 4); //数据 if(state == 0) return 0; state = Serial_SendString(USART1, "\r\n"); //包尾 if(state == 0) return 0; return 1; } /** * @摘要 获取接收数据大小 * @参数 无 * @返回值 接收数据大小(单位:Byte) * @说明 无 */ uint16_t Serial_GetRxSize(void) { return RxSize; } /** * @摘要 获取接收标志位 * @参数 无 * @返回值 接收标志:1(完成接收),0(未完成接收或未接收) * @说明 无 */ uint8_t Serial_GetRxFlag(void) { return RxFlag1; } /** * @摘要 清除接收标志位 * @参数 无 * @返回值 无 * @说明 无 */ void Serial_ClearRxFlag(void) { RxFlag1 = 0; } /** * @摘要 获取接收数据 * @参数 Rxdata:指向存储接收数据地址的指针 * @返回值 无 * @说明 调用此函数会自动清除接收缓冲区 */ void Serial_GetRxData(uint8_t* Rxdata) { //等待相应的DMA数据流传输完成 while(DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2) == RESET); //清除相应的DMA数据流传输完成标志位 DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2); for(uint16_t i=0; i<Serial_Buffer_Size; i++) { if(RxBuff1[i] == 0) break; Rxdata[i] = RxBuff1[i]; RxBuff1[i] = 0; } //重新开启DMA,此时DMA等待硬件触发 DMA_Cmd(DMA2_Stream2, ENABLE); } /** * @摘要 USART1中断服务函数 * @参数 无 * @返回值 无 * @说明 无 */ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET) { //下一句和上一句if判断里的语句共同作用清除了USART_IT_IDLE这个标志位 USART_ReceiveData(USART1); //计算收到的字节数 RxSize = Serial_Buffer_Size - DMA_GetCurrDataCounter(DMA2_Stream2); //失能DMA,此处作用是结束传输过程、标志传输完成 DMA_Cmd(DMA2_Stream2, DISABLE); //接收标志置1 RxFlag1 = 1; } }
- Serial.h文件内容如下
#ifndef __SERIAL_H #define __SERIAL_H #include <String.h> #include <stdarg.h> #include <stdio.h> #include "stm32f4xx.h" // Device header #include "Serial_Data.h" #include "MyMath.h" #include "MyDMA.h" #define Serial_Buffer_Size 1024 void Serial_Init(void); uint8_t Serial_SendByte(USART_TypeDef* USARTx, uint8_t Byte); uint8_t Serial_SendArray(USART_TypeDef* USARTx, uint8_t *array, uint16_t length); uint8_t Serial_SendString(USART_TypeDef* USARTx, const char *str); uint8_t Serial_SendNum(USART_TypeDef* USARTx, uint32_t Number, uint8_t type, uint8_t length); uint8_t Serial_SendSignedNum(USART_TypeDef* USARTx, int32_t Number, uint8_t length,FunctionalState ShowSign); uint8_t Serial_SendFloatNum(USART_TypeDef* USARTx, double Number,uint8_t type, uint8_t IntLength, uint8_t FraLength); uint8_t Serial_SendChinese(USART_TypeDef* USARTx, char *Chinese); uint8_t Serial_Printf(USART_TypeDef* USARTx, char* format,...); void Serial_GetRxData(uint8_t* Rxdata); uint16_t Serial_GetRxSize(void); uint8_t Serial_GetRxFlag(void); void Serial_ClearRxFlag(void); uint8_t* Serial_GetRxPackage(void); #endif
- MyDMA.c文件内容如下
#include "MyDMA.h" /** * @摘要 初始化DMA * @参数 PAddress: 外设地址 * @参数 MAddress: 存储器地址 * @参数 Size: 要传输的数据个数 * @返回值 无 * @说明 无 */ void MyDMA_Init(uint32_t PAddress, uint32_t MAddress,uint16_t Size) { //开启DMA2的时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //定义DMA初始化结构体 DMA_InitTypeDef DMA_InitStructure; //初始化上述结构体 DMA_StructInit(&DMA_InitStructure); //配置DMA通道为通道4 DMA_InitStructure.DMA_Channel = DMA_Channel_4; //配置DMA传输的存储器地址 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)MAddress; //配置存储器接收数据的单位为字节 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //配置存储器地址指针为自增模式 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //配置DMA传输的外设地址 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)PAddress; //配置外设发送数据的单位为字节 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //配置外设地址指针为非自增模式 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //配置存储器突发传输配置为单次传输 DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //配置外设突发传输配置为单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //配置DMA传输方向为外设到存储器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //配置DMA要传输的数据个数 DMA_InitStructure.DMA_BufferSize = Size; //配置DMA为非循环模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //配置DMA为非直接模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //配置FIFO阈值为全满 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //配置该通道的DMA优先级为中 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //等待DMA失能,以确保初始化可以成功 while(DMA_GetCmdStatus(DMA2_Stream2) == ENABLE); //初始化DMA DMA_Init(DMA2_Stream2, &DMA_InitStructure); //开启DMA,此时DMA等待硬件触发 DMA_Cmd(DMA2_Stream2, ENABLE); }
- MyDMA.h文件内容如下
#ifndef __MYDMA_H #define __MYDMA_H #include "stm32f4xx.h" // Device header void MyDMA_Init(uint32_t SAddress, uint32_t DAddress, uint16_t Size); #endif