| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Микроконтроллеры (MCU) и микропроцессоры (MPU) > Приём строки по USART [STM32] Keil5 |
| Автор: supercelt 5.1.2017, 16:12 | ||||||
Привет!. Помогите пожалуйста. Для вас это наверное расплюнуть, а у меня непонятки. Пишу код в Keil под stm32. Есть модуль, от которого по usart приходит строка вот такого вида:
Цель: выделить всё что между кавычек. Я так поразмыслил это можно делать 2 способами. Первый: Это кусок кода в файле mod.c где и крутится всё что касается модуля.
Здесь стандартно по прерыванию приёма я сбрасываю флаг, устанавливаю флаг, что я готов записать принятый байт и быстро ухожу. В самом прерывании я не стал записывать в переменную содержание регистра DR ибо сами знаете делать что-то в прерывании не комильфо. значит флаг flag_receive установился и далее в цикле while файла main.c: if(flag_receive){ Get(); flag_receive = 0; } Далее вызываем Get и всё складируется в buffer. Далее с помощью регулярки выдернуть всё что между кавычек. Вариант 2
То есть вытягиваем нужную строку непосредственно в процессе приёма байтов посимвольно. В коде могут быть ошибки, так как хотел показать идею. А теперь главные вопросы Если сначала набивать массив строкой, а потом разбирать регуляркой, как написать эту самую регулярку, что бы между кавычек вытащить? И второй вопрос, что будет работать быстрее и эффективнее 1 или 2 вариант? |
| Автор: Курсант 10.1.2017, 22:36 |
| Регвыр на МК применять, ИМХО, слишком круто, хотя для кругозора можно повозиться. Не соглашусь с тем, что нужно заполнять буфер. Можно использовать конечный автомат для управления распознаванием символов. Состояний у конечного автомата будет 11: 1. Ожидание \r (начальное состояние). После прихода \r переходим в состояние 2. 2. Ожидание \n (после прихода \n переходим в состояние 2, иначе в состояние 1) 3. Ожидание + (в случае прихода \r переходим в состояние 2, иначе в состояние 1) 4. Ожидание C (после C переходим в состояние 5, после \r - в состояние 2, иначе в состояние 1) 5. Ожидание M 6. Ожидание G 7. Ожидание R 8. Ожидание : 9. Ожидание <пробела> 10. Ожидание " 11. Ввод значения (можно проверять символы на принадлежность к цифрам и сразу преобразовывать в int). После прихода " возвращаем полученное значение функции-абоненту и переходим в состояние 1. Если приходит не цифра, и не ", сбрасываем накопленное значение и переходим в состояние 1.) |
| Автор: baldman88 10.1.2017, 23:23 |
| Все не так просто. В данном случае выгоднее сделать циклический буфер и уже парсить данные, полученные из него. |
| Автор: Курсант 10.1.2017, 23:35 | ||||
Много подводных камней. Во-первых, нужно своевременно опустошать буфер (или считывать его), хотя это не проблема. Проблема в том, что парсить данные нужно по мере готовности, т.е. после прихода закрывающей кавычки. Слишком много условностей. Конечный автомат, ИМХО, простое и надёжное решение, приспособленное к формату входных данных. Я не скажу, что это более простое решение. С точки зрения кода оно менее очевидное. Но оно быстрее и надёжнее. По сути, Вы делаете функцию парсинга, принимающую один единственный символ:
|
| Автор: baldman88 11.1.2017, 12:26 | ||
Я, наверное, параноик (видимо, работая с военными это нормальное состояние), но гарантии, что придет именно кавычка, нет. Тогда может возникнуть ситуация, что в качестве закрывающей будет воспринята открывающая из следующего сообщения (вероятность крайне низка, но такое может случиться!). Нужно предусмотреть сброс автомата в начальное состояние. Именно поэтому целесообразнее иметь буфер (и проверять его после прихода нового символа). И вот здесь использование конечного автомата очень даже оправдано (получили символ, прошлись по буферу -- если все в порядке, то выделили нужное значение, и при новом вызове автомат всегда находится в начальном состоянии). Пы.Сы.: ТС, скорее всего, неправильно поставил слеши -- они должны идти перед r и n. Да и первая строка, по идее, должна идти уже после пятой. |
| Автор: Курсант 11.1.2017, 14:41 | ||||
Как только в режиме чтения строки придёт любой символ из \r или \n, автомат сбросится и буфер обнулится. Ну, должен обнулиться, по крайней мере, возможно я забыл его обунлить, т.к. код иллюстративный, а не точный. Я, кстати, как раз хотел эту саму закрывающую кавычку Вам в качестве аргумента предложить. Т.е. Вам придётся парсить всю строку при каждом пришедшем символе. Более того, у нас один коллега реализовывал обмен по COM-порту как раз с помощью буфера и парсинга строк. Работает, вроде, но бывает что команды игнорирует, приходится повторно отправлять с компа. |
| Автор: supercelt 15.1.2017, 17:17 | ||
| В продолжении вопроса. Выделить из кавычек это я поторопился. Надо сначала ещё кучу всего сделать. Там условия такие, что строка может придти когда угодно, какая угодно. Я не знаю ее размер. Таким образом я решил что по мере поступления символов, а прерывании по usart1 я буду просто набивать буфер. А потом, когда он набьётся - разбирать строку. И вот проблема. Раз я не знаю размер строки, то когда она полностью придёт неизвестно. Проверять по символу \r\n не вариант, так как эти символы приходят вначале сообщения и в середине. Короче я думал думал и вот что придумал. Алгоритм такой: После того, как сработало первое прерывание по приёму usart1, я запускаю таймер. Настраиваю его на прерывание по переполнению. Далее приходит второй символ, таймер сбрасываю, он не успевает переполнится. И так далее. И вот пришёл последний символ. прерывание сработало последний раз и сбросило таймер. Он продолжает тикать. А раз символов то больше нет, он благополучно вваливается в прерывание по переполнению. В обработчике вектора вырубаю таймер и выставляю флаг что я принял всё-таки строку. Алгоритм простой, но как всегда всё работает почему-то не так как надо. Если из кода, который ниже убрать включение прерывание на таймер, то работает, но таймер соответственно не останавливается. А если включить прерывание, что так и должно быть - ничего не работает. Похоже даже на зависание. И самое интересное. Что бы я не делал, в режиме отладки, на кусок кода обработчика прерывания таймера нельзя поставить точку останова. Везде можно а именно на этот кусок нельзя.
|
| Автор: Курсант 15.1.2017, 23:28 |
| Вы не знаете чем набьётся буфер. ИМХО, если Вы не знаете максимальный размер того, что приходит между кавычек, Вы не можете гарантированно распознать строку - это раз. В буфер может набиться и две команды, и три, и две с половиной, что тогда? Две выполните, третью выкинете? А потом в следующем прерывании получите закрывающую кавычку от третьей команды и распознаете её как открывающую кавычку, и "съедите" кракозяблы. Говорите точно, что за строки. Если они как-то описываются, то можно говорить об алгоритме. Если данные на входе не поддаются никакому описанию, то и обработке они не поддаются... Разве только через самообучающуюся нейросеть |
| Автор: supercelt 16.1.2017, 21:13 | ||
Понимаете, разбор строки это как оказалось уже 2 шаг. Я застрял на первом. Неважно что пришло, и сколько размером набился буфер. Мне надо просто отследить когда в usart перестали поступать данные с момента начала. Прикрути таймер, но эта штука не особо то и работает. |
| Автор: baldman88 16.1.2017, 21:44 | ||||
Вам шашечки или ехать? Для начала попробуйте конкретизировать задачу. Что, конкретно, не получилось с таймером? И как он может помочь в изначальном вопросе? |
| Автор: supercelt 19.1.2017, 19:35 |
| Что с таймером. Вобщем в отладчике ставлю точки остановки на прерывании у таймера и юсарта. Запускаю. И самый первый раз останавливаемся на метке таймера. При том что таймер выключен. По сути получается якобы алгоритм отработал, выключил таймер TIM12->CR1 &= ~TIM_CR1_CEN; Но по задумке, повторю ещё раз: -Срабатывает первое прерывание по принятому байту юсарта -Набиваем буфер -Если это первое прерывание, то запускаем таймер, если не первое, то обнуляем таймер. -Далее принимаются байты, счётчик сбрасывается, таймер не успевает переполняться. -Затем данные кончились, таймер никто не обнулил, он продолжает считать и досчитывает до топа и переполняется. -Вваливаемся в вектор по переполнению. Выключаем таймер, ставим флаг что набили буфер. А в отладчике. Первое куда мы приходим это вектор таймера. А должно быть - юсарт. И ещё одна странность. По отладчику видно, что приём одного байта занимает примерно 80 тиков таймера. Для наглядности я ставил ARR = 0xFFFF. Завязывал на полную. НО если я поставлю например 20000 тиков, то кол-во тиков между прерываниями приёма юсарта будет уже не 80, а допустим 2000. По идее ARR же никак не должно влиять на количество тиков между прерываниями, ведь до конца все-равно не досчитываем. |
| Автор: baldman88 19.1.2017, 22:47 |
| Я бы не стал на 100% доверять эмулятору. Особенно если уже до этого были проблемы и неточности с его работой. Насколько я понял, то сообщения от устройства у Вас приходят не очень часто, и Вы хотите использовать таймер именно для определения того, что сообщение уже пришло? Не самый удачный алгоритм. Скорее -- совсем неудачный (если заранее известен формат сообщений от устройства). |
| Автор: supercelt 20.1.2017, 22:24 |
| А если неизвестен? то кроме таймера есть ещё шустрые варианты? |
| Автор: Курсант 21.1.2017, 13:50 |
| Ещё вариант - запустить счётчик реального времени. И при получении байта запоминать метку времени. Кроме того, периодически эту метку времени просматривать. Если интервал от этой метки времени до текущего момент времени превысил максимальную паузу между символами - отправляем накопленную строку на парсинг. |
| Автор: supercelt 22.1.2017, 00:39 | ||
Я почти так и сделал. При приёме первого байта, запускаю таймер. Следующий принятый байт сбрасывает таймер и он не успевает дойти до значения сравнения. После последнего принятого байта, таймер дотикивает до сравнения и валит в прерывание. Флаг готовности в руки, далее пилю набитый буфер как надо. Работает всё довольно таки шустро. |