STM32F407 单片机+DMA+环形缓冲区+GPS报文解析

2023-11-12

本文采用DMA+环形缓冲区对GPS报文进行解析,思路是通过DMA中断接收到GPS报文后,存放到环形缓冲区,然后在主程序中解析GPS报文。解析GPS报文的关键是:将环形缓冲区中的字节转换成字符串,然后在字符串中查找GPS报文头标识(例如:GPGGA)和GPS报文尾部标识(回车换行)。

一、为什么要用DMA接收GPS报文

        我们知道,可以用串口接收GPS报文,但是串口有一个缺点,每接收一个字节就会触发接收中断,或者发送一个字节就会触发空中断或者完成中断,造成单片机的CPU频繁产生中断。

         DMA有什么好处呢?你需要发送数据时,先把所有要发送的数据填充到发送缓冲区,然后告诉DMA,DMA就会将数据发送出去,发送完毕,触发一次中断告诉你发送完成。你需要接收数据时,DMA设备会将串行数据先存放到DMA内部缓冲区中,当接收完毕,会通知你接收完成,并通知你接收了多少个字节,这个时候,你只需要从DMA设备的接收缓冲区中取出接收的数据即可。

         从以上我们可以看到,用DMA接收数据时,接收完毕,DMA产生一次中断;用DMA发送数据时,发送完毕,DMA也只产生一次中断;这样就能大幅度的降低系统响应中断的次数。

           STM32F407 DMA通道关系参见如下链接:

                  https://blog.csdn.net/ba_wang_mao/article/details/104043284

           STM32单片机串口空闲中断接收不定长数据

https://blog.csdn.net/qq_20222919/article/details/109090429

           STM32单片机串口空闲中断+DMA接收不定长数据,参见如下链接:

https://blog.csdn.net/qq_20222919/article/details/109091456?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242

           STM32F103单片机modbus通信示例

https://blog.csdn.net/qq_20222919/article/details/109110998

二、DMA程序

       STM32F407共计6个串口,每个串口都可以配置为DMA方式接收和发送数据。其中:

               UARTX_DMA_Tx_Configuration(void)                    ---> 配置串口DMA接收初始化

               UARTX_DMA_Rx_Configuration(void)                   ---> 配置串口DMA发送初始化

               UARTX_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)   ---> 启动DMA发送


//DMA是高速传输模式,一般是ns级的速度,485是慢速传输,一般是ms级最多是us级的传输
//usart1
//    tx   2---7   channel4		
//    rx   2---5   channel4

//usart2
//    tx   1---6   channel4
//    rx   1---5   channel4

//usart3
//    tx   1---3   channel4
//    rx   1---1   channel4

//uart4
//    tx   1---4   channel4
//    rx   1---2   channel4

//uart5
//    tx   1---7   channel4
//    rx   1---0   channel4

//usart6
//    tx   2---6   channel5
//    rx   2---1   channel5
	
		

uint8_t USART1_DMA_RX_Buffer[USART1_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART1_DMA_TX_Buffer[USART1_DMA_TX_BUFFER_MAX_LENGTH];

uint8_t USART2_DMA_RX_Buffer[USART2_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART2_DMA_TX_Buffer[USART2_DMA_TX_BUFFER_MAX_LENGTH];

uint8_t USART3_DMA_RX_Buffer[USART3_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART3_DMA_TX_Buffer[USART3_DMA_TX_BUFFER_MAX_LENGTH];

uint8_t UART4_DMA_RX_Buffer[UART4_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t UART4_DMA_TX_Buffer[UART4_DMA_TX_BUFFER_MAX_LENGTH];

uint8_t UART5_DMA_RX_Buffer[UART5_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t UART5_DMA_TX_Buffer[UART5_DMA_TX_BUFFER_MAX_LENGTH];

uint8_t USART6_DMA_RX_Buffer[USART6_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART6_DMA_TX_Buffer[USART6_DMA_TX_BUFFER_MAX_LENGTH];






void USART1_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream7);
	while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_4; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART1_DMA_TX_Buffer;//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = USART1_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream7, &DMA_InitStructure);								//初始化DMA Stream
	DMA_Cmd(DMA2_Stream7, DISABLE); 										//开启DMA传输
}



void USART1_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream5);
	while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART1_DMA_RX_Buffer;//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = USART1_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream5 , &DMA_InitStructure);							//初始化DMA_Stream5	
	DMA_Cmd(DMA2_Stream5, ENABLE);  										//开启DMA传输
}



void USART2_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream6);
	while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_4; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART2_DMA_TX_Buffer;//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = USART2_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream6, &DMA_InitStructure);								//初始化DMA Stream
	DMA_Cmd(DMA1_Stream6, DISABLE); 										//开启DMA传输
}



void USART2_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream5);
	while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART2_DMA_RX_Buffer;//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = USART2_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream5 , &DMA_InitStructure);							//初始化DMA_Stream	
	DMA_Cmd(DMA1_Stream5, ENABLE);  										//开启DMA传输
}



void USART3_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA1_Stream3);
	while (DMA_GetCmdStatus(DMA1_Stream3) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_4; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART3_DMA_TX_Buffer;//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = USART3_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream3, &DMA_InitStructure);								//初始化DMA Stream
	DMA_Cmd(DMA1_Stream3, DISABLE); 										//开启DMA传输
}



