Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: USART запрос/ответ
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
mr_smit
Здравствуйте. Необходимо обмениваться данными с устройством. Каждые 250 мс необходимо отправить:
Запрос: 82 10 F1 21 01 A5
и получить:
Ответ: 80 F1 10 26 61 01 3B 90 41 04 00 00 00 00 47 80 00 00 00 52 52 80 18 00 8E 00 5C 00 00 00 00 00 00 00 00 00 FF FF DD A4 47 02 CE

Данные в ответе выделены зеленым (меняются). Длинна ответа не меняется. Как принять этот массив???

В CVAVR использую мастер начальной настройки. Там создается кольцевой буффер. Т.е. первый байт который попал в буффер первым из него и выйдет. Запрос этого байта через функцию getchar(). Никак не соображу как реализовать. Т.е. получается мне надо вызвать эту функцию "длинна массива" раз. Или создать ещё один промежуточный буффер и в него считывать при каждом вызове getchar() ? Или может какой алгоритм существует. Короче запутался.
AHTOXA
Да, заведите второй буфер, для пакета. Заведите счётчик принятых байтов. При отправке запроса обнуляйте его. И потом начинайте увеличивать по приёму каждого байта. Пока счётчик меньше пяти - проверяем заголовок. Досчитали до пяти - значит пошли данные, складываем их в буфер. Досчитали до скольки-то там - данные закончились, проверяем хвостик пакета, и, если всё правильно, то обрабатываем принятые данные. После этого можно снова отправлять запрос.
Добавьте ещё тайм-аут на приём символа, для надёжности.
ILYAUL
80 F1 10 26 , а вот 0x26 это счётчик передаваемых байт - похоже
mr_smit
Цитата(ILYAUL @ Dec 17 2011, 23:33) *
80 F1 10 26 , а вот 0x26 это счётчик передаваемых байт - похоже

CE - контрольная сумма (2 младших разряда)

Цитата(AHTOXA @ Dec 17 2011, 22:08) *
Да, заведите второй буфер, для пакета

Да я вот думаю может нафиг вообще этот кольцевой буффер blink.gif Чего то он меня с толку сбивает. Создать буффер в который будут приниматься байты. Когда нужно - читать его. А при каждом следующем запросе начинать прием байтов в начало буффера, перезаписывая его.
AHTOXA
Кольцевой буфер оставьте, он вам никак не мешает. Зато поможет не пропустить символ на приёме, если вдруг затянете с обработкой предыдущего символа. Сделайте его небольшим, байт на 8, авось памяти хватитsm.gif
А остальное - всё как вы написали:
Цитата(mr_smit @ Dec 18 2011, 03:24) *
Создать буффер в который будут приниматься байты. Когда нужно - читать его. А при каждом следующем запросе начинать прием байтов в начало буффера, перезаписывая его.
mr_smit
Просто я не понимаю каким образом мне вызывать getchar()

Код
if (rx_counter>42) {     // всего 43 байта в ответе
      char i;
      char buf[50];
      for (i=0;i<42;i++) {
        buf[i] = getchar();
      };
}

Так как то криво sad.gif
mr_smit
В общем убрал кольцевой буффер и сделал вот так:
Код
#define BUFFER_SIZE 200
unsigned char buffer[BUFFER_SIZE];    // приемный буффер
unsigned char startCommunication[] = {0x81,0x10,0xf1,0x81,0x03};
volatile unsigned char counter;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   buffer[counter]=data;
   counter++;
   if (counter >= BUFFER_SIZE) {  
     counter = 0;
     }
   }
}

Если вдруг буффер переполнится то пишем данные в его начало. Просто не сойдется контрольная сумма. Получим данные при следующем запросе.

Теперь отправка команды.
Код
void SendCommand (unsigned char *command) {
  counter = 0;
  while (*command) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}

Использование:
Код
SendCommand(startCommunication);


