STM32——定位模块ATGM336H,数据解析,提取经纬度
模塊介紹
ATGM336H定位模塊支持GPS系統,BDS(北斗)系統,GLONASS(俄羅斯)系統,伽利略衛星導航系統(歐盟)。這個模塊要拿到室外才能接收到信號,且初次初始化或者隔太久時間沒有啟用會導致獲取定位信息的時間很長。
可以使用中科微電子提供的集成軟件設置模塊,可以設置串口輸出的參數,波特率等等參數。
本文介紹用STM32串口接收定位模塊的數據,并將數據進行解析,解析后得到經緯度的原始數據,把經緯度原始數據轉換成精確的經緯度信息后存放到數組里,可打印或繼續串口傳輸到其他設備,后面會介紹傳輸到ESP32,并保存到SPIFFS中。
上圖是模塊通過串口向上位機輸出的數據幀樣例。
通過GNSS工具可以看到模塊能夠獲取的數據幀有如下格式。
其中RMC數據是最簡定位信息,得到這串數據后,可以將GNRMC開始的前六項數據提取,分別是UTC時間,數據有效標志位,緯度,緯度方向,經度,經度方向。此時的經度和緯度只是原始數據,需要再轉換為精確數據。
RMC的數據樣例為
$GNRMC,123211.000,A,2295.33602,N,11326.27041,E,3.21,217.19,100722,,,A*7A程序思路
數據轉換:
2322.74250 格式:ddmm.mmmmm
11326.27041 格式:ddmm.mmmmm
轉換成北緯:23 + 22.74250 / 60 = 23.37904
轉換成東經:113 + 26.27041 / 60 = 113.43784
比較不準。。串口輸出的數據就是這樣的,估計芯片哪里出了問題。。
讀取數據可以先用一個緩存數組接收所有的數據,先判斷是不是$GNRMC開頭的數據幀,如果是的話則逐個字符接收,因為數據幀是以換行符為結尾的,當讀取到’\n’則是數據幀的結尾。
定義幾個字符數組,用于分別存放UTC時間,經度和緯度的原始數組,經度和緯度的方向。
以逗號為分隔符切割字符串,這里我們需要用到strstr這個函數。
返回指向str1中第一次出現str2的位置的指針,當str1中沒有str2則返回空指針。我們可以定義一個子串指針,用這個函數在數據幀中找逗號,再用memcpy函數把數據分割后存在相應的數組。
首先定義一個結構體存放幾個數組,并宏定義數組的長度。
void ATGM_StructInit() {GNRMC_Info.isGetData = false;GNRMC_Info.isParseData = false;GNRMC_Info.isUsefull = false;memset(GNRMC_Info.GPS_Buffer, 0, GPS_Buffer_Length); memset(GNRMC_Info.UTCTime, 0, UTCTime_Length);memset(GNRMC_Info.latitude, 0, latitude_Length);memset(GNRMC_Info.N_S, 0, N_S_Length);memset(GNRMC_Info.longitude, 0, longitude_Length);memset(GNRMC_Info.E_W, 0, E_W_Length); }首先初始化結構體,先把結構體成員內容全部置零。
void ATGM336H_Init() {GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;USART_InitTypeDef USART_InitStructure;ATGM_StructInit();RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //USART1_TX PA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure);//USART1_RX PA.10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure); //配置中斷通道NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ;//優先級最低NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_InitStructure.USART_BaudRate = 9600;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(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);USART_Cmd(USART1, ENABLE); ClrBuf(); //初始化 }之后配置引腳,配置串口1中斷,配置串口。引腳TX我選擇PA9,RX選擇PA10。TX要選擇復用推挽輸出,RX選擇浮空輸入。之后開通NVIC中斷通道,配置串口波特率為9600,8位數據位,1位停止位,吳嬌艷,無硬件流控制,模式為收發模式,因為我既要接收定位數據,也要打印或發送數據。最后別忘記使能中斷和串口。
unsigned int DataIndex = 0; void USART1_IRQHandler() {unsigned char receive;if(USART_GetITStatus(USART1, USART_IT_RXNE) == 1) {receive = USART_ReceiveData(USART1); //讀取接收到的數據if(receive == '$')DataIndex = 0; USART_RX_BUF[DataIndex++] = receive;//GNRMC\GPRMCif(USART_RX_BUF[0] == '$' && USART_RX_BUF[4] == 'M' && USART_RX_BUF[5] == 'C') {if(receive == '\n') {memset(GNRMC_Info.GPS_Buffer, 0, GPS_Buffer_Length); //清空memcpy(GNRMC_Info.GPS_Buffer, USART_RX_BUF, DataIndex); //保存數據GNRMC_Info.isGetData = true;DataIndex = 0;memset(USART_RX_BUF, 0, USART_REC_LEN); //清空 } } } }先是定義一個DataIndex作為數組的下標,當讀到了$標志表示數據幀開始,將下標置零,之后判斷是不是我們要讀取的這種格式,如果是則讀到’\n’,即把數據轉而存儲到GPS_Buffer中,并把數據讀取標志位置為true。
void ParseGps() {char *subString;char *subStringNext;char i = 0;if (GNRMC_Info.isGetData){GNRMC_Info.isGetData = false; // printf("\r\n"); // printf(GNRMC_Info.GPS_Buffer);//截取數據幀前六部分 |對地航速 對地航向 日期//$GNRMC,112536.000,A,2322.75023,N,11326.28605,E,| 0.00, 0.00, 100722,,,A*78for (i = 0 ; i <= 6 ; i++){if (i == 0){if ((subString = strstr(GNRMC_Info.GPS_Buffer, ",")) == NULL)//如果沒有找到逗號{return;//ERROR}}else{subString++;if ((subStringNext = strstr(subString, ",")) != NULL){char usefulBuffer[2]; switch(i){case 1:memcpy(GNRMC_Info.UTCTime, subString, subStringNext - subString);break; case 2:{memcpy(usefulBuffer, subString, subStringNext - subString);//有效標志位if(usefulBuffer[0] == 'A')GNRMC_Info.isUsefull = true;else if(usefulBuffer[0] == 'V')GNRMC_Info.isUsefull = false; break;} case 3:memcpy(GNRMC_Info.latitude, subString, subStringNext - subString);break; case 4:memcpy(GNRMC_Info.N_S, subString, subStringNext - subString);break; case 5:memcpy(GNRMC_Info.longitude, subString, subStringNext - subString);break; case 6:memcpy(GNRMC_Info.E_W, subString, subStringNext - subString);break; default:break;}subString = subStringNext; }}}GNRMC_Info.isParseData = true; } }這里進行六次循環,把數據幀分成六部分,用strstr函數找到從子串next指針開始的下一個逗號的位置,并把這個這個位置到子串指針之間的數據分別存到各自的數組中去。六次循環之后便結束,因為后面的數據是對地航速,對地航向和日期,我沒有這個需求,有需要的同學可以自己修改。
extern float Lat; extern float Lon; extern char dest[23]; void printGpsBuffer() {//$GNRMC,123211.000,A,2322.74250,N,11326.27041,E,3.21,217.19,100722,,,A*7Aif (GNRMC_Info.isParseData){int i = 0;GNRMC_Info.isParseData = false; if(GNRMC_Info.isUsefull){float tmp = 0; int j = 0; GNRMC_Info.isUsefull = false;for (i = 0; GNRMC_Info.latitude[i] != '\0'; i++){if (GNRMC_Info.latitude[i] == '.'){continue;}if (i <= 1){Lat = (GNRMC_Info.latitude[0] - 48) * 10 + (GNRMC_Info.latitude[1] - 48);//取出個位和十位}else{tmp += (GNRMC_Info.latitude[i] - 48);tmp *= 10;}}for (j = 0; j <= 5; j++){tmp /= 10;}Lat += tmp / 60;//23 22.74250//23.xxxxxint iLat = 0; iLat = (int)Lat;GNRMC_Info.latitude[0] = iLat / 10 + '0';GNRMC_Info.latitude[1] = iLat % 10 + '0';GNRMC_Info.latitude[2] = '.';Lat -= iLat;for (j = 3; j < 10; j++){Lat *= 10;iLat = (int)Lat;GNRMC_Info.latitude[j] = iLat + '0';Lat -= iLat;} tmp = 0;//113.27041for (i = 0; GNRMC_Info.longitude[i] != '\0'; i++){if (GNRMC_Info.longitude[i] == '.'){continue;}if (i <= 2){Lon = (((GNRMC_Info.longitude[0] - 48) * 10 + (GNRMC_Info.longitude[1] - 48)) * 10) + (GNRMC_Info.longitude[2] - 48);//取出個位和十位和百位}else{tmp += (GNRMC_Info.longitude[i] - 48);tmp *= 10;}}for (j = 0; j <= 5; j++){tmp /= 10;}int iLon = 0;//113.43784Lon += tmp / 60;iLon = (int)Lon;GNRMC_Info.longitude[0] = iLon / 100 + '0';GNRMC_Info.longitude[1] = (iLon % 100) / 10 + '0';GNRMC_Info.longitude[2] = iLon % 10 + '0';GNRMC_Info.longitude[3] = '.';Lon -= iLon;for (j = 4; j < 11; j++){Lon *= 10;iLon = (int)Lon;GNRMC_Info.longitude[j] = iLon + '0';Lon -= iLon;}dest[8] = dest[10] = dest[20] = ',';dest[9] = 'N'; dest[21] = 'E'; dest[22] = '\0';for(i = 0; i < 22; i++){if(i <= 7)dest[i] = GNRMC_Info.latitude[i];if(i >= 11 && i <= 19)dest[i] = GNRMC_Info.longitude[i - 11];}//printf("\r\ndest = ");printf(dest);//printf("\r\n");}else{printf("GPS DATA Is Not Useful!");} } }最后就是數據的轉換,要把經緯度數組中的字符提取出來組合成數字。對于緯度,先提取數組前兩位的數據作為個位和十位,從第三位開始后面的數據提取出來組成一個浮點數,之后把這個浮點數除以60,最后加上個位和十位的整數,便得到了轉換后的緯度。經度同理,區別在于經度有百位。
這里只是簡單的沒有啥依據的不合理的提取。
提取得到兩個浮點數后,把經緯度方向一起存儲在dest數組中,組合成完整的包括經緯度方向的數據。
結語
數據解析的步驟很繁瑣,且模塊輸出的原始數據不是很準確。后面再試試。
總結
以上是生活随笔為你收集整理的STM32——定位模块ATGM336H,数据解析,提取经纬度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现代汉语句子成分分析
- 下一篇: 海边旅行必备物品清单