void USART3_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA1_Stream1);
	while (DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART3_DMA_RX_Buffer;//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = USART3_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream1 , &DMA_InitStructure);							//初始化DMA_Stream	
	DMA_Cmd(DMA1_Stream1, ENABLE);  										//开启DMA传输
}



void UART4_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream4);
	while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_4; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART4_DMA_TX_Buffer;	//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = UART4_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream4, &DMA_InitStructure);								//初始化DMA Stream
	DMA_Cmd(DMA1_Stream4, DISABLE); 										//开启DMA传输
}



void UART4_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream2);
	while (DMA_GetCmdStatus(DMA1_Stream2) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART4_DMA_RX_Buffer;	//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = UART4_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream2 , &DMA_InitStructure);							//初始化DMA_Stream	
	DMA_Cmd(DMA1_Stream2, ENABLE);  										//开启DMA传输
}



void UART5_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream7);
	while (DMA_GetCmdStatus(DMA1_Stream7) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_4; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART5->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART5_DMA_TX_Buffer;	//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = UART5_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream7, &DMA_InitStructure);								//初始化DMA Stream
	DMA_Cmd(DMA1_Stream7, DISABLE);  										//关闭DMA传输	

}



void UART5_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);					//DMA1时钟使能
	DMA_DeInit(DMA1_Stream0);
	while (DMA_GetCmdStatus(DMA1_Stream0) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART5->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART5_DMA_RX_Buffer;	//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = UART5_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA1_Stream0 , &DMA_InitStructure);
	DMA_Cmd(DMA1_Stream0, ENABLE);  										//开启DMA传输
	
}





//可以这么认为,USART6如果DMA的TX使用DMA2_Stream6,则USART6如果DMA的RX必须使用DMA2_Stream1
//                 USART6如果DMA的TX使用DMA2_Stream7,则USART6如果DMA的RX必须使用DMA2_Stream2

void USART6_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream6);
	while (DMA_GetCmdStatus(DMA2_Stream6) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_5; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART6_DMA_TX_Buffer;//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = USART6_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream6 , &DMA_InitStructure);							//初始化DMA Stream
	DMA_Cmd(DMA2_Stream6 , DISABLE); 										//开启DMA传输
}



void USART6_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream1);
	while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_5;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART6_DMA_RX_Buffer;//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = USART6_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//使用普通模式 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//中等优先级 DMA_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream1 , &DMA_InitStructure);							//初始化DMA_Stream5	
	DMA_Cmd(DMA2_Stream1 , ENABLE);  										//开启DMA传输
}



void USART1_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{		
	if (nSendCount < USART1_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(USART1_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA2_Stream7 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA2_Stream7 , nSendCount);  //数据传输量
		DMA_Cmd(DMA2_Stream7 , ENABLE);               		//开启DMA传输
	}
}



void USART2_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{		
	if (nSendCount < USART2_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(USART2_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA1_Stream6 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA1_Stream6 , nSendCount);  //数据传输量
		DMA_Cmd(DMA1_Stream6 , ENABLE);               		//开启DMA传输
	}
}



void USART3_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{		
	if (nSendCount < USART3_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(USART3_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA1_Stream3 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA1_Stream3) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA1_Stream3 , nSendCount);  //数据传输量
		DMA_Cmd(DMA1_Stream3 , ENABLE);               		//开启DMA传输
	}
}



void UART4_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{	
	if (nSendCount < UART4_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(UART4_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA1_Stream4 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA1_Stream4 , nSendCount);  //数据传输量
		DMA_Cmd(DMA1_Stream4 , ENABLE);               		//开启DMA传输
	}
}




void UART5_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{	
	if (nSendCount < UART5_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(UART5_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA1_Stream7 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA1_Stream7) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA1_Stream7 , nSendCount);  //数据传输量
		DMA_Cmd(DMA1_Stream7 , ENABLE);               		//开启DMA传输
	}
}




void USART6_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{	
	if (nSendCount < USART6_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(USART6_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA2_Stream6 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA2_Stream6) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA2_Stream6 , nSendCount);  //数据传输量
		DMA_Cmd(DMA2_Stream6 , ENABLE);               		//开启DMA传输
	}
}

三、串口初始化程序

      不仅要初始化串口,还要初始化DMA接收和DMA发送使能。


void UART4_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	
	USART_DeInit(UART4);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4 , ENABLE); 		//for USART2, USART3, UART4 or UART5.	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
		
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_UART4);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_UART4);     	

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitStructure.USART_BaudRate = 38400;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(UART4, &USART_InitStructure);
	USART_Cmd(UART4, ENABLE);

	USART_ClearFlag(UART4, USART_FLAG_TC);
	while (USART_GetFlagStatus(UART4, USART_FLAG_TC) == RESET);
	USART_ClearFlag(UART4, USART_FLAG_TC);

    USART_ITConfig(UART4, USART_IT_RXNE, DISABLE);
	USART_ITConfig(UART4, USART_IT_TXE, DISABLE);
	USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);
	USART_ITConfig(UART4, USART_IT_TC, ENABLE);
	USART_DMACmd(UART4 ,   USART_DMAReq_Tx,ENABLE);
	USART_DMACmd(UART4 ,   USART_DMAReq_Rx,ENABLE);
}