Работает (смотрю COM порт сниффером). Но если написать:
Код
flash unsigned char startCommunication[] = {0x81,0x10,0xf1,0x81,0x03};

То на строчке:
Код
SendCommand(startCommunication);

Выдает ошибку:
Error: function argument #1 of type 'flash unsigned char [5]' is incompatible with required parameter of type 'unsigned char *'
Я так понимаю нельзя со строкой из флеша напрямую работать. Так то конечно всё равно, но строчки статичные, пусть лучше во флеше лежат. Как то можно к ним обратиться в моём случае?
mr_smit
И еще:
Код
unsigned char startCommunication[] = {0x81,0x10,0xf1,0x81,0x03};
...
void SendCommand (unsigned char *command) {
  counter = 0;
  while (*command) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}
...
SendCommand(startCommunication);

Почему то выдает один лишний байт blink.gif Откуда 04 ???
Нажмите для просмотра прикрепленного файла
AHTOXA
Цитата(mr_smit @ Dec 18 2011, 15:07) *
Выдает ошибку:
Error: function argument #1 of type 'flash unsigned char [5]' is incompatible with required parameter of type 'unsigned char *'
Я так понимаю нельзя со строкой из флеша напрямую работать. Так то конечно всё равно, но строчки статичные, пусть лучше во флеше лежат. Как то можно к ним обратиться в моём случае?

Я не знаю, как это сделано в CVAVR, но видимо надо объявить SendCommand как
Код
void SendCommand (flash unsigned char *command)

Ну или что-то типа этого.

Цитата(mr_smit @ Dec 18 2011, 18:14) *
Почему то выдает один лишний байт blink.gif Откуда 04 ???

Это вам ещё повезло, что всего один лишний байтsm.gif
У вас в SendCommand идёт проверка на ноль (признак конца команды). А нуля-то и нет! Добавьте:
unsigned char startCommunication[] = {0x81,0x10,0xf1,0x81,0x03,0};
mr_smit
AHTOXA, спасибо!!!!!
Код
void SendCommand (flash unsigned char *command)

Работает!!!
Код
flash unsigned char startCommunication[] = {0x81,0x10,0xf1,0x81,0x03,0};

Работает!!!!

А может можно как то переделать функцию SendCommand так чтобы не дописывать 0 в комманды? Просто я с этими указателями чего то путаюсь sad.gif

А вот этот кусок вообще не понимаю:
Код
while (*command) {}

каким макаром тут проверка на ноль идет blink.gif Это же указатель на первый элемент массива. Причем тут ноль?

P.S. Да, не сам писАл. Нашел пример.
sigmaN
Цитата
Код
while (*command) {}
там наверно дальше где-то есть
Код
command++;


аа вот оно
Код
UDR = *command++;
т.е. UDR присваивается значение первого элемента массива, а сам указатель после операции указывает уже на второй, потом на следующей итерации снова while проверяет его и если не ноль, то снова идёт присвоение UDR а command указывает на следующий элемент.... и так пока не встретится ноль в общем )
AHTOXA
Цитата(mr_smit @ Dec 19 2011, 00:20) *
А вот этот кусок вообще не понимаю:

конструкция
Код
while (выражение) {команды}
выполняет команды пока выражение не ноль.
command - указатель, да. А *command - то, на что указывает указатель. Про продвижение указателя sigmaN уже объяснил.
ЗЫ. Читать букварь по Си, срочно! Без понимания основ трудно написать что-то приличное.
_Артём_
Цитата(mr_smit @ Dec 18 2011, 20:20) *
А может можно как то переделать функцию SendCommand так чтобы не дописывать 0 в комманды? Просто я с этими указателями чего то путаюсь sad.gif


Переделать можно, например так:

Код
void SendCommand (unsigned char *command, unsigned char length) {
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}


B вызывать соотв.:
Код
SendCommand(startCommunication, 5);



Цитата(mr_smit @ Dec 18 2011, 20:20) *
А вот этот кусок вообще не понимаю:
Код
while (*command) {}

