|
USART и NMEA |
|
|
|
Dec 28 2011, 13:54
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Здравствуйте!
Надо ли преобразовывать как-то ASCII коды, которые получаю по USART, (типичная последовательность NMEA $GPZDA,102433.00,28,12,2011,,*6A, из которой мне нужны только 2-5 поля)? (я так понимаю, что не надо)
Не подскажите более удачный алгоритм считывания символов с USARTa, кроме как перечисление if'ов?
|
|
|
|
|
Dec 29 2011, 05:02
|
Знающий
   
Группа: Участник
Сообщений: 837
Регистрация: 8-02-07
Пользователь №: 25 163

|
Я 3 раза перечитал, но так и не понял сути вопроса. Цитата Надо ли преобразовывать как-то ASCII коды, которые получаю по USART Ну если начальство сказало, что надо, то преобразовывайте. Цитата Не подскажите более удачный алгоритм считывания символов с USARTa, кроме как перечисление if'ов? Более удачный алгоритм - считывать с юсарта без ифов. Я вот считываю и перечисления ифов нет. Делаю я это так: считываю байт, если приёмник не пуст, повторяю в цикле.
|
|
|
|
|
Dec 29 2011, 11:39
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(andrewlekar @ Dec 29 2011, 08:02)  Я 3 раза перечитал, но так и не понял сути вопроса. Потому я такие вопросы и задаю, что новичок как в С++, так и в программировании контроллеров. Занимаюсь ими месяцев 8-9, периодически отрываясь от основной работы. Цитата Ну если начальство сказало, что надо, то преобразовывайте. Начальство тут не при чём, тут дело в непонимании. Мой алгоритм примерно выглядит так: CODE while (!Character==32) { while ( ! (USART1->SR & USART_SR_RXNE) ) ; // ждать, пока символ не получен { CharNum++; if (CharNum==1) { USART1->SR &=~ USART_SR_RXNE;} // пропустить 1-ый символ if (CharNum==3) { Character = (USART1->DR & 0x1FF ) ;} // считать 2-ой символ ..... Получается, что я каждый символ должен посчитать, а их 32, через оператор if. Цитата Следующий этап - синтаксический анализ. В этом случае он тривиален, из входного потока лексем просто лишние выбрасываются, нужные сохраняются. Цитата Делаю я это так: считываю байт, если приёмник не пуст, повторяю в цикле. Вот я и спрашиваю, как это сделать (на уровне кода)? можно ли это упростить с помощью языка? А не использовать для этого 60 строчек кода.
|
|
|
|
|
Dec 29 2011, 11:56
|
Знающий
   
Группа: Свой
Сообщений: 624
Регистрация: 15-06-10
Из: Россия
Пользователь №: 57 939

|
Ты ЩО дурный (С) Без обид У тебя сообщение начинается с $ , заканчивается * +2 символа контрольной суммы. Вот и начинай запись в буфер с $ , а когда * прилетает - обрабатывай сообщение из буфера. Сколько * прилетело столько и сообщений обработать надо, т.е. счётчик сообщений ещё надо инкрементить в прерывании. Вот и всё - 2 if, флаговая переменная и счётчик сообщений в прерывании - обработчик в цикле основной программы завязан на if (флаговая переменная){парсер}
Сообщение отредактировал MKdemiurg - Dec 29 2011, 11:58
|
|
|
|
|
Dec 29 2011, 12:08
|

Гуру
     
Группа: Свой
Сообщений: 3 304
Регистрация: 13-02-07
Из: 55°55′5″ 37°52′16″
Пользователь №: 25 329

|
2 MarYuriy Там кстати не всё железо даёт sentence с дробными секундами: 33.00 есть железяки что дают с только один знак после запятой, есть - что вообще без дробных. Так же есть железяки что выдают формат так: $GPZDA,10,24,33.00,28,12,2011,.... (то есть запятые после часов и минут) Так что советую вам сделать более полный разбор полётов  (ну и как то учесть CRC, потоум как всякое может быть на линии, хотя если прибор не военный, то ....  )
|
|
|
|
|
Dec 29 2011, 12:14
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(MarYuriy @ Dec 29 2011, 13:39)  Потому я такие вопросы и задаю, что новичок как в С++, так и в программировании контроллеров. Занимаюсь ими месяцев 8-9, периодически отрываясь от основной работы. Начальство тут не при чём, тут дело в непонимании. Мой алгоритм примерно выглядит так: CODE while (!Character==32) { while ( ! (USART1->SR & USART_SR_RXNE) ) ; // ждать, пока символ не получен { CharNum++; if (CharNum==1) { USART1->SR &=~ USART_SR_RXNE;} // пропустить 1-ый символ if (CharNum==3) { Character = (USART1->DR & 0x1FF ) ;} // считать 2-ой символ ..... Получается, что я каждый символ должен посчитать, а их 32, через оператор if. Вот я и спрашиваю, как это сделать (на уровне кода)? можно ли это упростить с помощью языка? А не использовать для этого 60 строчек кода. Непонятен алгоритм, который вы пытаетесь реализовать. Код while (!Character==32) Не буду утверждат, что символ 32 вообще не встречается в NMEA. мало ли. Но то что не характерен - точно. Типичная NMEA посылка, например: $GPRMC,данные в формате, какие должны быть по стандарту.*КС\r\n КС- контрольная сумма, вычисляемая так; xor всех байт от $ до * (не включая их). \r\n - перевод строки. Цитата Получается, что я каждый символ должен посчитать, а их 32, через оператор if. Почему 32? Некоторые поля могут иметь фиксированный фориат (и при этом иногда отсутствовать в строке - быть пустуми).
|
|
|
|
|
Dec 29 2011, 13:00
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(_Артём_ @ Dec 29 2011, 15:14)  Непонятен алгоритм, который вы пытаетесь реализовать. while (!Character==32)[/code] Почему 32? Некоторые поля могут иметь фиксированный фориат (и при этом иногда отсутствовать в строке - быть пустуми). Я ошибся, просто быстро накидал часть кода, как вижу решение. Должно быть while (!CharNum==32) 32 символа передаётся в одном пакете $GPZDA. Цитата Ты ЩО дурный (С) Без обид Я думал, что вчера написал, что мне нужно вычленить только определённые символы из этой последовательности: время без десятых и сотых, и дату. А сейчас не мог понять, зачем мне пишут то, что и так понятно ещё и ругают... Спать надо больше. Извиняюсь. А сам вопрос звучит так: как вычленить только определённые символы из этой последовательности: время без десятых и сотых, и дату? вот. т.е. в моём варианте примерно так(если я опять нигде не ошибся) CODE while (!CharNum==32) { while ( ! (USART1->SR & USART_SR_RXNE) ) ; // ждать, пока символ не получен { CharNum++; if (CharNum==1) { USART1->SR &=~ USART_SR_RXNE;} // пропустить 1-ый символ , // то же для 2,3-7
if (CharNum==8) { Character = (USART1->DR & 0x1FF ) ;} // считать 2-ой символ // то же для 9,10-13 // и т.д.
|
|
|
|
|
Dec 29 2011, 16:15
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(MarYuriy @ Dec 29 2011, 15:00)  Я ошибся, просто быстро накидал часть кода, как вижу решение. Должно быть
while (!CharNum==32) Понятно Цитата(MarYuriy @ Dec 29 2011, 15:00)  32 символа передаётся в одном пакете $GPZDA. Далеко не факт: зависит от реализации протокола производителем приёмника - некоторые поля могут быть пустыми, если нет этих данных. Например, попадались строки RMC с пустыми полями времени/даты и тп(наверное потому что они не были еще определены на тот момент).
|
|
|
|
|
Dec 30 2011, 04:46
|
Знающий
   
Группа: Участник
Сообщений: 837
Регистрация: 8-02-07
Пользователь №: 25 163

|
Цитата while (!CharNum==32) Во-первых, такой код вообще работать не будет. У ! приоритет больше, чем у сравнения, поэтому цикл не выполнится ни разу. Цитата А сам вопрос звучит так: как вычленить только определённые символы из этой последовательности: время без десятых и сотых, и дату? Самое простое решение для вас: читать все символы с юарта в буфер. Когда обнаружится символ *, чтение прекратить, а буфер отправить на обработку. Буфер можно обрабатывать как строку - тут надо смотреть, есть ли у вас возможность пользоваться стандартной библиотекой си для работы со строками. Если можно, то есть функция поиска подстроки в строке: ищете подстроку $GPZDA в буфере - так вы определите начало строки с NMEA. Потом от начала NMEA ищете подстроку - запятую (или просто проверяете посимвольно в цикле). Таким образом отсчитываете нужное количество запятых. Так вы попадете на поле с нужными данными. Их читаете вплоть до следующей запятой (или любого нечитаемого символа) и уже дальше думаете, как конвертировать полученные символы в число.
|
|
|
|
|
Dec 30 2011, 12:03
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
_Артём_, я использую Trimble Resolusion-T. Если данных нет, то он посылает поля заполненные нулями, т.е. вмсето каждого символа нуль - по документации.
andrewlekar, спасибо, буду разбираться!
|
|
|
|
|
Jan 6 2012, 17:11
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 6-04-11
Пользователь №: 64 159

|
Посмотрите LPC1768. Установка времени. (WEB&GPS&Manual). . Я там как раз баловался GPS, usart и прочим. Может что будет полезным.
|
|
|
|
|
Jan 10 2012, 17:43
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Как организовать буфер я нашёл. А вот как работать со строкой я так и не понял, тем более что библиотека iostream не подключается, а выдаёт ошибки при компиляции, поэтому невозможно использовать фунцию типа strcpy(s1,s2)..
compiling main.c... P:\Keil\ARM\RV31\INC\rw/_defs.h(781): error: #20: identifier "namespace" is undefined P:\Keil\ARM\RV31\INC\rw/_defs.h(781): error: #65: expected a ";" P:\Keil\ARM\RV31\INC\iosfwd(93): error: #20: identifier "namespace" is undefined P:\Keil\ARM\RV31\INC\iosfwd(93): error: #65: expected a ";" P:\Keil\ARM\RV31\INC\iosfwd(117): error: #65: expected a ";" P:\Keil\ARM\RV31\INC\iosfwd(122): error: #65: expected a ";" P:\Keil\ARM\RV31\INC\iosfwd(127): error: #65: expected a ";" ...
Что делать с принятой строкой, чтобы извлечь из неё нужную мне информацию?
|
|
|
|
|
Jan 10 2012, 18:22
|
Местный
  
Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182

|
Цитата(MarYuriy @ Jan 10 2012, 20:43)  Как организовать буфер я нашёл. А вот как работать со строкой я так и не понял, тем более что библиотека iostream не подключается, а выдаёт ошибки при компиляции, поэтому невозможно использовать фунцию типа strcpy(s1,s2).. Причём тут iostream если речь идёт о Си, не Си++ ? Подключайте string.h там strcpy объявлена Цитата функция поиска подстроки в строке: ищете подстроку $GPZDA в буфере Чудовищно тяжёлая операция для МК. На длинных строках умрёт.
--------------------
typedef enum { no, yes, maybe } bool; | блог тут
|
|
|
|
|
Jan 11 2012, 05:15
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 6-04-11
Пользователь №: 64 159

|
Не такая и тяжелая эта операция. У меня счас буфер 384 байта. Раз в секунду ищется подстрока "$GPRMC", выкопирываются оттуда секунды, минуты, часы, дни, месяцы, годы. Все это передается RTC и выводиться на textlcd. Еще остается времени для другой работы.
Сообщение отредактировал lexanet - Jan 11 2012, 05:15
|
|
|
|
|
Jan 11 2012, 09:05
|
Местный
  
Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182

|
Цитата(WHALE @ Jan 11 2012, 07:24)  Та ладна, чё там тяжелого.Гляньте исходник в вашем компиляторе. Там тупо перебор от забора и до обеда (gcc). Пробовал как-то так делать и это оказалось жутко медленно. Например, принято 200 байт и из них нужно найти 5 - это делалось в цикле, а приём в прерывании, так кроме поиска подстроки проц на 100 МГц больше ничего не мог делать.
--------------------
typedef enum { no, yes, maybe } bool; | блог тут
|
|
|
|
|
Jan 12 2012, 11:44
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Cosmojam @ Jan 11 2012, 11:05)  Там тупо перебор от забора и до обеда (gcc). Пробовал как-то так делать и это оказалось жутко медленно. Например, принято 200 байт и из них нужно найти 5 - это делалось в цикле, а приём в прерывании, так кроме поиска подстроки проц на 100 МГц больше ничего не мог делать. А если так: Код unsigned char RMCStrHead[]="GPRMC,";// длина 6 байт unsigned char RMCRxState, NMEASum;
// приём нового байта в прерываниии: if (NextByteReceived) { unsigned char new_byte=GetByteFromGPSUART();// чтение нового байта if (RMCRxState>0 && RMCRxState<NMEA_READ_SUM_STATE) NMEASum^=new_byte; switch (RMCRxState) { case 0: case 1: case 2: case 3: case 4: case 5: if (new_byte==RMCStrHead[RMCRxState]) RMCRxState++; else RMCRxState=0; if (RMCRxState==1) NMEASum=new_byte; break; case 6: case 7: case 8: case ...:// столько, сколько нужно //выделение нужных полей по запятым //при приёме символа CR или LF - сброс в RMCRxState 0 //при прёме * - след. 2 байта - сумма считывание суммы //если сумма совпала - первичное обновление данных //если сумма не совпала - начать приём сначала break; } } Цитата(Cosmojam @ Jan 11 2012, 11:05)  Например, принято 200 байт и из них нужно найти 5 - это делалось в цикле, а приём в прерывании, так кроме поиска подстроки проц на 100 МГц больше ничего не мог делать. Использовал ATMega128 на 11,0592МГц, приём в прерывании 10 кГц и разбор строки в прерывании 1 кГц, скорость порта приёмника 9600. Занимало производительности чуть более чем ничего.
|
|
|
|
|
Jan 13 2012, 13:03
|
Местный
  
Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182

|
Это что-то вроде дерева получается, должно быть очень эффективно, спасибо за идею А ещё можно так: в прерывании в буфер записывается символ и ++указатель. В цикле проверяется изменился ли указатель с прошлого раза. Если да - был приём, берём разницу (сколько байт принято) и делаем memcmp с нужными подстроками от буфер[указатель - длина искомой строки - i ], где i счётчик в цикле от 1 до кол-ва принятых байт. Цитата Использовал ATMega128 на 11,0592МГц, приём в прерывании 10 кГц и разбор строки в прерывании 1 кГц, скорость порта приёмника 9600. Занимало производительности чуть более чем ничего. Ну значит я где-то накосячил. Ладно, пускай будет strstr
--------------------
typedef enum { no, yes, maybe } bool; | блог тут
|
|
|
|
|
Jan 13 2012, 14:36
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Cosmojam @ Jan 13 2012, 15:03)  А ещё можно так: в прерывании в буфер записывается символ и ++указатель. В цикле проверяется изменился ли указатель с прошлого раза. Если да - был приём, берём разницу (сколько байт принято) и делаем memcmp с нужными подстроками от буфер[указатель - длина искомой строки - i ], где i счётчик в цикле от 1 до кол-ва принятых байт. Тогда лучше так: Код struct TRxBuffer { unsigned char Buf[BUF_SIZE]; unsigned char Next, First, ByteToRead; void WriteByte(unsigned char b) { Buf[Next]=b; ItemCount++; if (++Next==BUF_SIZE) Next=0; } /// и т.д. } И лучше сделать шаблонный класс.
|
|
|
|
|
Jan 14 2012, 07:17
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
В свое время столкнулся с задачей разбора RMC и GGA строк на 51 контроллере, ни о каком сохранении строк там речи быть не может - памяти там минимум, так что парсер работает из прерывания и сразу разбирает строку. В прерывании не сидит долго. Данные собираются из RMC и GGA строки, контрольная сумма проверяется, но отправляет готовые данные только после очередной RMC строки. Парсер проверен с несколькими видами gps и глонасс приемников, на разных контроллерах от 51 до ARM, работает стабильно, надеюсь кому нибудь будет полезен: gps_parser
|
|
|
|
|
Jun 13 2012, 06:30
|
Участник

Группа: Участник
Сообщений: 46
Регистрация: 14-07-11
Пользователь №: 66 220

|
Решил задать два вопроса здесь чтобы не плодить тем.
Насколько я понял из стандарта нмеа поле значения это ascii цифры разделенные запятыми и в общем случае переменной длинны. Допускается ли делать поля фиксированной длинны - дополняя число нулям.
И такой вопрос в стандарте говорится что если для поля нет данных то остаются только запятые, в тоже время в описании GPS сентенций встретилась фраза о том что в случае отсутствия данных поле будет заполнено знаками подчеркивания.
|
|
|
|
|
Jun 13 2012, 07:01
|

Профессионал
    
Группа: Свой
Сообщений: 1 818
Регистрация: 15-10-09
Из: Владивосток
Пользователь №: 52 955

|
Цитата(Alexey K @ Jun 13 2012, 16:30)  Допускается ли делать поля фиксированной длинны - дополняя число нулям. Это как это - вы анализируете протокол НМЕА, или его формируете? Во втором случае дополняйте на здоровье. А при анализе ешьте то, что дают. Цитата И такой вопрос в стандарте говорится что если для поля нет данных то остаются только запятые, в тоже время в описании GPS сентенций встретилась фраза о том что в случае отсутствия данных поле будет заполнено знаками подчеркивания. Все известные мне GPS-приемники и компасы (работающие в нмеа) знаков подчеркивания не формировали, при пустом поле идут запятые подряд
|
|
|
|
|
Jun 13 2012, 07:12
|
Участник

Группа: Участник
Сообщений: 46
Регистрация: 14-07-11
Пользователь №: 66 220

|
Данные я формирую так что по первому пункту первому хорошо. А по второму переделаю. Спасибо
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|