Перейти к содержанию

Приём строки по USART [STM32] Keil5


supercelt

Рекомендуемые сообщения

Привет!. Помогите пожалуйста. Для вас это наверное расплюнуть, а у меня непонятки. Пишу код в Keil под stm32. Есть модуль, от которого по usart приходит строка вот такого вида:

r\n\
+CMGR: "993848",145
r\n\
r\n\
OK

Цель: выделить всё что между кавычек. Я так поразмыслил это можно делать 2 способами. 

Первый:
Это кусок кода в файле mod.c где и крутится всё что касается модуля.

char buffer[];
uint8_t i;
void USART1_IRQHandler(void){ //Прерывание USART1
        if(USART1->SR & USART_SR_RXNE){ //Если прерывание вызвано по приёму USART1
                USART1->SR &= ~USART_SR_RXNE; //Сбрасываем флаг приёма USART1
                flag_receive = 1;
        }
}
void Get(){
buffer[i++] = USART1->DR;
}

Здесь стандартно по прерыванию приёма я сбрасываю флаг, устанавливаю флаг, что я готов записать принятый байт и быстро ухожу. В самом прерывании я не стал записывать в переменную содержание регистра DR ибо сами знаете делать что-то в прерывании не комильфо.
значит флаг flag_receive установился и далее в цикле while файла main.c:
if(flag_receive){
Get();
flag_receive = 0;
}
Далее вызываем Get и всё складируется в buffer. Далее с помощью регулярки выдернуть всё что между кавычек.

Вариант 2

