ESP32 GPS解析:如何高效将uint8_t数组转换为单个float值?
问题
我是嵌入式开发新手,当前使用ESP32-S3-WROOM-1从NEO-6M GPS模块获取UART数据,开发环境为Visual Studio Code。目前通过逐字节循环的方式将uint8_t数组转换为float值(如纬度、经度),具体代码如下:
char* Post_Time_String = strstr(String_Input, ","); F_Extract_Until_Comma(Post_Time_String, Lat_Val); for(int itr=0; itr<sizeof(Lat_Val),itr++) { if(Lat_Val[itr] != ".") { gps_info->GPS_POSITION->latitude = (10 * gps_info->GPS_POSITION->latitude) + Lat_Val[itr]; } }
但这种方式效率较低,希望找到无需循环的更优转换方案,比如是否可使用strtof函数实现?附上当前编写的NMEA解析相关完整C代码及头文件供参考:
NMEA_PARSE_CODE.c
#include <stdio.h> #include <NMEA_PARSE_CODE.h> void F_FIND_COMMA(char *String_Input, char* String_Output) { char *String_Output = strstr(String_Input, ","); return String_Output; } void F_Extract_Until_Comma(char *String_Input, uint8_t* Extracted_Output[]) { uint8_t String_Length = strlen(String_Input); uint8_t* F_Buffer[20] = {NULL}; char* comma_parse_string = F_FIND_COMMA(String_Input); //Get string after comma //Find comma and extract data until next comma uint8_t comma_parse_string_length = strlen(comma_parse_string); //Get length of string after comma for (uint8_t itr = 0; itr < comma_parse_string_length, itr++) { if(comma_parse_string[itr] == ",") { break; } F_Buffer[itr] = comma_parse_string[itr]; Extracted_Output[itr] = comma_parse_string[itr]; } } void F_U8_to_F(uint8_t Input_val, uint8_t pos, float* Output) { float OutputHold; OutputHold = 10*Input_val[pos]; OutputHold = OutputHold + Input_val[pos+1]; Output = OutputHold; } void F_NMEA_TIME_CONVERT(uint8_t Input_Val, NMEA_GPS gps_info) { F_U8_to_F(timeval, 0, gps_info->GPS_TIME->nmea_hours); F_U8_to_F(timeval, 2, gps_info->GPS_TIME->nmea_minutes); F_U8_to_F(timeval, 4, gps_info->GPS_TIME->nmea_seconds); } void F_GGA_Parse(uint8_t* String_Input, NMEA_GPS gps_info) { //TIME, Lat, N/S, Long, E/W, Fix, Sat#, HDOP, Alt, unit, Geoid, unit, ......... strof(); uint8_t time_value[12], Lat_Val[12]; //obtain from String_Input and to convert into float value F_Extract_Until_Comma(String_Input, time_value); F_NMEA_TIME_CONVERT(time_value, gps_info) //time end char* Post_Time_String = strstr(String_Input, ","); F_Extract_Until_Comma(Post_Time_String, Lat_Val); for(int itr=0; itr<sizeof(Lat_Val),itr++) { if(Lat_Val[itr] != ".") { gps_info->GPS_POSITION->latitude = (10 * gps_info->GPS_POSITION->latitude) + Lat_Val[itr]; } } F_U8_to_F(Lat_Val,0,gps_info); } char F_Extract_GPS_Data(char * raw_string_input, NMEA_GPS* gps_info) { char* GPS_ID_FOUND = strstr(raw_string_input, "$GP"); uint8_t Comma_Rounds = 0; if(NULL == GPS_ID_FOUND) { return NULL; } else { if(strstr(GPS_ID_FOUND, "GGA")) { gps_info->GPS_TYPE = GGA; gps_info->GGA_Flag = YES; Comma_Rounds = 14; } else if(strstr(GPS_ID_FOUND,"RMC")) { gps_info->GPS_TYPE = RMC; gps_info->RMC_Flag = YES; Comma_Rounds = 11; } else if(strstr(GPS_ID_FOUND,"VTG")) { gps_info->GPS_TYPE = VTG; gps_info->VTG_Flag = YES; Comma_Rounds = 9; } if(gps_info->GPS_TYPE == 1) { F_GGA_Parse(GPS_ID_FOUND, gps_info); } // if(GPS_ID_FOUND[0]) } }
NMEA_PARSE_CODE.h
#include <stdio.h> typedef enum { NO=0, YES=1 } NMEA_ID_GGA; typedef enum { NO=0, YES=1 } NMEA_ID_RMC; typedef enum { NO=0, YES=1 } NMEA_ID_VTG; typedef enum { GGA=1, RMC, VTG } NMEA_ID; typedef struct { float nmea_hours; float nmea_minutes; float nmea_seconds; float nmea_thousands; } NMEA_UTC_TIME; typedef struct { uint8_t nmea_year; uint8_t nmea_month; uint8_t nmea_day; } NMEA_DATE; typedef struct { float longitude; float latitude; } NMEA_POSITION; typedef struct { char NORTHSOUTH; char EASTWEST; float course; //In degrees float knot_speed; //In knots float meter_speed; //In meters per second } NMEA_VELOCITY; typedef struct { NMEA_ID GPS_TYPE; NMEA_ID_GGA GGA_Flag; NMEA_ID_RMC RMC_Flag; NMEA_ID_VTG VTG_Flag; NMEA_UTC_TIME GPS_TIME; NMEA_DATE GPS_DATE; NMEA_POSITION GPS_POSITION; NMEA_VELOCITY GPS_VELOCITY; } NMEA_GPS; typedef struct { NMEA_ID ID; NMEA_UTC_TIME TIME; } NMEA_GGA;
当前代码虽可编译,但希望优化uint8_t数组转float的效率,避免逐字节循环操作。
优化方案
1. 使用strtof实现无循环转换
strtof是标准C库提供的字符串转float函数,底层经过编译器高度优化(部分平台会用硬件指令加速),效率远高于手动逐字节循环,完全满足你的需求。但需要先修正当前代码中的几个基础错误:
- 单个字符比较必须用单引号(
'.'),双引号(".")表示字符串指针,会导致逻辑错误。 - 提取的字符串必须以
\0结尾,否则strtof无法识别为合法C字符串。 - 修正函数参数的类型不匹配问题(比如
F_U8_to_F中输入应为数组而非单个uint8_t)。
2. 关键函数修改示例
(1)修复F_Extract_Until_Comma,生成合法C字符串
// 修改参数为char数组,直接提取ASCII字符串,无需转uint8_t void F_Extract_Until_Comma(char *String_Input, char* Extracted_Output, size_t max_len) { char* comma_start = strstr(String_Input, ","); if (!comma_start) { Extracted_Output[0] = '\0'; return; } comma_start++; // 跳过当前逗号,指向数据起始位置 char* next_comma = strstr(comma_start, ","); if (!next_comma) { // 无下一个逗号,取到字符串末尾 strncpy(Extracted_Output, comma_start, max_len - 1); } else { // 截取到下一个逗号前的内容 size_t data_len = next_comma - comma_start; if (data_len >= max_len) data_len = max_len - 1; strncpy(Extracted_Output, comma_start, data_len); } Extracted_Output[max_len - 1] = '\0'; // 强制添加字符串终止符 }
(2)替换循环转换逻辑为strtof
在F_GGA_Parse中直接用strtof转换纬度:
// 修正参数为指针,避免结构体拷贝 void F_GGA_Parse(char* String_Input, NMEA_GPS* gps_info) { char time_value[12] = {0}, Lat_Val[12] = {0}; F_Extract_Until_Comma(String_Input, time_value, sizeof(time_value)); // 提取并转换纬度 char* Post_Time_String = strstr(String_Input, ","); F_Extract_Until_Comma(Post_Time_String, Lat_Val, sizeof(Lat_Val)); // 一行代码完成转换,无需循环 gps_info->GPS_POSITION.latitude = strtof(Lat_Val, NULL); // 时间转换也可采用类似逻辑,提取字符串后拆分转换 // ... 其他解析逻辑 }
3. 其他代码修正建议
- 修正
F_FIND_COMMA的返回值错误:char* F_FIND_COMMA(char *String_Input) { return strstr(String_Input, ","); } - 优化时间转换函数,直接处理字符串:
void F_NMEA_TIME_CONVERT(char* time_str, NMEA_UTC_TIME* time_info) { // NMEA时间格式如"123456.789",拆分时、分、秒 char hour_str[3] = {time_str[0], time_str[1], '\0'}; char min_str[3] = {time_str[2], time_str[3], '\0'}; char sec_str[6] = {time_str[4], time_str[5], time_str[6], time_str[7], time_str[8], '\0'}; time_info->nmea_hours = strtof(hour_str, NULL); time_info->nmea_minutes = strtof(min_str, NULL); time_info->nmea_seconds = strtof(sec_str, NULL); }
4. 效率说明
strtof作为标准库函数,其实现经过严格的性能优化,在嵌入式平台上的执行效率远高于手动编写的循环转换逻辑,同时代码的可读性和可维护性也会大幅提升,完全适配ESP32的开发场景。
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

