英文:
STM32 elegant way to parse NMEA sentences without allocating to much memory
问题
I'm writing a parser for NMEA sentences.
They can look like this:
$GPBWC,220516,5130.02,N,00046.34,W,213.8,T,218.0,M,0004.6,N,EGLM11
$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E62
$GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43
This block of code repeats around each second. I would like to analyze line by line. I use the STM32 Hal UART command to read this. First I use HAL_UART_Receive_IT
to generate an interrupt when an incoming sentence begins with $
. Then I parse the first line. By reading char
by char
in string until the break line command \n
is reached. So in this example I parse the first command $GPBWC
. When I'm done I want to parse the next line $GPRMS
. However during the parsing of $GPBWC
UART doesn't stop and I'm missing the sentences $GPRMC
and $GPVTG
.
I can receive the whole block at a time but this requires a lot of allocated memory which is constantly blocked and makes my code unnecessarily heavy. Also, I need to extend the memory even more if I will have more types of sentences.
I want to get all sentences line by line without missing any of them. There has to be a more elegant way than receiving the whole block at a time.
Update
Thanks @0___________ for the hint.
I've implemented the following code:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
update(rx_byte[0]);
HAL_UART_Receive_IT(&hlpuart1, rx_byte, 1);
}
void update(char c) {
if (c == '$') {
sen_index = 0;
msgValid = 1;
}
if (msgValid == 1) {
sentence[sen_index] = c;
sen_index++;
if (c == '\n') {
msgValid = 0;
strncpy(prefix, sentence + 1, 5);
if (strcmp(prefix, "GPRMC") == 0) {
...
}
if (strcmp(prefix, "GPBWC") == 0) {
...
}
if (strcmp(prefix, "GPVTG") == 0) {
...
}
}
...
}
while (1) {
HAL_UART_Receive_IT(&hlpuart1, rx_byte, 1);
}
}
This is not the best code, but it works.
英文:
I'm writing a parser for NMEA sentences.
They can look like this:
$GPBWC,220516,5130.02,N,00046.34,W,213.8,T,218.0,M,0004.6,N,EGLM*11
$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
$GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43
This block of code repeats around each second. I would like to analyze line by line. I use the STM32 Hal UART command to read this. First I use HAL_UART_Receive_IT
to generate an interrupt when an incoming sentence begins with $
. Then I parse the first line. By reading char
by char
in string until the break line command \n
is reached. So in this example I parse the first command $GPBWC
. When I'm done I want to parse the next line $GPRMS
. However during the parsing of $GPBWC
UART doesn't stop and I'm missing the sentences $GPRMC
and $GPVTG
.
I can receive the whole block at a time but this requires a lot of allocated memory which is constantly blocked and makes my code unnecessary heavy. Also I need to extend the memory even more if I will have more types of sentences.
I want to get all sentences line by line without missing any of them. There has to be a more elegant way then receiving the whole block at a time.
Update
Thx @0___________ for the hint.
I'm implemented the following code:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
update(rx_byte[0]);
HAL_UART_Receive_IT(&hlpuart1, rx_byte, 1);
}
void update(char c) {
if (c == '$') {
sen_index = 0;
msgValid = 1;
}
if (msgValid == 1) {
sentense[sen_index] = c;
sen_index++;
if (c == '\n') {
msgValid = 0;
strncpy(prefix, sentense + 1, 5);
if (strcmp(prefix, "GPRMC") == 0) {
...
}
if (strcmp(prefix, "GPBWC") == 0) {
...
}
if (strcmp(prefix, "GPVTG") == 0) {
...
}
}
...
while (1) {
HAL_UART_Receive_IT(&hlpuart1, rx_byte, 1);
}
This is not the best code but it works.
答案1
得分: 2
- 在中断处理程序中不要做太多的工作!中断处理程序只读取字符并将其添加到缓冲区。保持中断短而快。
- 先进先出队列(FIFO)。中断处理程序从一侧将字符附加到FIFO,然后主循环从另一侧从FIFO中读取。请记住,中断和其他代码之间的通信需要使用volatile关键字和锁。
- 最大的NMEA行包含82个字符。有一个静态的83字节缓冲区用于存储单行NMEA消息以供解析使用。请注意,这不是缓冲区的大小,只是用于一个消息的内部NMEA解析缓冲区。
- 不要重新发明轮子 - NMEA很古老,有数百万库可用于解析它。Minmea是一个用于解析NMEA消息的良好库。
- 如果您希望进行更快的传输,可以考虑使用DMA,但这确实很难实现,因为您不能依赖于每个字符的中断。我认为只有在波特率大于115200(或者您需要非常低的功耗)时才考虑使用DMA。
英文:
- Do not do much work in interrupt handler! Interrupt handler only reads characters and adds to a buffer. Keep interrupts short & fast.
- FIFO. Interrupt handler appends characters to a FIFO from one side. Then the main loop reads from the fifo from the other side. Remember about volatile and locking needed for communication between interrupt and other code.
- Max NMEA line has 82 characters. Have a static 83 buffer to store a single line for parser to parse. Note this is not the size of the buffer, only internal NMEA parser buffer for one message.
- Do not reinvent the wheel - NMEA is old, there are millions libraries to parse it. Minmea is a fine library for parsing NMEA messages.
- You may want for faster transfer use DMA, however it is really hard to implement, because you can't depend on interrupt every character. I think only consider DMA if your baudrate is greater than 115200 (or you need really low power consumption).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论