四、串口中断服务程序

      必须使用串口空中断和串口发送完成中断来判断DMA接收完成或DMA发送完成,不能使用串口其它中断。

      (1)、ch > 0  表示产生DMA接收中断,并且接收的字节数ch 大于0。

      (2)、WriteBufferTo_ringBuffer(GPS_ring , UART4_DMA_RX_Buffer , ch);   将接收到的ch个字节保存到环形缓冲区GPS_ring。

void UART4_IRQHandler(void)
{
	int16_t ch;

	
	if (USART_GetITStatus(UART4 , USART_IT_IDLE) != RESET)
	{		
		USART_ClearITPendingBit(UART4 , USART_IT_IDLE);	//必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
		ch = USART_ReceiveData(UART4);					//必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
		DMA_Cmd(DMA1_Stream2, DISABLE);
		DMA_ClearFlag(DMA1_Stream2 , DMA_FLAG_TCIF2 | DMA_FLAG_FEIF2 | DMA_FLAG_DMEIF2 | DMA_FLAG_TEIF2 | DMA_FLAG_HTIF2);
		ch = UART4_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Stream2);
		if (ch > 0)   
		{
			WriteBufferTo_ringBuffer(GPS_ring , UART4_DMA_RX_Buffer , ch);
		}
		DMA_SetCurrDataCounter(DMA1_Stream2 , UART4_DMA_RX_BUFFER_MAX_LENGTH);
		DMA_Cmd(DMA1_Stream2, ENABLE);
	}	
	else if (USART_GetITStatus(UART4 , USART_IT_TC)!= RESET) 
	{
		USART_ClearITPendingBit(UART4 , USART_IT_TC);
		DMA_ClearFlag(DMA1_Stream4 , DMA_FLAG_TCIF4 | DMA_FLAG_FEIF4 | DMA_FLAG_DMEIF4 | DMA_FLAG_TEIF4 | DMA_FLAG_HTIF4);
		DMA_SetCurrDataCounter(DMA1_Stream4 , 0);
	}
}

五、字符串底层函数

      将环形缓冲区中的字节转换成字符串,在字符串中查找GPS报文头标识(例如:GPGGA)和GPS报文尾部标识(回车换行)



void hex_to_string(uint8_t hex_buffer[] , char *string , uint16_t n)
{
	uint16_t i;	
	char *p = string;


	for (i = 0 ; i < n ; i++)
		*p++ = hex_buffer[i];

	*p = '\0';
}



void string_to_hex(char *string , uint8_t hex_buffer[])
{
	char *p = string;
	uint16_t i = 0;

	
	while (*p != '\0')
		hex_buffer[i++] = *p++;
}



uint8_t ASCII_To_Hex(uint8_t number)
{

	if (number >= '0' && number <= '9')
		return (number - 0x30);

	else if (number >= 'a' && number <= 'f')
		return ((number - 'a') + 10);
	
	else if (number >= 'A' && number <= 'F')
		return ((number - 'A') + 10);

	return (0);
}



uint16_t FIND(char *s , char *t)
{
   uint16_t i;
   uint16_t j;
   int16_t end;


   end = strlen(s) - strlen(t);
   if (end >= 0)
   {
	   for (i = 0 ; i <= end ; i++)
	   {
		   for (j = i ; s[j] == t[j-i] ; j++)
		   {
			   if (t[j-i+1] == '\0')
				  return (i+1);
		   }
	   }
   }
   return (0);
}



uint16_t string_FindChar(char *s , char t)
{
   char *p = s;
   uint16_t i;


   for (i = 0 ; *p != '\0'; p++ , i++)
   {
	  if (*p == t)
		 return (i + 1);
   }
   return (0);
}



uint16_t string_FindCharS(char *s , char t , uint16_t n)
{
   char *p = s;
   uint16_t i;
   uint16_t j;


	for (i = j = 0; (*p != '\0' && i < n); p++ , j++)
	{
		if (*p == t)
			i++;
	}

	if (i == n)
		return (j);
   return (0);
}



void LEFT(char* src , char* dest , uint16_t n)
{
   char *p = src;
   char *q = dest;
   uint16_t i = 0;


   while (*p!='\0' && i < n)
   {
	  *q++ = *p++;
	  i++;
   }
   *q='\0';
}



void RIGHT(char *src , char *dest , uint16_t n)
{
	char *p = src;
	char *q = dest;
	uint16_t len = strlen(src);

	if (n > len) n = len;

	p += (len - n);   			//	从右边第n个字符开始
	while(*p != '\0')
		*(q++) = *(p++);
	*q = '\0';
}



void MID(char *src , char *dest , uint16_t n , uint16_t m)
{
	char *p = src;
	char *q = dest;
	uint16_t len = strlen(src);
	uint16_t j = m;
	uint16_t i = 0;


	if (j > len)
		j = len;

	p += j;
	while(*p != '\0' && i < n)
	{
	  *q++ = *p++;
	  i++;
	}
	*q='\0';
}



void Reverse(char *s)
{
	char c;
	int16_t i ;
	int16_t j ;

	for (i = 0 , j = strlen(s) - 1 ; i < j ; i++ , j--)
	{
		c = s[i];
		s[i] = s[j];
		s[j] = c;
	}
}



