Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: USART и NMEA
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
MarYuriy
Здравствуйте!

Надо ли преобразовывать как-то ASCII коды, которые получаю по USART, (типичная последовательность NMEA $GPZDA,102433.00,28,12,2011,,*6A, из которой мне нужны только 2-5 поля)?
(я так понимаю, что не надо)

Не подскажите более удачный алгоритм считывания символов с USARTa, кроме как перечисление if'ов?
andrewlekar
Я 3 раза перечитал, но так и не понял сути вопроса.
Цитата
Надо ли преобразовывать как-то ASCII коды, которые получаю по USART

Ну если начальство сказало, что надо, то преобразовывайте.
Цитата
Не подскажите более удачный алгоритм считывания символов с USARTa, кроме как перечисление if'ов?

Более удачный алгоритм - считывать с юсарта без ифов. Я вот считываю и перечисления ифов нет. Делаю я это так: считываю байт, если приёмник не пуст, повторяю в цикле.
SSerge
Повышаем градус абстракции!
От GPS-модуля ведь приходят текстовые строки, не правда ли? Значит и принимаем их с UART посимвольно, собирая из символов строки.

Когда строка полностью принята переходим к разбору той строки.
Отдельные поля в строке, как видим отделяются друг от друга запятой. Поле может быть пустым либо содержать число (целое или float), либо некое имя.
По науке этот этап называется лексическим анализом, а сами поля - лексемы. Множество терминальных символов (разделители лексем) состоит из запятой и конца строки.

