Уже который месяц не могу справится с проблемой надежного приема строк по USART с телефона в контроллер для обработки. Все дело в том, что существующие для этого средства CVAVR мягко говоря не вполне комфортно приенять. А именно пытаюсь приручить функцию scanf(). Она работает достаточно нестабильно, контроллер зависает, если функция вызывается без наличия строк в буффере.
даже если проверять наличия символов в буффере напрмер так:
где rx_counter - это счетчик символов в буффере в генерируемом коде CVAVR
Все равно программа виснет в этом месте.
Я подозреваю что нужно реализовать проверку ни сколько наличия символов в буффере, сколько непрочтенного символа конца строки.
Может есть у кого какие наработки по этому вопросу
я буквально 10 минут назад закончил свои ковыряния со строками... но в WinAVR. принципиально это дела не меняет, НО! я использую синхронный ввод. т.е. без применения циклических буферов приема и обработки прерываний. тупо жду. пока придет символ в UDR.
анализ библиотеки avr-libc из комплекта WinAVR показал, что наиболее надежно работать с fgets(), так как она позволяет контролировать переполнение буфера приема строки. однако, я сделал иначе.
если интересует алгоритм - могу рассказать. в принципе, и кодом могу поделиться, если не пугает то, что он для WinAVR
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Думаю в данном случае лучше считывать принятые байты по одному в отдельный буфер, а после прочтения символа конца строки уже использовать пресловутый scanf(), для верности можно в этом буфере заменить символ конца строки нулем или ноль добавить после него.
Примерно так.
да, примерно так только в данном случае получается двойная буфферизация - CVAVR создает циклический буфер приема, а в дополнение вы из этого буфера себе в буфер все копируете. расточительно по ОЗУ
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Про ОЗУ - согласен. Но переделывать код Мастера - себе дороже. Хотя в нем можно задать буфер больше, чем максимальная длина пакета данных и попытаться работать напрямую с буфером.
ИМХО поудалять нафиг все, что Мастер написал кроме заголовков обработчиков прерываний. И переписать все по-своему.
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
Будте добры и вы кодом поделитесь!!!
Думаю не будет принципиальной разницы в ЦВАВР и ВинАВР, ведь не код собираюсь копировать, а принцып понять так сказать на примере
Спасибо за хороший пример, чуть чуть его поправлю можно???
Там необходимо использовать функцию sscanf();
Хотя... блин, может и я туплю выто про WinAVR....
в CVAVR функция sscanf читает из входной строки а scanf() только с усарта используя getchar();
И еще вопросец!!!
Еще телефон передает после символа 0x0D символ 0x0A, может его вообще просто игнорировать...
Предположим при запросе AT, телефон ответит OK(в терминале).
Реально же с него идет "0D 0A 4F 4B 0D 0A"
Вот на этот набор символов ваша программа тоже среагирует не совсем четко. Первый OD она проигнорирует, а вот OA запишет себе - это мне не желательно.
Как вы считаете?? может стоит 0A игнорировать???
И кстати.... уточню. Ваш код DrWatson должен быть циклом дополнен правильно??? и строка else i++; должна относиться к if((MyBuf.... или я не прав... в данном виде он работать не будет.. нет цикличности, не ставить же его в прерывание по приему символа.
в WinAVR нет мастера кода, поэтому многое там надо делать ручками. зато лучше понимаешь, что к чему.
по поводу символов завершения строки. символ '\n' надо просто игнорировать, а символ '\r' заменять на 0, т.е. обозначать им конец строки. scanf делает это автоматически, если надо - делайте это вручную
я в своем случае сделал очень просто: завел массив строк-команд во FLASH, потом читаю из потока строку с ограничением длины, потом при помощи библиотечной функции strtok() парсю строку по словам (первое слово - команда, остальные - аргументы команды), затем обычным циклом перебираю массив строк и сравниваю с первым словом в принятой строке. как совпало - значит, я знаю, какая команда принята. это вкратце, код завтра покажу.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
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 или оба в той или иной последовательности - результат должен быть одинаков.
- Если вы такие умные, то почему тогда строем не ходите?
ἓν οἶδα ὅτι οὐδὲν οἶδα (с) Σωκράτης
/** Ожидание и ввод строки-команды
* Функция ждет, пока будет введена строка. Возвращает управление только в том
* случае, если получен символ "конец строки".
* @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), но реально возвращает не более заданного количества! это было нужно мне, но может быть лишним для вас.
/** Возвращает код принятой команды
* Функция вводит строку-команду из потока ввода, ищет совпадения в массиве
* поддерживаемых команд 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; // и все.
}
главное в этом кусочке, на что я хотел обратить ваше внимание, это обработка "хвоста" команды, т.е. списка аргументов.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...