Прием и обработка строк в CVAVR

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

Уважаемые коллеги!!!

Уже который месяц не могу справится с проблемой надежного приема строк по USART с телефона в контроллер для обработки. Все дело в том, что существующие для этого средства CVAVR мягко говоря не вполне комфортно приенять. А именно пытаюсь приручить функцию scanf(). Она работает достаточно нестабильно, контроллер зависает, если функция вызывается без наличия строк в буффере.

даже если проверять наличия символов в буффере напрмер так:

Код: Выделить всё

 if(rx_counter) scanf(...)
где rx_counter - это счетчик символов в буффере в генерируемом коде CVAVR

Все равно программа виснет в этом месте.
Я подозреваю что нужно реализовать проверку ни сколько наличия символов в буффере, сколько непрочтенного символа конца строки.
Может есть у кого какие наработки по этому вопросу
Any Problems????
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18759
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

я буквально 10 минут назад закончил свои ковыряния со строками... но в WinAVR. принципиально это дела не меняет, НО! я использую синхронный ввод. т.е. без применения циклических буферов приема и обработки прерываний. тупо жду. пока придет символ в UDR.

анализ библиотеки avr-libc из комплекта WinAVR показал, что наиболее надежно работать с fgets(), так как она позволяет контролировать переполнение буфера приема строки. однако, я сделал иначе.

если интересует алгоритм - могу рассказать. в принципе, и кодом могу поделиться, если не пугает то, что он для WinAVR :)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Опытный кот
Аватара пользователя
Сообщения: 890
Зарегистрирован: Вт янв 20, 2009 14:49:08
Откуда: Гондурас, Мурманск

Сообщение DrWatson »

Думаю в данном случае лучше считывать принятые байты по одному в отдельный буфер, а после прочтения символа конца строки уже использовать пресловутый scanf(), для верности можно в этом буфере заменить символ конца строки нулем или ноль добавить после него.
Примерно так.

Код: Выделить всё

if(rx_counter)
{
   MyBuffer[i]=getchar();
   if((MyBuffer[i]==0x0d)&&(i>0))
  {
      MyBuffer[i]=0;
      scanf(MyBuffer, ....);
      i=0;
   }
}
else i++;
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18759
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

DrWatson писал(а):Примерно так.
да, примерно так :) только в данном случае получается двойная буфферизация - CVAVR создает циклический буфер приема, а в дополнение вы из этого буфера себе в буфер все копируете. расточительно по ОЗУ
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Опытный кот
Аватара пользователя
Сообщения: 890
Зарегистрирован: Вт янв 20, 2009 14:49:08
Откуда: Гондурас, Мурманск

Сообщение DrWatson »

Про ОЗУ - согласен. Но переделывать код Мастера - себе дороже. Хотя в нем можно задать буфер больше, чем максимальная длина пакета данных и попытаться работать напрямую с буфером.
ИМХО поудалять нафиг все, что Мастер написал кроме заголовков обработчиков прерываний. И переписать все по-своему.
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

ARV

Будте добры и вы кодом поделитесь!!!
Думаю не будет принципиальной разницы в ЦВАВР и ВинАВР, ведь не код собираюсь копировать, а принцып понять так сказать на примере
Any Problems????
Контактная информация:
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

2 DrWatson

Спасибо за хороший пример, чуть чуть его поправлю можно??? :))
Там необходимо использовать функцию sscanf();
Хотя... блин, может и я туплю выто про WinAVR....
в CVAVR функция sscanf читает из входной строки а scanf() только с усарта используя getchar();
Any Problems????
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 890
Зарегистрирован: Вт янв 20, 2009 14:49:08
Откуда: Гондурас, Мурманск

Сообщение DrWatson »

Да, действительно sscanf();
просто я ими не пользовался, потому и ошибся :)
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

А в WinAVR больше возможностей по сравнению с ЦВАВР??
Может уже стоит переходить от простоты использования к эффективности 8)
Any Problems????
Контактная информация:
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

И еще вопросец!!!
Еще телефон передает после символа 0x0D символ 0x0A, может его вообще просто игнорировать...
Предположим при запросе AT, телефон ответит OK(в терминале).
Реально же с него идет "0D 0A 4F 4B 0D 0A"

Вот на этот набор символов ваша программа тоже среагирует не совсем четко. Первый OD она проигнорирует, а вот OA запишет себе - это мне не желательно.
Как вы считаете?? может стоит 0A игнорировать???