uint8_t Get_Comma_string(char *string , char *str , uint16_t No1 , uint16_t No2)
{
	uint16_t comma1;
	uint16_t comma2;

	if (No1 > 0 && No2 > 0 && No1 < No2)
	{
		comma1 = string_FindCharS(string , ',' , No1);
		comma2 = string_FindCharS(string , ',' , No2);
		if (comma1 > 0 && comma2 >0)
		{
			if (comma2 - comma1 >= 1)
			{
				MID(string , str , comma2 - comma1 - 1 , comma1);
				return (1);//TRUE);
			}
		}
	}
	return (0);//FALSE);
}

六、环形缓冲区程序

本环形缓冲区程序支持一次向环形缓冲区写1个字节和多个字节;同时还支持向环形缓冲区读1个字节和读多个字节。


#define MIN(a,b) ( (a) < (b) ) ? (a):(b)
#define RING_MAXLEN     		(1024)



typedef struct tagring_buf_struct
{
	uint8_t *buffer;
	uint16_t volatile size;
	uint16_t volatile rptr;
	uint16_t volatile wptr;
}ring_buf_struct , *ring_buf_struct_ptr;


uint8_t GPS_ring_buffer[RING_MAXLEN];
ring_buf_struct GPS_ring_struct;
ring_buf_struct_ptr GPS_ring;






void ringBufferInit(void)
{	
	
	GPS_ring = &GPS_ring_struct;	
	GPS_ring->size = RING_MAXLEN;
	GPS_ring->buffer = GPS_ring_buffer;
	memset(GPS_ring->buffer , 0x00 , GPS_ring->size);
	GPS_ring->rptr = 0x00;
	GPS_ring->wptr = 0x00;
}



BOOL ringBufferEmpty(ring_buf_struct *ring)
{
	if (ring->rptr == ring->wptr)
		return (TRUE);
	return (FALSE);
}



BOOL ringBufferFull(ring_buf_struct *ring)
{
	if (ring->rptr == ((ring->wptr + 1) % ring->size))
		return (TRUE);
	return (FALSE);
} 
	


BOOL WriteCharTo_ringBuffer(ring_buf_struct *ring , uint8_t ch)
{
	if (ringBufferFull(ring))
		return (FALSE);
	
	ring->buffer[ring->wptr] = ch;
	ring->wptr++;
	ring->wptr = ring->wptr % ring->size;
	return (TRUE);
}



BOOL ReadCharFrom_ringBuffer(ring_buf_struct *ring , uint8_t *ch)
{
	if (ringBufferEmpty(ring))
		return (FALSE);
	
	*ch = ring->buffer[ring->rptr];
	ring->rptr++;
	ring->rptr = ring->rptr % ring->size;
	return (TRUE);
}



uint16_t ringBufferFilllength(ring_buf_struct *ring)
{
	return ((ring->size + ring->wptr - ring->rptr) % ring->size);
}
	


uint16_t ringBufferFreelength(ring_buf_struct *ring)
{
	return (ring->size - ringBufferFilllength(ring) - 1);
}



BOOL WriteBufferTo_ringBuffer(ring_buf_struct *ring , uint8_t *buffer , uint16_t n)
{
	uint16_t i;

	
	if (ringBufferFull(ring))
		return (FALSE);
		
	if (ringBufferFreelength(ring) < n)
		return (FALSE);
	
	i = MIN(n , ring->size - ring->wptr % ring->size);	//从ring->wptr开始到缓冲区结尾的空间
	memcpy(ring->buffer + (ring->wptr % ring->size) , buffer , i);
	if (n > i)
		memcpy(ring->buffer , buffer + i , n - i);		//从缓冲区开始(0)到ring->rptr之前的n-i个有效空间
	ring->wptr = (ring->wptr + n) % ring->size;
	return (TRUE);
}



uint16_t ReadBufferFrom_ringBuffer(ring_buf_struct *ring , uint8_t *buffer , uint16_t n)
{
	uint16_t i;
	uint16_t len;

                      
 	len = MIN(n , ringBufferFilllength(ring));                                   
 	i = MIN(len , ring->size - ring->rptr % ring->size );      
 	memcpy(buffer , ring->buffer + (ring->rptr % ring->size) , i);
	if (len > i)
		memcpy(buffer + i, ring->buffer , len - i); 	
 	ring->rptr = (ring->rptr + len) % ring->size;	
    return (len);	
}

七、GPS结构体


enum GPS_MESSAGE_TYPE {
			enum_GPS_NULL_MESSAGE     = 0 ,
			enum_GPS_GPNTR_MESSAGE    = 1 ,
			enum_GPS_GPRMC_MESSAGE    = 2 ,
			enum_GPS_GPVTG_MESSAGE    = 3 ,
			enum_GPS_GPGGA_MESSAGE    = 4 ,
	        enum_GPS_GPTRA_MESSAGE    = 5 ,
};



union CONVERT 
{
	int32_t long_num;
    int16_t integer[2];
	uint8_t BUF[4];
};




typedef struct tagGPS_STRUCT
{
    uint8_t year;
    uint8_t month;
    uint8_t day;
	uint8_t millisecond;		//GPS的毫秒不准
	
    double x;
    double y;
	double z;
	int16_t	HIGH_AM1;
	int16_t	HIGH_AM2;
	int16_t HDOP;
	
    uint8_t hour;
    uint8_t minute;	
    uint8_t second;	
	uint16_t Angle;	
	uint8_t pos_status;
	int16_t star_amount;
		
} GPS_STRUCT;



typedef struct tagGPS_MESSAGE_TYPE
{
    int16_t head_pos;
    enum GPS_MESSAGE_TYPE enum_message_type;
} GPS_MESSAGE_TYPE_STRUCT;