char buffer;
char str[];
void Get(){
    uint8_t f;
    buffer = USART1->DR;
    if(f){
        str[i++] = buffer;
    }
    if(buffer == """){
        if(f){
            f = 0;
        } else {
            f = 1;
        }
    }
}

То есть вытягиваем нужную строку непосредственно в процессе приёма байтов посимвольно. 
В коде могут быть ошибки, так как хотел показать идею.
А теперь главные вопросы
Если сначала набивать массив строкой, а потом разбирать регуляркой, как написать эту самую регулярку, что бы между кавычек вытащить?
И второй вопрос, что будет работать быстрее и эффективнее 1 или 2 вариант?

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

15 часов назад, supercelt сказал:

В самом прерывании я не стал записывать в переменную содержание регистра DR ибо сами знаете делать что-то в прерывании не комильфо

это прерывание будет работать в масштабе времени примерно так - один день ты ждешь байт из USART и примерно минуту его рассматриваешь. Сохранение одного единственного регистра в буфер добавит еще пару секундочек к той минуте. Критично? Запихивай все в буфер строки прямо из прерывания.

Чтобы начать разбор строки надо дождаться особых символов. Например '\n'. Иначе откуда знать что пора? Но есть один нюанс - если этот символ никогда не придет (ошибки приема или еще чтото) то буфер переполнится. Надо думать и об этом.

Сама строка дербанится уже просто библиотечными функциями string.h

У меня на HAL есть готовый код приема строки и разбора до командного процессора. Но тут видимо не тот случай

Изменено пользователем mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

В продолжении вопроса. Выделить из кавычек это я поторопился. Надо сначала ещё кучу всего сделать. Там условия такие, что строка может придти когда угодно, какая угодно. Я не знаю ее размер. Таким образом я решил что по мере поступления символов, а прерывании по usart1 я буду просто набивать буфер. А потом, когда он набьётся - разбирать строку. И вот проблема. Раз я не знаю размер строки, то когда она полностью придёт неизвестно. Проверять по символу \r\n не вариант, так как эти символы приходят вначале сообщения и в середине. Короче я думал думал и вот что придумал. Алгоритм такой:

После того, как сработало первое прерывание по приёму usart1, я запускаю таймер. Настраиваю его на прерывание по переполнению. Далее приходит второй символ, таймер сбрасываю, он не успевает переполнится. И так далее. И вот пришёл последний символ. прерывание сработало последний раз и сбросило таймер. Он продолжает тикать. А раз символов то больше нет, он благополучно вваливается в прерывание по переполнению. В обработчике вектора вырубаю таймер и выставляю флаг что я принял всё-таки строку. Алгоритм простой, но как всегда всё работает почему-то не так как надо. Если из кода, который ниже убрать включение прерывание на таймер, то работает, но таймер соответственно не останавливается. А если включить прерывание, что так и должно быть - ничего не работает. Похоже даже на зависание. И самое интересное. Что бы я не делал, в режиме отладки, на кусок кода обработчика прерывания таймера нельзя поставить точку останова. Везде можно а именно на этот кусок нельзя.

#include "gsm.h"
#include "lcd.h"
/////////////////////////////////////////////Buffer сделать c префиксом сетап. Может набить туда ещё команд? сделать вывод со 2 юсарта на комп, дублиовать экран на комп
char Buffer_CSCA[] = "AT+CSCA?\r\n";
char Buffer_Setup[] = "ATE0\r\n";//ATV0, ATE0, ATS4=000
char Buffer_Receive[100];
uint8_t flag_receive, buf, flag_setup, i, f, ex;
uint16_t co;
unsigned short buff[100];

void TIM12_IRQHandler(void){
		if(TIM12->SR & TIM_SR_UIF){ 
				TIM12->SR &= ~TIM_SR_UIF;
		}
		flag_receive = 1;
		TIM12->CNT = 0;
		TIM12->CR1 &= ~TIM_CR1_CEN;
}

void DMA1_Channel4_IRQHandler(void){ //Прерывание DMA
		if(DMA1->ISR & DMA_ISR_TCIF4){ //Если прерывание вызвано завершением передачи...
				DMA1->IFCR = DMA_IFCR_CTCIF4; //Сброс флага завершения передачи
				DMA1_Channel4->CCR &= ~DMA_CCR4_EN; //Вырубаем DMA
		}
}

void USART1_IRQHandler(void){ //Прерывание USART1
		if(USART1->SR & USART_SR_RXNE){ //Если прерывание вызвано по приёму USART1
				USART1->SR &= ~USART_SR_RXNE; //Сбрасываем флаг приёма USART1
				buff[i] = TIM12->CNT;
				Buffer_Receive[i++] = USART1->DR;
				LCD_Send_Data(USART1->DR);
								if(!f){
										TIM12->CR1 |= TIM_CR1_CEN;
										f = 1;
								} else {
										TIM12->CNT = 0;
								}
		}
}


void GSM_INI(void){
		RCC->APB2ENR |= RCC_APB2ENR_USART1EN;                           //USART1 clock
		RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN;        //PortA & Alt clock
		GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
		GPIOA->CRH |= GPIO_CRH_CNF9_1; 																	//Alternate function output Push-pull
		GPIOA->CRH |= GPIO_CRH_MODE9_1; 																//Speed 2 MHz
	
		/*RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //Test usart2
		GPIOA->CRL &= ~GPIO_CRL_CNF2_0; //Test usart2
		GPIOA->CRL |= GPIO_CRL_CNF2_1; //Test usart2
		GPIOA->CRL |= GPIO_CRL_MODE2_1; //Test usart2*/
	
//==USART1=======================================================================================	
		USART1->BRR = 0x9C4; //9600 бод скорость обмена
		USART1->CR1 |= USART_CR1_RXNEIE; //Разрешаем локальные прерывания по приёму USART
		USART1->CR1 |= USART_CR1_RE | USART_CR1_TE; //Включаем передатчик и приёмник
		USART1->CR1 |= USART_CR1_UE; //Включаем USART
	  NVIC_EnableIRQ(USART1_IRQn); //Разрешаем глобальные прерывания для USART1
//==============================================================================================
	
//==USART2=[TEST]===============================================================================
		/*USART2->BRR = 0x9C4;
		USART2->CR1 |= USART_CR1_TE;
		USART2->CR1 |= USART_CR1_UE;*/
//==============================================================================================	

//==DMA=========================================================================================
		RCC->AHBENR |= RCC_AHBENR_DMA1EN;
		DMA1_Channel4->CPAR = (uint32_t)&(USART1->DR); //С какой периферией работаем
		DMA1_Channel4->CMAR = (uint32_t)Buffer_Setup; //Указатель на нулевой элемент буфера откуда отправляем данные
		DMA1_Channel4->CCR |= DMA_CCR4_DIR | DMA_CCR4_MINC; //Читаем ИЗ памяти в периферию | при считывании увеличиваем на 1 адрес буфера Buffer, что бы при следующем считывани перейти на след ячейку памяти.
		DMA1_Channel4->CCR &= ~DMA_CCR4_PINC; //Увеличение на 1 адрема периферии выкл. Если вкл, то счётчик увеличится при след. чтении и тогда работа будет уже не с юсарт а с другой периферией
		DMA1_Channel4->CCR |= DMA_CCR4_TCIE;
		DMA1_Channel4->CNDTR = sizeof(Buffer_Setup); //Размер буфера данных
		DMA1_Channel4->CCR &= ~(DMA_CCR4_MSIZE | DMA_CCR4_PSIZE | DMA_CCR4_CIRC); //Память - 8 бит | Периферия - 8 бит | Не циклическая передача.
		DMA1_Channel4->CCR |= DMA_CCR4_PL_0; //Приоритет - средний (Medium)
		USART1->CR3 |= USART_CR3_DMAT; // Разрешаем USART использовать DMA
		NVIC_EnableIRQ(DMA1_Channel4_IRQn); //Разрешаем глобально прерывания по DMA1 канал 4
		DMA1_Channel4->CCR |= DMA_IT_TC; //Разрешаем локальнве прерывания по завершению передачи DMA1 канал 4
		DMA1_Channel4->CCR |= DMA_CCR4_EN; //Включаем DMA что бы передать настройки модулю GSM командой из Buffer
//==============================================================================================	

//==TIMER12=for=receive bytes from USART========================================================
		RCC->APB1ENR |= RCC_APB1ENR_TIM12EN;
		TIM12->CR1 |= TIM_CR1_ARPE;
		TIM12->ARR = 0xFFFF;
		TIM12->PSC = 0xEF;
		TIM12->CNT = 0;
		NVIC_EnableIRQ(TIM12_IRQn);
		TIM12->DIER |= TIM_DIER_UIE;
		TIM12->EGR = TIM_EGR_UG;      //******Вызываем Update Event что бы таймер не уходил в прерывание сразу после старта
		__NOP();											//
		__NOP();											//
		TIM12->SR &= ~(TIM_SR_UIF);    //****************************
		//TIM16->CR1 |= TIM_CR1_CEN;
//==============================================================================================
//
//В инициализации сделать поочерёдную отправку команд настроек и получение ответов с выводом сообщений на экран. Т.е. Постали - получили -вывели. След ком. послали получили вывели.
//поменять 16 таймер на другой, не совмещенный по векторам ни с каким другим

}

void GSM_COMM_SEND_SMS_SERVICE(){
		DMA1_Channel4->CMAR = (uint32_t)Buffer_CSCA;
		DMA1_Channel4->CNDTR = sizeof(Buffer_CSCA);
		DMA1_Channel4->CCR |= DMA_CCR4_EN;
}

void GSM_COMM_GET_SMS_SERVICE(){
		buf = USART1->DR;
		LCD_Send_Data(buf);
		
		//while(!(USART2->SR & USART_SR_TC)); //Ждем пока бит TC в регистре SR станет 1
		//USART2->DR = buf;
}
//---------------------------------------------------------------

 

Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Код ваш не читал но вот что посоветую.

\n\r это разделитель строк, соответственно если вы не ждете такой ситуации:

"начало стро\n\r

ки"\n\r

То можете спокойно использовать символ новой строки как признак того что пора заполненный буфер обработать и очистить. Ну естественно, буффер будет обрабатываться несколько раз, для каждой новой строки - тут вылавливайте именно ту строку которая содержит ваши кавычки. Далее, по поводу заранее неизвестн длины строки. Придумайте лимит, скажем 1000 символов. Если пришедшая строка больше - то просто или игнорим остальные символы до следующей комбинации "новая строка", или просто обрабатываем /очищаем буффер, как только он заполнился, и пишем оставшиеся символы в буфер сначала, как будто встретили символ новой строки. Тут уж как хотите.

никакой таймер тут не нужен.

выделить подстроку из строки можно Или с помощью strtok() или вручную как то так:

void extract_between_quotes(char* s, char* dest) {

int in_quotes = 0; *dest = 0; while(*s != 0) { if(in_quotes) { if(*s == '"') return; dest[0]=*s; dest[1]=0; dest++; } else if(*s == '"') in_quotes=1; s++; }

}

Учтите ситуации когда кавычка или одна в строке или ее вовсе нет или их три и больше.

Примеров в сети навалом

успехов!

Изменено пользователем bobbjenkins
Ссылка на комментарий
Поделиться на другие сайты

TO  mail_robot:

Цитата

У меня на HAL есть готовый код приема строки и разбора до командного процессора. Но тут видимо не тот случай

Не могли бы вы поделиться этим кодом?

В принципе я уже состряпал терминал, хотелось бы поглядеть на ваш код.

Ссылка на комментарий
Поделиться на другие сайты

вот, упрощенная версия. Библиотеки мысленно подкиньте

Src.zip

Изменено пользователем mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...