каким макаром тут проверка на ноль идет blink.gif

P.S. Да, не сам писАл. Нашел пример.


*command = чтение байта по указателю command и если там 0, то получается while (0) - цикл заканчивается.

Цитата(mr_smit @ Dec 18 2011, 20:20) *
Это же указатель на первый элемент массива. Причем тут ноль?


На первый байт он указывал на входе в функцию.
Код:
Код
UDR = *command++;

означает, что в UDR пишется байт по указателю, затем указатель инкрементируется (++ - постинкремент).
mr_smit
Ну примерно понял. Спасибо. И ещё. Как лучше организовать запрос каждые 250 мс? Я завел 16-ти битный таймер на ~250 мс (прерывание по совпадению). Ну не могу же я функцию SendCommand в прерывание ставить. Она ведь долго выполняется. Завести переменную битовую как флаг разрешения, инвертировать её по прерыванию и опрашивать в основном цикле? Изменилась с 0 в 1 - SendCommand, из 1 в 0 - SendCommand. Или как то красивее это можно сделать?
sigmaN
Цитата
Завести переменную битовую как флаг разрешения, инвертировать её по прерыванию и опрашивать в основном цикле?
Вполне себе нормальный подход. Вопрос только в красоте его исполнения и универсальности.
Вот тут покрутите учебный курс http://easyelectronics.ru/category/avr-uchebnyj-kurs как раз описывается служба таймеров и типа планировщик задач. Всё на Си.
mr_smit
Цитата(sigmaN @ Dec 19 2011, 11:16) *
Вот тут покрутите учебный курс http://easyelectronics.ru/category/avr-uchebnyj-kurs

Курс этот я видел/читал. Просто подумал может кто сталкивался с такой задачей. Может как то проще можно. Хотя куда уж sm.gif Остановлюсь на этом пока.

Вот так сделал:
Код
volatile bit OldState, StartSend;
...
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)     // каждые 250 мс
{
  OldState = StartSend;    // запоминаем состояние
  StartSend^=1;            // инвертируем бит
}
...
if (StartSend != OldState) {}
mr_smit
У меня тут вопрос появился. Нигде не смог найти толкового описания как переводить из одной системы исчисления в другую. В общем что мне надо:

В "моём" ответе 11-й байт это температура. Формула для пересчета в реальную температуру: N=E-40 [°C]. E - передаваемое значение, N - физическая величина. В примере это значение равно 47. Берем windows калькулятор. Переводим 47 hex в dec. Получаем 71. Дальше 71-40=31°C. Собственно говоря как это сделать? И как быть если температура будет отрицательной?

Код
unsigned char temp;
char convert[16];
...
temp = buffer[10];
temp =  (преобразовать в dec) - 40;     // ????
sprintf(convert,"%u",temp);
// вывод на дисплей char

Т.е. 0х47 hex -> 71 dec ??? И как быть с отрицательной температурой?
И есть ли что то быстрее sprintf ?
toweroff
как обычно, наверное... знаковый байт, старший бит - знак... если 1 - число отрицательное и содержимое 7 младших битов есть дополнение до 0
mr_smit
Ладно, мне надо вычесть 40. Т.е. 40 dec в hex будет 28. Тогда:
Код
unsigned char temp;
char convert[16];
...
temp = buffer[10];
temp = temp - 28;
if ((temp >> 8) == 1) {       // если отрицательное
  sprintf(convert,"-%02u",temp);
}
else {
  sprintf(convert,"%02u",temp);
}
// вывод на дисплей char

Правильно? Или нет?
Палыч
Цитата(mr_smit @ Dec 20 2011, 21:35) *
Ладно, мне надо вычесть 40. Т.е. 40 dec в hex будет 28. ... Правильно? Или нет?

Коль нужно вычесть сорок, то и вычитайте
Код
temp = temp - 40;