八、GPS报文解析程序



typedef struct tagCLOCK
{
	uint8_t year;
	uint8_t month;
	uint8_t day;
	uint8_t hour;
	uint8_t minute;
	uint8_t second;
	uint8_t week;		
} CLOCK;


char string_GPNTR[20] = "GNNTR";
char string_GPRMC[20] = "GNRMC";
char string_GPGGA[20] = "GNGGA";
char string_GPVTG[20] = "GNVTG";
char string_GPTRA[20] = "GNTRA";
char string_CRLF[20] = "\r\n";
char string_STAR[20] = "*";
char gps_string[512];
GPS_STRUCT GPS;
CLOCK UTC_time;
CLOCK LOCAL_time;


//------------------------------------------------------------------------
//UTC时间转换为任意时区时间,如果是转换为北京时间,设置timezone=8即可。
//utc_time = UTC时间
//timezone = 时区(就北京时区=8)
//------------------------------------------------------------------------
void UTC_to_BJtime(CLOCK *utc_time, int8_t timezone , CLOCK *local_time)
{
	int16_t year,month,day,hour;
	int16_t lastday = 0;			//last day of this month(本月天数)
	int16_t lastlastday = 0;		//last day of last month(上月天数)

	
    year  = utc_time->year;			//utc time
    month = utc_time->month;
    day   = utc_time->day;
    hour  = utc_time->hour + timezone; 
	
	//1月大,2月小,3月大,4月小,5月大,6月小,7月大,8月大,9月小,10月大,11月小,12月大
	if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12)
	{
		lastday = 31;		//本月天数
		lastlastday = 30;	//这里应该补上上个月的天数
        if (month == 3)
		{
            if ((year%400 == 0) || (year%4 == 0 && year%100 != 0))	//if this is lunar year
                lastlastday = 29;		//上个月的天数(闰年)
            else
                lastlastday = 28;		//上个月的天数(平年【非闰年】)
        }
		
        //if (month == 8)
		if (month == 8 || month == 1)	//这里应该是8月和1月,因为8月和1月的上一个月(7月和12月)的天数是31天
            lastlastday = 31;			//上个月的天数
    }
    else if (month == 4 || month == 6 || month == 9 || month == 11)
	{
        lastday = 30;					//本月天数
        lastlastday = 31;				//上个月的天数
    }
    else
	{
        lastlastday = 31;				//上个月的天数
        if ((year%400 == 0)||(year%4 == 0 && year%100 != 0))
            lastday = 29;				//本月天数(闰年)
        else
            lastday = 28;				//本月天数(平年【非闰年】)
    }

	
    if (hour >= 24)		// 当算出的区时大于或等于24:00时,应减去24:00,日期加一天
	{					// if >24, day+1
		hour -= 24;
        day += 1; 
        if (day > lastday)	// 当算出的日期大于该月最后一天时,应减去该月最后一天的日期
		{ 		// next month,  day-lastday of this month
			day -= lastday;
            month += 1;
            if (month > 12)	// 当算出的月份大于12时,应减去12,年份加上一年
			{		//	next year , month-12
				month -= 12;
                year += 1;                
			}//if (month > 12)
		}//if (day > lastday)
	}//if (hour >= 24)
    
	
	if (hour < 0)	// 当算出的区时为负数时,应加上24:00,日期减一天
	{										// if <0, day-1
		hour += 24;
        day -= 1; 
        if (day < 1)	// 当算出的日期为0时,日期变为上一月的最后一天,月份减去一个月
		{					  // month-1, day=last day of last month
			day = lastlastday;
            month -= 1;
            if (month < 1)	// 当算出的月份为0时,月份变为12月,年份减去一年
			{ 			// last year , month=12
				month = 12;
                year -= 1;
            }//if (month < 1)
        }//if (day < 1)
	}//if (hour < 0)
	
	local_time->year   = year;
	local_time->month  = month;
	local_time->day    = day;
	local_time->hour   = hour;
	local_time->minute = utc_time->minute;
	local_time->second = utc_time->second;
}



//基姆拉尔森计算公式根据日期判断星期几
void CalculateWeekDay(int16_t year , int16_t month , int16_t day)
{
	if (month == 1 || month ==2) 
    {
        month +=12;
        year--;
    }
 
	int16_t iWeek = (day + 2 * month + 3 * (month + 1)/5 + year + year/4 - year/100 + year/400)%7;

	switch (iWeek)
    {
		case 0: break;//printf("星期一\n"); break;
     	case 1: break;//printf("星期二\n"); break;
     	case 2: break;//printf("星期三\n"); break;
     	case 3: break;//printf("星期四\n"); break;
     	case 4: break;//printf("星期五\n"); break;
     	case 5: break;//printf("星期六\n"); break;
     	case 6: break;//printf("星期日\n"); break;
	}
}



void GPS_STRING_Init(void)
{
	char *p = gps_string;

	*p = '\0';
	
}



void GPS_Init(void)
{
	GPS_STRING_Init();	
	memset(&GPS , 0x00 , sizeof(GPS_STRUCT));
	memset(&UTC_time , 0x00 , sizeof(CLOCK));
	memset(&LOCAL_time , 0x00 , sizeof(CLOCK));
}



void GPS_strcat(char * string , uint8_t ch)
{
	char *p = string;
	

	while (*p != '\0')
		p++;
	*p++ = ch;
	*p = '\0';
}	