Следующий этап - синтаксический анализ. В этом случае он тривиален, из входного потока лексем просто лишние выбрасываются, нужные сохраняются.
MarYuriy
Цитата(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 строчек кода.
MKdemiurg
Ты ЩО дурный (С) Без обид sm.gif

У тебя сообщение начинается с $ , заканчивается * +2 символа контрольной суммы.

Вот и начинай запись в буфер с $ , а когда * прилетает - обрабатывай сообщение из буфера. Сколько * прилетело столько и сообщений обработать надо, т.е. счётчик сообщений ещё надо инкрементить в прерывании.

Вот и всё - 2 if, флаговая переменная и счётчик сообщений в прерывании - обработчик в цикле основной программы завязан на if (флаговая переменная){парсер}
Kuzmi4
2 MarYuriy
Там кстати не всё железо даёт sentence с дробными секундами: 33.00
есть железяки что дают с только один знак после запятой, есть - что вообще без дробных.

Так же есть железяки что выдают формат так:
$GPZDA,10,24,33.00,28,12,2011,....
(то есть запятые после часов и минут)

Так что советую вам сделать более полный разбор полётов biggrin.gif (ну и как то учесть CRC, потоум как всякое может быть на линии, хотя если прибор не военный, то .... wink.gif )
_Артём_
Цитата(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? Некоторые поля могут иметь фиксированный фориат (и при этом иногда отсутствовать в строке - быть пустуми).
MarYuriy
Цитата(_Артём_ @ 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

// и т.д.

_Артём_
Цитата(MarYuriy @ Dec 29 2011, 15:00) *
Я ошибся, просто быстро накидал часть кода, как вижу решение. Должно быть

while (!CharNum==32)


Понятно
Цитата(MarYuriy @ Dec 29 2011, 15:00) *
32 символа передаётся в одном пакете $GPZDA.


Далеко не факт: зависит от реализации протокола производителем приёмника - некоторые поля могут быть пустыми, если нет этих данных.
Например, попадались строки RMC с пустыми полями времени/даты и тп(наверное потому что они не были еще определены на тот момент).

andrewlekar
Цитата
while (!CharNum==32)

Во-первых, такой код вообще работать не будет. У ! приоритет больше, чем у сравнения, поэтому цикл не выполнится ни разу.
Цитата
А сам вопрос звучит так: как вычленить только определённые символы из этой последовательности: время без десятых и сотых, и дату?

Самое простое решение для вас: читать все символы с юарта в буфер. Когда обнаружится символ *, чтение прекратить, а буфер отправить на обработку. Буфер можно обрабатывать как строку - тут надо смотреть, есть ли у вас возможность пользоваться стандартной библиотекой си для работы со строками. Если можно, то есть функция поиска подстроки в строке: ищете подстроку $GPZDA в буфере - так вы определите начало строки с NMEA. Потом от начала NMEA ищете подстроку - запятую (или просто проверяете посимвольно в цикле). Таким образом отсчитываете нужное количество запятых. Так вы попадете на поле с нужными данными. Их читаете вплоть до следующей запятой (или любого нечитаемого символа) и уже дальше думаете, как конвертировать полученные символы в число.
MarYuriy
_Артём_, я использую Trimble Resolusion-T. Если данных нет, то он посылает поля заполненные нулями, т.е. вмсето каждого символа нуль - по документации.

andrewlekar, спасибо, буду разбираться!
lexanet
Посмотрите LPC1768. Установка времени. (WEB&GPS&Manual). . Я там как раз баловался GPS, usart и прочим. Может что будет полезным.
MarYuriy
Как организовать буфер я нашёл. А вот как работать со строкой я так и не понял, тем более что библиотека 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 ";"
...

Что делать с принятой строкой, чтобы извлечь из неё нужную мне информацию?

Cosmojam
Цитата(MarYuriy @ Jan 10 2012, 20:43) *
Как организовать буфер я нашёл. А вот как работать со строкой я так и не понял, тем более что библиотека iostream не подключается, а выдаёт ошибки при компиляции, поэтому невозможно использовать фунцию типа strcpy(s1,s2)..

Причём тут iostream если речь идёт о Си, не Си++ ? Подключайте string.h там strcpy объявлена
Цитата
функция поиска подстроки в строке: ищете подстроку $GPZDA в буфере

Чудовищно тяжёлая операция для МК. На длинных строках умрёт.
WHALE
Цитата(Cosmojam @ Jan 10 2012, 22:22) *
Чудовищно тяжёлая операция для МК. На длинных строках умрёт.


Та ладна, чё там тяжелого.Гляньте исходник в вашем компиляторе.
lexanet
Не такая и тяжелая эта операция. У меня счас буфер 384 байта. Раз в секунду ищется подстрока "$GPRMC", выкопирываются оттуда секунды, минуты, часы, дни, месяцы, годы. Все это передается RTC и выводиться на textlcd. Еще остается времени для другой работы.
Cosmojam
Цитата(WHALE @ Jan 11 2012, 07:24) *
Та ладна, чё там тяжелого.Гляньте исходник в вашем компиляторе.

Там тупо перебор от забора и до обеда (gcc). Пробовал как-то так делать и это оказалось жутко медленно. Например, принято 200 байт и из них нужно найти 5 - это делалось в цикле, а приём в прерывании, так кроме поиска подстроки проц на 100 МГц больше ничего не мог делать.
_Артём_
Цитата(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. Занимало производительности чуть более чем ничего.

Cosmojam
Это что-то вроде дерева получается, должно быть очень эффективно, спасибо за идею
А ещё можно так: в прерывании в буфер записывается символ и ++указатель. В цикле проверяется изменился ли указатель с прошлого раза. Если да - был приём, берём разницу (сколько байт принято) и делаем memcmp с нужными подстроками от буфер[указатель - длина искомой строки - i ], где i счётчик в цикле от 1 до кол-ва принятых байт.
Цитата
Использовал ATMega128 на 11,0592МГц, приём в прерывании 10 кГц и разбор строки в прерывании 1 кГц, скорость порта приёмника 9600. Занимало производительности чуть более чем ничего.

Ну значит я где-то накосячил. Ладно, пускай будет strstr sm.gif
_Артём_
Цитата(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;
}
/// и т.д.
}


И лучше сделать шаблонный класс.

kan35
В свое время столкнулся с задачей разбора RMC и GGA строк на 51 контроллере, ни о каком сохранении строк там речи быть не может - памяти там минимум, так что парсер работает из прерывания и сразу разбирает строку. В прерывании не сидит долго.
Данные собираются из RMC и GGA строки, контрольная сумма проверяется, но отправляет готовые данные только после очередной RMC строки.
Парсер проверен с несколькими видами gps и глонасс приемников, на разных контроллерах от 51 до ARM, работает стабильно, надеюсь кому нибудь будет полезен:
gps_parser
Alexey K
Решил задать два вопроса здесь чтобы не плодить тем.

Насколько я понял из стандарта нмеа поле значения это ascii цифры разделенные запятыми и в общем случае переменной длинны.
Допускается ли делать поля фиксированной длинны - дополняя число нулям.

И такой вопрос в стандарте говорится что если для поля нет данных то остаются только запятые, в тоже время в описании GPS сентенций
встретилась фраза о том что в случае отсутствия данных поле будет заполнено знаками подчеркивания.
V_G
Цитата(Alexey K @ Jun 13 2012, 16:30) *
Допускается ли делать поля фиксированной длинны - дополняя число нулям.

Это как это - вы анализируете протокол НМЕА, или его формируете? Во втором случае дополняйте на здоровье. А при анализе ешьте то, что дают.
Цитата
И такой вопрос в стандарте говорится что если для поля нет данных то остаются только запятые, в тоже время в описании GPS сентенций
встретилась фраза о том что в случае отсутствия данных поле будет заполнено знаками подчеркивания.

Все известные мне GPS-приемники и компасы (работающие в нмеа) знаков подчеркивания не формировали, при пустом поле идут запятые подряд
Alexey K
Данные я формирую так что по первому пункту первому хорошо.
А по второму переделаю. Спасибо
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.