Если нужно использовать шестнадцатиричную константу:
Код
temp = temp - 0x28;
toweroff
Цитата(mr_smit @ Dec 20 2011, 21:35) *
Код
if ((temp >> 8) == 1) {       // если отрицательное

Правильно? Или нет?

а не проще вот так?
Код
if (temp & 0x80) {       // если отрицательное


да и %02u - вроде как тут нужно %03d, одно место останется под знак, да и число будет выводиться как обычное десятичное знаковое, а не беззнаковое
mr_smit
Цитата(toweroff @ Dec 20 2011, 21:48) *
а не проще вот так?
Код
if (temp & 0x80) {       // если отрицательное

да и %02u - вроде как тут нужно %03d, одно место останется под знак, да и число будет выводиться как обычное десятичное знаковое, а не беззнаковое


Решил потестировать немного:
Код
unsigned char temp = 0x19;
char convert[16];
...
temp = temp - 0x28;
if (temp & 0x80) {
  sprintf(convert,"-%03d",temp);
  put_string(115,4,convert,0xF800,1);
}
else {
  sprintf(convert,"%02d",temp);
  put_string(115,4,convert,0xF800,1);
}


Если написать unsigned char temp = 0x47, то выводится число 31, всё правильно (47hex - 28hex = 1F hex ->31dec)
Но если написать unsigned char temp = 0x19, то выводится число -241, а не -15 (19hex - 25hex = FFFFFFFFFFFFFFF1 hex -> -15dec)
Всё равно не понимаю как минус вывести.
P.S. F1 hex = 241 dec
toweroff
Цитата(mr_smit @ Dec 20 2011, 23:27) *
Решил потестировать немного:
Код
unsigned char temp = 0x19;
char convert[16];
...
temp = temp - 0x28;
if (temp & 0x80) {
  sprintf(convert,"-%03d",temp);
  put_string(115,4,convert,0xF800,1);
}
else {
  sprintf(convert,"%02d",temp);
  put_string(115,4,convert,0xF800,1);
}


Если написать unsigned char temp = 0x47, то выводится число 31, всё правильно (47hex -> 71dec -> 71-40)
Но если написать unsigned char temp = 0x19, то выводится число -241, а не -15 (19hex -> 25dec -> 25-40)


ну тогда вот так:
Код
unsigned char temp = 0x19;
char convert[4]; // куда больше-то?
...
temp = temp - 40;
sprintf(convert, "%s%02d", ((temp & 0x80)? "-": ""), ((temp & 0x80)? (unsigned char)(0-temp): temp) );
put_string(115,4,convert,0xF800,1);


ну это на скорую руку. Скорее всего, что-то не так с отображением sprintf, должно быть нормально. Сама она должна разруливать отрицательные числа
mr_smit
Цитата(toweroff @ Dec 20 2011, 15:17) *
...и содержимое 7 младших битов есть дополнение до 0

Дошло. В случае отрицательной температуры надо добавить: temp = 256 - temp;

Код
unsigned char temp = 0x19;
char convert[16];
...
temp = temp - 0x28;
if (temp & 0x80) {
  temp = 256 - temp;
  sprintf(convert,"-%d",temp);
  put_string(115,4,convert,color_chislo,1);
}
else {
  sprintf(convert,"%d",temp);
  put_string(115,4,convert,color_chislo,1);
}

Выводит -15. Ура! Спасибо!
XVR
Жуть, столько телодвижений вместо того, что бы просто написать signed char temp (как у вас по сути и должно быть)
Код
signed char temp = 0x19;
char convert[16];
...
temp = temp - 0x28;
sprintf(convert,"%d",temp);
toweroff
Цитата(XVR @ Dec 21 2011, 15:18) *
signed char temp

Цитата(toweroff)
ну это на скорую руку. Скорее всего, что-то не так с отображением sprintf, должно быть нормально. Сама она должна разруливать отрицательные числа

ну точно я вчера уработался - сидел еще форматирование printf лопатил biggrin.gif
mr_smit
Цитата(XVR @ Dec 21 2011, 14:18) *
Жуть, столько телодвижений вместо того, что бы просто написать signed char temp (как у вас по сути и должно быть)
Код
signed char temp = 0x19;
char convert[16];
...
temp = temp - 0x28;
sprintf(convert,"%d",temp);

Спасибо за код.

Скажите ещё почему sizeof не работает. Выводит только первые 2 байта:
Код
void SendCommand (unsigned char *command) {
  unsigned int  length = 0;
  length = sizeof(command);
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}
toweroff
Цитата(mr_smit @ Dec 21 2011, 22:10) *
А в чем разница между signed char и char? В учебнике написано что это одно и то же. Но signed char выводит -15, а char выводит 241

signed потому и обрабатывается как число со знаком, со всеми вытекающими (старший бит, дополненте и т.д.)
unsigned - число без знака

посмотрите документацию компилятора на Ваш проц, там должны быть указаны размерности char, signed char и unsigned char с диапазонами значений
_Артём_
Цитата(mr_smit @ Dec 21 2011, 20:10) *
Спасибо за код.

Скажите ещё почему sizeof не работает. Выводит только первые 2 байта:
Код
void SendCommand (unsigned char *command) {
  unsigned int  length = 0;
  length = sizeof(command);
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}


Вы не правы: это именно sizeof() и работает, и работает он так как надо, то есть так как Вы указали:

length = sizeof(command); = размер command, который есть тип unsigned char * - на многих 8-битных МК (например АВР), для указателя на байт хватает 2 байта. Вот у Вас два байта и шлются в порт, на 32 битных МК слалось бы, наверное 4 байта.

Совет: Укажите длину посылки как параметр функции.

Код
[code]void SendCommand (unsigned char *command, unsigned int  length ) {
// (unsigned char *command, unsigned int  length = 0), если нужно умолчание, хотя к чему оно здесь?
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = *command++;
  }
}

[/code]
mr_smit
Просто вроде как sizeof должна возвращать длину массива. Но тут да, точно, указатель, а не массив (я начинаю понимать sm.gif ) Может тогда убрать указатель из аргумента функции?

Просто хочу писАть: SendCommand(startCommunication); без указания длинны

Код
void SendCommand (unsigned char command) {
  unsigned int  length,length1 = 0;
  length = length1 = sizeof(command);
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = command[length1 - length];
  }
}

Но чет ругается компилятор
_Артём_
Цитата(mr_smit @ Dec 22 2011, 00:03) *
Просто вроде как sizeof должна возвращать длину массива. Но тут да, точно, указатель, а не массив (я начинаю понимать sm.gif ) Может тогда убрать указатель из аргумента функции?

Просто хочу писАть: SendCommand(startCommunication); без указания длинны

Код
void SendCommand (unsigned char command) {
  unsigned int  length,length1 = 0;
  length = length1 = sizeof(command);
  while (length--) {
    while(!(UCSRA & (1<<UDRE)));  // ждем окончания передачи байта
    UDR = command[length1 - length];
  }
}

Но чет ругается компилятор


Что ж ему не ругаться: command - это байт , а вы к нему обращаетесь как элементу массива (хамите).

Понятно Вам что нужно:
функция аналогичная например такому(C#. правда а не Си)
Код
void F1(byte []byte_array) {
    for (int i=0; i<byte_array.Length; i++) {
           byte_array[i]=i;// нужные действия
    }
}


Но в C# каждый массив всегда содержит свою длину, а в Си - нет.
Может и есть что-то такое в Си, а может и нет(утверждать не буду, не помню).
XVR
Цитата(_Артём_ @ Dec 22 2011, 03:11) *
Но в C# каждый массив всегда содержит свою длину, а в Си - нет.
Может и есть что-то такое в Си, а может и нет(утверждать не буду, не помню).
Нету такого в С (утверждать буду) biggrin.gif
mr_smit
Всем спасибо! rolleyes.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.