void Read_GPS_GPRMC_UTC(char *string)
{
	uint8_t x1;
    uint8_t x2;
    char str[100];
    char *p;
    
	
	if (Get_Comma_string(string , str , 1 , 2))
	{
		p = str;   
		x1 = *p++;
		x2 = *p++;
		UTC_time.hour = (x1 - 0x30) * 10 + (x2 - 0x30);
		
		   
		x1 = *p++;
		x2 = *p++;
		UTC_time.minute = (x1 - 0x30) * 10 + (x2 - 0x30);
		
		
		x1 = *p++;
		x2 = *p++;
		UTC_time.second = (x1 - 0x30) * 10 + (x2 - 0x30);
 	}
}



void Read_GPS_GPRMC_DATE(char *string)
{
	uint8_t x9;
    uint8_t x10;
    char str[100];
    char *p;
    
	
	if (Get_Comma_string(string , str , 9 , 10))
	{
		p = str;  
		x9 = *p++;
		x10 = *p++;
		UTC_time.day = (x9 - 0x30) * 10 + (x10 - 0x30);
    
		
		x9 = *p++;
		x10 = *p++;
		UTC_time.month = (x9 - 0x30) * 10 + (x10 - 0x30);
		
		
		x9 = *p++;
		x10 = *p++;
		UTC_time.year = (x9 - 0x30) * 10 + (x10 - 0x30);	
 	}
}



void Read_GPS_GPVTG_speed(char *string)
{
    char str[100];
	float fnum;
	union CONVERT Convert;    
	
	
	if (Get_Comma_string(string , str , 7 , 8))
	{
		fnum = atof(str);					
	}
}



//
// GPTRA 报文格式
//
//$GPTRA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>*hh<CR><LF>
//$GPTRA,090807.00,007.23,-00.27,000.00,4,13,0.00,0004*54
//<1>  UTC 时间  hhmmss.ss(时分秒格式)  104252.00
//<2>  方向角,hhh.hh  0~360 度    044.56   司南磁偏角
//<3>  俯仰角:-90~90度  ppp.pp  -09.74 
//<4>  横滚角:-90~90度  rrr.rr  0 
///<5>  解状态 
//0:无效解; 
//1:单点定位解; 
//2:伪距差分; 
//4:固定解; 
//5:浮动解; 
//<6>  卫星数 
//<7>  差分延迟 
//<8>  差分站ID号0000~1023(前导位数不足则补0,如果不是差分定位将为空)
//<9>  校验数据(以*开始)
//==============================================================
void Read_GPS_GPTRA(char *string )
{
    char str[100];
	float fnum;
   
	
	if (Get_Comma_string(string , str , 2 , 3))
	{
		fnum = atof(str);
		fnum = fnum * 10;
		GPS.Angle  = fnum;			
	}
	
	if (Get_Comma_string(string , str , 5 , 6))
	{	
		GPS.GPTRA_pos_status = atoi(str);		
	}		
	
	if (Get_Comma_string(string , str , 6 , 7))
	{
		GPS.GPTRA_star_amount = atoi(str);		
	}	
}



void Read_GPS_GPGGA_xyz(char *string)
{
	uint8_t x1;
    uint8_t x2;	
	char *p;
	char str[100];
	int16_t inum;
	int32_t lnum1;
	int32_t lnum2;
	float fnum;
	union CONVERT Convert;
	
	
	if (Get_Comma_string(string , str , 1 , 2))
	{
		p = str;  
		x1 = *p++;
		x2 = *p++;
		UTC_time.hour = (x1 - 0x30) * 10 + (x2 - 0x30);
		
		
		x1 = *p++;
		x2 = *p++;
		UTC_time.minute = (x1 - 0x30) * 10 + (x2 - 0x30);
		
		
		x1 = *p++;
		x2 = *p++;
		UTC_time.second = (x1 - 0x30) * 10 + (x2 - 0x30);	
		
		
		p++;												
		x1 = *p++;											
		x2 = *p++;											
		GPS.millisecond = (x1 - 0x30) * 10 + (x2 - 0x30);	
 	}	
	
	if (Get_Comma_string(string , str , 2 , 3))
	{
		RTRIM_ZERO(str);
        //GPS-X   

	}
	
	if (Get_Comma_string(string , str , 4 , 5))
	{
		RTRIM_ZERO(str);
        //GPS-Y   
	}
	
	if (Get_Comma_string(string , str , 6 , 7))
	{	
		GPS.pos_status = atoi(str);		
	}		
	
	if (Get_Comma_string(string , str , 7 , 8))	
	{
		GPS.star_amount = atoi(str);		
	}	
			
	if (Get_Comma_string(string , str , 8 , 9))	
	{
		fnum = atof(str);
		fnum = fnum * 100;
		inum = fnum;
		GPS.HDOP = inum;		
	}	
	
	if (Get_Comma_string(string , str , 9 , 10))
	{
		RTRIM_ZERO(str);
        //GPS-Z   
	}

	if (Get_Comma_string(string , str , 11 , 12))
	{
		fnum = atof(str);
		fnum = fnum * 10000;
		Convert.long_num = fnum;
		GPS.HIGH_AM1 = Convert.integer[1];		//高位部分
		GPS.HIGH_AM2 = Convert.integer[0];		//低位部分
	}	
}