И кстати.... уточню. Ваш код DrWatson должен быть циклом дополнен правильно??? и строка else i++; должна относиться к if((MyBuf.... или я не прав... в данном виде он работать не будет.. нет цикличности, не ставить же его в прерывание по приему символа.
Any Problems????
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18759
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

в WinAVR нет мастера кода, поэтому многое там надо делать ручками. зато лучше понимаешь, что к чему.
по поводу символов завершения строки. символ '\n' надо просто игнорировать, а символ '\r' заменять на 0, т.е. обозначать им конец строки. scanf делает это автоматически, если надо - делайте это вручную :)

я в своем случае сделал очень просто: завел массив строк-команд во FLASH, потом читаю из потока строку с ограничением длины, потом при помощи библиотечной функции strtok() парсю строку по словам (первое слово - команда, остальные - аргументы команды), затем обычным циклом перебираю массив строк и сравниваю с первым словом в принятой строке. как совпало - значит, я знаю, какая команда принята. это вкратце, код завтра покажу.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 890
Зарегистрирован: Вт янв 20, 2009 14:49:08
Откуда: Гондурас, Мурманск

Сообщение DrWatson »

1. мой кусок кода конечно же предполагался в теле цикла, я while()... ставить не стал, мало ли что там еще в этом цикле должно делаться по ходу программы, а начальная инициализация переменной i перед циклом.
2. Да действительно, с else Вы правы. Совсем я невнимательным стал - старею :)
3. В принципе можно сделать проверку и на то и на другое, а лишнее отбрасывать:

Код: Выделить всё