BOOL GPS_Checksum(char *string , uint16_t head , uint16_t star)
{
	char *p1 = string + head - 1;	
	char *p2;
	char ch;
	uint8_t checksum = 0x00;
	uint8_t sn_lo;
	uint8_t sn_hi;
	uint8_t sn_lo_hex;
	uint8_t sn_hi_hex;	
	uint8_t sn_hex;


	if ((star > head) && strlen(string) >= (star + 2))
	{
		while (*p1 != '*' && *p1 != '\0')
		{
			ch = *p1++;
			checksum = checksum ^ ch;
		}
	
		p2 = p1;
		if (*p2 == '*')
		{				
			p2++;
			sn_hi = *p2++;									//	'37' = 7
			sn_lo = *p2;									//	'42' = B
			sn_hi_hex = ASCII_To_Hex(sn_hi);
			sn_lo_hex = ASCII_To_Hex(sn_lo);
			sn_hex = (sn_hi_hex << 0x04) + sn_lo_hex;
			if (checksum == sn_hex)
				return (TRUE);
		}
	}//if ((star > head) && strlen(string) >= (star + 2))
	return (FALSE);
}



GPS_MESSAGE_TYPE_STRUCT GPS_message_Type(char *string)
{
	GPS_MESSAGE_TYPE_STRUCT gps_message_type;
	
	
	gps_message_type.head_pos = FIND(string , string_GPNTR);
	if (gps_message_type.head_pos)
	{
		gps_message_type.enum_message_type = enum_GPS_GPNTR_MESSAGE;
		return (gps_message_type);
	}
	
	gps_message_type.head_pos = FIND(string , string_GPRMC);
	if (gps_message_type.head_pos)
	{
		gps_message_type.enum_message_type = enum_GPS_GPRMC_MESSAGE;
		return (gps_message_type);
	}
	
	gps_message_type.head_pos = FIND(string , string_GPVTG);
	if (gps_message_type.head_pos)
	{
		gps_message_type.enum_message_type = enum_GPS_GPVTG_MESSAGE;
		return (gps_message_type);
	}
	
	gps_message_type.head_pos = FIND(string , string_GPGGA);
	if (gps_message_type.head_pos)
	{
		gps_message_type.enum_message_type = enum_GPS_GPGGA_MESSAGE;
		return (gps_message_type);
	}
		
	gps_message_type.head_pos = FIND(string , string_GPTRA);
	if (gps_message_type.head_pos)
	{
		gps_message_type.enum_message_type = enum_GPS_GPTRA_MESSAGE;
		return (gps_message_type);
	}
	
	gps_message_type.head_pos = 0x00;
	gps_message_type.enum_message_type = enum_GPS_NULL_MESSAGE;
	return (gps_message_type);
}



void UART4_GPS_Poll(void)
{
	uint8_t ch;		
	BOOL succ_mark = FALSE;
	uint16_t star;
	GPS_MESSAGE_TYPE_STRUCT gps_message_type;
		
	//从环形缓冲区中取出一个字节
	succ_mark = ReadCharFrom_ringBuffer(GPS_ring , &ch);
	if (succ_mark)
	{
		GPS_strcat(gps_string , ch);//从环形缓冲区中取出一个字节,追加到字符串gps_string有尾部,准备解析GPS报文
		if (FIND(gps_string , string_CRLF) || strlen(gps_string) >= 255)			
		{
			gps_message_type = GPS_message_Type(gps_string);
			switch (gps_message_type.enum_message_type)
			{
							
				case enum_GPS_GPRMC_MESSAGE:
					star  = FIND(gps_string , string_STAR);
					if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))
					{
						Read_GPS_GPRMC_UTC(gps_string + gps_message_type.head_pos - 1);
						Read_GPS_GPRMC_DATE(gps_string + gps_message_type.head_pos - 1);

					}//if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))						
					break;
						
				case enum_GPS_GPVTG_MESSAGE:
					star  = FIND(gps_string , string_STAR);
					if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))
					{
						Read_GPS_GPVTG_speed(gps_string + gps_message_type.head_pos - 1);
					}//if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))	
					break;
							
				case enum_GPS_GPGGA_MESSAGE:
					star  = FIND(gps_string , string_STAR);
					if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))
					{
						Read_GPS_GPGGA_xyz(gps_string + gps_message_type.head_pos - 1);
					}//if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))					
					break;
								
				case enum_GPS_GPTRA_MESSAGE:
					star  = FIND(gps_string , string_STAR);
					if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))
					{
						Read_GPS_GPTRA(gps_string + gps_message_type.head_pos - 1);
					}//if (GPS_Checksum(gps_string , gps_message_type.head_pos , star))					
					break;
					
				default:								
					break;
			}//switch (gps_message_type.enum_message_type)
			GPS_STRING_Init();
		}//if (FIND(gps_string , string_CRLF))
	}//if (succ_mark)
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F407 单片机+DMA+环形缓冲区+GPS报文解析 的相关文章

随机推荐

  • 终止代码:DRIVER_IRQL_NOT_LESS_OR_EQUAL 失败的操作:CH341S64.SYS

    终止代码 DRIVER IRQL NOT LESS OR EQUAL 失败的操作 CH341S64 SYS Python串口程序致使电脑蓝屏自动重启 CH430驱动有问题 Python串口程序致使电脑蓝屏自动重启 最近在做一个项目 其中有一
  • Redis入门——Key、五大数据类型图文详解

    文章目录 1 Redis简介 2 Redis常见命令 3 Redis Key关键字 4 五大数据类型简介 4 1 String 字符串 4 2 List 列表 4 3 Set 4 3 Hash 4 4 ZSet 1 Redis简介 Redi
  • 什么是索引?MySQL常见的几种索引类型和原理

    来源 素文宅博客 地址 https blog yoodb com yoodb article detail 1536 在关系数据库中 索引是一种单独的 物理的对数据库表中一列或多列的值进行排序的一种存储结构 它是某个表中一列或若干列值的集合
  • Java---JUC并发篇(多线程详细版)

    Java 多线程 1 并发基础 线程篇 1 1 java线程状态及线程状态之间的转化 1 2 操作系统层面有5种状态 2 线程池的核心参数 7个核心参数 3 sleep与wait方法对比 4 lock锁与synchronized锁区别 5
  • docker启动容器及启动一个挂载数据卷的容器

    在docker运行容器前可以先使用docker images列出本地镜像 docker运行容器前需要本地存在对应的镜像 如果本地不存在该镜像 Docker会从镜像仓库下载此镜像 先创建 usr share nginx html 目录 创建数
  • python: more Layer Architecture and its Implementation in Python

    sql server 学生表 DROP TABLE DuStudentList GO create table DuStudentList StudentId INT IDENTITY 1 1 PRIMARY KEY StudentName
  • python操作图像处理

    绘制图形方式一 1 图像读取 img cv imread C Users asus Desktop QQ jpg source cv cvtColor img cv COLOR BGR2RGB 2 均值滤波 blur cv blur img
  • AcWing 99. 激光炸弹(二维前缀和)

    输入样例 2 1 0 0 1 1 1 1 输出样例 1 解析 二维前缀和 枚举每个正方形区间的最大值即可 本题只能开一个5000的二维数组 两个会MLE 代码 include
  • flask+APScheduler定时任务的使用

    APScheduler定时任务使用以及在flask中的调用 APScheduler简介 组成部分 调度器 安装 普通使用安装 结合flask使用安装 使用 添加job add job参数详解 interval 间隔时间 每隔一段时间执行 d
  • Linux下实现编写汇编程序

    本学期的微机原理课程上机使用的是MASM汇编器 上课时使用的是Windows上的DOS 而Linux中的汇编工具是nasm 具体的可以点击链接 http os 51cto com art 201101 243138 htm 这里写代码片 下
  • C++11智能指针的基本原理及使用

    介绍 智能指针是一个类 用来存储指向动态分配对象的指针 负责自动释放动态分配的对象 防止堆内存泄漏 动态分配的资源 交给一个类对象去管理 当类对象声明周期结束时 自动调用析构函数释放资源 分类 auto ptr 已弃用 使用unique p
  • 神经网络算法和遗传算法,数据挖掘神经网络算法

    神经网络算法与进化算是什么关系 应该没有太大的关系吧 我对遗传算法了解一点 遗传算法主要用来优化神经网络第一次运行时所用的连接权值 因为随机的连接权值往往不能对针对的问题有比较好的收敛效果 Matlab神经网络工具箱自动生成的初始权值其实已
  • Tracy 小笔记 Vue - 网络模块封装(axios)

    安装 axios 和 引入 安装 npm install axios save 引用 import axios from axios 网络请求可以测试的几个接口地址 http httpbin org get http 123 207 32
  • Linux中的JDK安装和配置

    Linux中的JDK安装和配置 1 查看是否已安装JDK yum list installed grep java 2 卸载系统Java环境 yum y remove java 1 8 0 openjdk 3 卸载tzdata java y
  • 那年的夏天很笛子

    原文 salance moon spaces live com 在某个阶段 我想应该是时候把至今为止影响自己走上美工 设计 程序之路的历程整理一下了 但是下笔的时候才发现 其实这几乎成了我童年的回忆录 因为程序暂且不算 美工 设计就是我人格
  • Connect函数阻塞

    1 采用select 在学习嵌入式Linux网络编程中 很多同学都发现了一个问题 那就是调用connect函数时 如果服务端关闭 客户 端调用connect 函数时 发现阻塞在那里 而且利用ctrl c信号去停止客户端程序时 需要等待一个较
  • git:一次回滚多个commit

    说明 独立分散的commit共同回滚 git revert n sha 1 git 单次commit对应的sha 1值 sha 1 sha 1 ps n代表不会生成新的commit 如果想直接生成commit请去掉 n 最近连续的coomi
  • hr谈薪资后说请示领导_如何巧妙回答面试中的薪资问题呢?

    好不容易挺过了群面 单面 没想到在HR面被薪资问题打个措手不及 你的期望薪资是多少 说低了总觉得委屈自己 说高了又怕offer不保 好不容易在前面几轮面试积攒的自信 在这个问题上就变成 emm差不多就行吧 然后面试一结束就开始无限后悔 对于
  • 【ML特征工程】第 1 章 :机器学习管道

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • STM32F407 单片机+DMA+环形缓冲区+GPS报文解析

    本文采用DMA 环形缓冲区对GPS报文进行解析 思路是通过DMA中断接收到GPS报文后 存放到环形缓冲区 然后在主程序中解析GPS报文 解析GPS报文的关键是 将环形缓冲区中的字节转换成字符串 然后在字符串中查找GPS报文头标识 例如 GP