i=0;
while(1)
{
   if(rx_counter) 
   { 
      MyBuffer[i]=getchar(); 
      if(((MyBuffer[i]==0x0d)||(MyBuffer[i]==0x0a))
      {
          if(i>0)
          {
             MyBuffer[i]=0; 
             sscanf(MyBuffer, ....); 
             i=0;
          }
      }
      else i++; 
   }
    // Здесь еще возможно какие-то действия
} 
В этом случае "лишние" символы будут пропускаться, т.е. независимо от того придет ли в конце строки CR или LF или оба в той или иной последовательности - результат должен быть одинаков.
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18759
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

вот куски обещанного мной кода.
Команды у меня имеют такой формат: CMD [аргументы].

1. сделаны такие объявления

Код: Выделить всё

/// тип: символ в памяти программ
typedef PROGMEM char pgmchar;
/// тип-перечисление: варианты команд
typedef enum _command_t {
	cmd_dig_out		/// управление релейными выходами
	,cmd_dig_in		/// ввод состояний дискретных линий
	,cmd_extend		/// зарезервированная команда расширенного управления
	,cmd_ana_in		/// получение значений АЦП по входам
	,cmd_info		/// информация о программе
	,cmd_status		/// текущее состояние
	,cmd_help		/// справка по командам
	,cmd_reset		/// программный "горячий" сброс
	,cmd_last		// ПОСЛЕДНЯЯ команда - не обрабатывается, просто счетчик
} command_t;

// строки в сегменте памяти программ
pgmchar dout[]	= "DOUT";
pgmchar din[]	= "DIN";
pgmchar ext[]	= "EXT";
pgmchar ain[]	= "AIN";
pgmchar info[]	= "INFO";
pgmchar status[]= "STATUS";
pgmchar hlp[]	= "HELP";
pgmchar rst[]	= "RESET";
pgmchar s_ok[]	= "OK";
pgmchar s_err[]	= "ERROR";

/// массив строк-команд
PGM_P cmd_str[cmd_last] = {
		// порядок строк должен совпадать с порядком констант cmd_xxxx
		dout, din, ext, ain, info, status, hlp, rst
};
2. Функция ввода строки.

Код: Выделить всё

/** Ожидание и ввод строки-команды
 * Функция ждет, пока будет введена строка. Возвращает управление только в том
 * случае, если получен символ "конец строки".
 * @param str - буфер для приема строки
 * @param len - максимальное число символов в команде
 * \note Все введеные символы сверх #len игнорируются
 */
static void getstring(char* str, uint8_t len){
	char ch, ch1;
	do{
		ch1 = getchar();				// получаем символ из потока ввода
		if(ch1 == '\n') continue;		// если это перевод строки - игнорируем
		if((ch1 == '\r') 				// если это возврат каретки
			|| (len == 0))				// или сверхлимитные символы,
			ch = 0;						// то это значит конец ввода,
		else
			ch = ch1;					// иначе символ надо сохранить
		if(len){						// только если лимит не исчерпан,
			*str++ = ch;				// сохраняем введенный символ
			len--;						// и ведем счет символов
		}
	} while (ch1 != '\r');				// если в потоке конец строки - выходим
}
Обратите внимание, что функция принимает символы в любом количестве (до нажатия ENTER), но реально возвращает не более заданного количества! это было нужно мне, но может быть лишним для вас.

3. функция приема команды

Код: Выделить всё

/** Возвращает код принятой команды
 * Функция вводит строку-команду из потока ввода, ищет совпадения в массиве
 * поддерживаемых команд cmd_str и, если находит, возвращает тип команды.
 * Если введена неподдерживаемая команда, функция выводит в поток вывода
 * сообщение об ошибке ввода и не возвращает управление.
 * @param arg - указатель на строку "остатка" команды, т.е. части команды, содержащей параметры
 * @return - тип принятой команды
 */
static command_t get_cmd(char **arg){
	static char tmp[10];
	char *cmd;
	do{
		getstring(tmp,10);						// прием строки-команды
		strupr(tmp);							// перевод ее к верхнему регистру
		cmd = strtok(tmp," ");					// выделение первой части команды
		for(uint8_t i = 0; i < cmd_last; i++){	// поиск первой части в массиве
			if(strcmp_P(cmd, cmd_str[i])==0){	// если найдено совпадение,
				*arg = strtok(NULL," ");		// то получаем строку параметров
				if(strtok(NULL," ")){			// если параметров больше одного,
					break;						// то это ошибка ввода
				}
				else return i;					// а иначе - возвращаем результат
			}
		}
		if(tmp[0]) result(ER);					// непустая ненайденная строка - ошибка
	} while(1);									// крутимся в цикле вечно
}
обратите внимание, что обе функции не возвращают управление, пока не примут подходящую команду! это было нужно мне, но может быть ненужным вам.

4. вот так я обрабатываю полученные команды в главном цикле

Код: Выделить всё

	char *arguments;		// строка аргументов команды
		cmd = get_cmd(&arguments);
		switch(cmd){
		//--------------------------------------------------------------------------
		case cmd_dig_out:// управление релейными выходами
			number = strtoul(arguments,NULL,0);		// получаем маску вывода
			if(errno == ERANGE){					// если параметр - не число,
				result(ER);							// то сигнализируем об ошбике
				break;								// и все.
			}
главное в этом кусочке, на что я хотел обратить ваше внимание, это обработка "хвоста" команды, т.е. списка аргументов.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 828
Зарегистрирован: Пн мар 16, 2009 21:40:57

Сообщение ikarab »

Спасибо. Все четко прокомментировано. Красиво ИМХО.
Контактная информация:
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

Да.. все круто разложено... Спасиб :)))
Any Problems????
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 828
Зарегистрирован: Пн мар 16, 2009 21:40:57

Сообщение ikarab »

Еще бы проектик ! Чтоб чик-чик и посимулировать ...
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18759
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

ikarab писал(а):Еще бы проектик ! Чтоб чик-чик и посимулировать ...
ну чикайтесь, ежели хочется :) симулянты... :))) не забудьте в настройках терминала протеуса эхо включить...
Вложения
demo.rar
проект протеуса +
исходники модулей +
готовый ELF-файл для загрузки в протеус +
makefile для сборки своими силами
(33.31 КБ) 338 скачиваний
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 828
Зарегистрирован: Пн мар 16, 2009 21:40:57

Сообщение ikarab »

Спасибо ! Когда видишь как все это крутится-вертится - повеселей всеж ...
Контактная информация:
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

Уважаемый ARV

А в вашем случае прога не висит в месте:

Код: Выделить всё

.
ch1 = getchar();
.
в случае, если на вход ничего не поступало????

Например в CVAVR в реализации функции getchar() есть строка:

Код: Выделить всё

while (rx_counter==0);


Нужно полагать перед тем как обратиться к этой функции нужно убедиться, что что-нить на вход пришло???? или в WinAVR эта функция написана по-другому??
Any Problems????
Контактная информация:
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Пн мар 30, 2009 15:55:13
Откуда: г. Комсомольск на Амуре

Сообщение kupriyanov »

Кстати в функции sscanf() необходимость полностью отпадает.
Any Problems????
Контактная информация:
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»