Учебный курс. Организация обмена по USART `у с использованием кольцевого буфера

07/01/2010 - 21:39

Вступление

   Реализация обмена данными по USART`у, которую мы рассматривали в предыдущей статье имеет некоторые недостатки. Функция отправки символа перед тем как инициировать передачу выполняет опрос флага UDRE регистра UCSRA в цикле while. А это значит, что микроконтроллер тратит свое драгоценное время на пустую работу. Прием данных осуществляется в однобайтовый буфер, при этом предыдущие данные затираются, если микроконтроллер не успевает их считывать.  
   Для простых приложений такого подхода вполне достаточно, но для сложных гораздо эффективнее организовать передачу (прием) данных используя кольцевой (циклический) буфер. Чтобы ответить на вопрос почему, давайте сначала разберемся, что из себя представляет этот буфер.

Кольцевой буфер

   Простая реализация кольцевого буфера включает в себя массив и три дополнительные переменные – указатель головы буфера, указатель хвоста буфера и счетчик символов, содержащихся в массиве.

#define SIZE_BUF 8

//кольцевой (циклический) буфер
unsigned char cycleBuf[SIZE_BUF];
unsigned char tail = 0;      //"указатель" хвоста буфера 
unsigned char head = 0;   //"указатель" головы буфера
unsigned char count = 0;  //счетчик символов

   Данные добавляются в “хвост” буфера, при этом "указатель" хвоста и счетчик символов увеличиваются на единицу. Переменная tail всегда указывает на следующую свободную ячейку буфера и может изменяться в диапазоне от 0 до SIZE_BUF – 1.
   Считываются (извлекаются) данные из “головы”, при этом индекс головы увеличивается на единицу, а счетчик символов уменьшается. Переменная head указывает на текущий считываемый символ и тоже изменяется в диапазоне от 0 до SIZE_BUF – 1.
   Буфер называется кольцевым, потому что данные в него могут добавляться с любой позиции до тех пор пока в нем есть место. Получается, что данные записываются как бы по кольцу. Посмотрите на рисунки, думаю станет понятно.
кольцевой (циклический) буфер пуст
Когда в кольцевом буфере нет данных, указатель головы равен указателю хвоста, а счетчик символов равен нулю.
в кольцевом (циклическом) буфере данные
Когда в буфер помещены данные, tail указывает на следующую пустую ячейку буфера, head на первый символ, а счетчик показывает сколько ячеек в буфере занято. При считывании данных из буфера изменяются только указатели и счетчик, содержимое массива не меняется. Данные помещаемые в буфер записываются "поверх" старых.
в кольцевом (циклическом) буфере данные
Указатели хвоста и головы могут принимать любые значения от 0 до SIZE_BUF - 1.

Для работы с буфером требуется как минимум три функции – очистить буфер, положить символ, взять символ.
   
//"очищает" буфер
void FlushBuf(void)
{
  tail = 0;
  head = 0;
  count = 0;
}

   Как вы видите, массив cycleBuf[] не очищается. Функция обнуляет "указатели" хвоста, головы и счетчик символов, но это равносильно тому, что в буфере нет данных.

//положить символ в буфер
void PutChar(unsigned char sym)
{
  if (count < SIZE_BUF){   //если в буфере еще есть место
      cycleBuf[tail] = sym;    //помещаем в него символ
      count++;                    //инкрементируем счетчик символов
      tail++;                           //и индекс хвоста буфера
      if (txBufTail == SIZE_BUF) txBufTail = 0;
    }
}

//взять символ из буфера

unsigned char GetChar(void)
{
   unsigned char sym = 0;
   if (count > 0){                            //если буфер не пустой
      sym = cycleBuf[head];              //считываем символ из буфера
      count--;                                   //уменьшаем счетчик символов
      head++;                                  //инкрементируем индекс головы буфера
      if (head == SIZE_BUF) head = 0;
   }
   return sym;
}

Как можно использовать кольцевой буфер

   Как можно использовать кольцевой буфер для передачи  данных по USART `у? Очень просто. Допустим нам нужно послать по USART `у строку. Первый символ строки мы записываем в регистр UDR, а остальные кидаем в буфер. По завершению передачи модуль USART вызовет прерывание (естественно оно должно быть разрешено) и в прерывании возьмет из буфера следующий символ. В следующем прерывании еще, потом еще и так пока не опустошит весь буфер. Данные помещаются в буфер намного быстрее, чем осуществляется их передача и микроконтроллер не тратит свое драгоценное время на опрос флага готовности модуля USART.  
   А что делать с приемом? То же самое. В прерывании модуля USART (по завершению приема) записываем данные в другой кольцевой буфер, в основной программе обрабатываем. Конечно и здесь возможны потери данных, если буфер переполнится, но в этом случае мы можем просто увеличить размер приемного буфера.

Обмен по USART/UART с использованием кольцевого буфера

За основу я взял предыдущий проект и существенно его переработал.
Добавил в функцию инициализации USART `а разрешение прерывания по завершению передачи. За это отвечает бит TXCIE регистра UCSRB.

Инициализация usart `a

void USART_Init(void)
{
  UBRRH = 0;
  UBRRL = 51; //скорость обмена 9600 бод

  //разр. прерыв при приеме и передачи, разр приема, разр передачи.
  UCSRB = (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN);  

  //размер слова 8 разрядов
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}

  Объявил переменные для приемного и передающего кольцевого буфера и описал все функции для работы с ними. Рассмотрим только "передающую часть".

Передающий кольцевой буфер

unsigned char usartTxBuf[SIZE_BUF];
unsigned char txBufTail = 0;
unsigned char txBufHead = 0;
unsigned char txCount = 0;

Возможно не лишним будет добавить перед объявлением массива usartTxBuf[] и переменной txCount ключевое слово volatile, поскольку эти переменные используются и в основной программе и в прерываниях. Но проект работал без нареканий и я оставил так.

Функция очистки передающего буфера

void USART_FlushTxBuf(void)
{
  txBufTail = 0;
  txCount = 0;
  txBufHead = 0;
}
   В этой функции есть один нюанс! Переменная txCount специально обнуляется раньше txBufHead. Если  поменять их местами, могут возникнуть проблемы. Например, происходит передача данных, переменная txBufHead указывает на 4 элемент массива и в основном цикле программы мы запускаем эту функцию. Она обнуляет две переменные txBufTail и txBufHead и происходит прерывание по завершению передачи. Поскольку txCount еще не равна нулю, микроконтроллер будет извлекать данные из буфера. Но не из 4 ячейки массива, а из 0!
   По-хорошему это лечиться запрещением прерываний на время выполнения функции, например, с помощью ключевого слова __monitor

Функция загрузки символа в буфер

void USART_PutChar(unsigned char sym)
{
  if(((UCSRA & (1<<UDRE)) != 0) && (txCount == 0)) UDR = sym;
  else {
    if (txCount < SIZE_BUF){                    //если в буфере еще есть место
      usartTxBuf[txBufTail] = sym;             //помещаем в него символ
      txCount++;                                     //инкрементируем счетчик символов
      txBufTail++;                                    //и индекс хвоста буфера
      if (txBufTail == SIZE_BUF) txBufTail = 0;
    }
  }
}

   Если модуль USART не занят и передающий буфер пустой, символ записывается в регистр UDR. Это инициирует передачу данных и вызовет прерывание, когда модуль освободится.  В данной реализации символ будет помещаться в буфер, только если в нем есть свободное место.

Функция загрузки строки в буфер

void USART_SendStr(unsigned char * data)
{
  unsigned char sym;
  while(*data){
    sym = *data++;
    USART_PutChar(sym);
  }
}

  С подобной функцией мы уже имели дело, когда разбирались с lcd дисплеем. Функции передается указатель на строку и она посимвольно помещается в буфер с помощью функции USART_PutChar(sym). Цикл while выполняется пока не будет считан символ конца строки.

Обработчик прерывания по завершению передачи

#pragma vector=USART_TXC_vect
__interrupt void usart_txc_my(void)
{
  if (txCount > 0){                              //если буфер не пустой
    UDR = usartTxBuf[txBufHead];         //записываем в UDR символ из буфера
    txCount--;                                      //уменьшаем счетчик символов
    txBufHead++;                                 //инкрементируем индекс головы буфера
    if (txBufHead == SIZE_BUF) txBufHead = 0;
  }
}

Пока буфер содержит данные - обработчик прерывания будет считывать их из буфера и передавать по USART `у.

“Передающая часть” аналогична, только в прерывании данные принимаются, а для их чтения используется функция unsigned char USART_GetChar().
Полную версию программного модуля можно посмотреть по ссылкам ниже

usart.h
usart.c

Основную программу не привожу, она очень простая. Скачивайте нужный проект, смотрите, разбирайтесь, моделируйте в Proteus.

Размышления

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

Файлы

Комментарии   

# alexandershahbazov 09.01.2010 22:02
Возьмем на заметку !

Я в основном на прием делал так :

unsigned char pars;
while(rx_counter)
{
pars=getchar();putchar(pars);
'Здесь код порой достаточно приличный
};

rx_counter , getchar , putchar
образованы через CodeWizardAVR ( CodeVisionAVR )
Ответить | Ответить с цитатой | Цитировать
# Pashgan 10.01.2010 15:27
Любой способ хорош, если он работает.. кольцевой буфер можно еще сделать на структурах (struct)
Ответить | Ответить с цитатой | Цитировать
# Guest 18.01.2010 14:22
подскажите полному, чайнику(, как мне через UART заставить светить светодиоды?
То есть подаю один сигнал-горит красный, подаю другой сигнал-зеленый. ЗЫ Железо собрано, светики зажигать уже умею, спасибо!
Ответить | Ответить с цитатой | Цитировать
# gringo 18.01.2010 16:46
считывать данные из UDR в какую-нибудь переменную, затем сравнивать ее с константами... например так
if (data == 'g'){
OnLedGreen();OffLedRed();
}
if (data == 'r'){
OnLedRed();OffLedGreen();
}
или с помощью оператора switch
Ответить | Ответить с цитатой | Цитировать
# Guest 04.02.2010 11:19
Есть простор для оптимизации:
1)Можно обойтись без счетчика. Достаточно сравнивать индексы головы и хвоста. Неравенство означает наличие данных в буфере. Вам когда-нибудь нужно было знать, сколько конкретно данных в буфере? Мне нет. Достаточно просто знать - есть данные или нет.
2) Если поставить условие, что размер буфера должен быть степенью двойки, то txBufTail++;
if (txBufTail == SIZE_BUF) txBufTail = 0;
превращается в более эффективное
txBufTail = (txBufTail + 1) & (SIZE_BUF - 1));
В этом случае легко считать и количество данных в буфере (если понадобится): txCount = (txBufTail - txBufHead) & (SIZE_BUF - 1);

А пренебрегать volatile ни в коем случае не стоит.
Ответить | Ответить с цитатой | Цитировать
# DigRo 28.03.2013 09:39
Цитирую Guest:

2) Если поставить условие, что размер буфера должен быть степенью двойки, то txBufTail++;
if (txBufTail == SIZE_BUF) txBufTail = 0;
превращается в более эффективное
txBufTail = (txBufTail + 1) & (SIZE_BUF - 1));

Поясните пожалуйста: допустим txBufTail = 2 (0010), а
SIZE_BUF = 10 (1010)
Получается, что следующее значение
txBufTail = (0011)&(1001)=(0001)
13 Что то я не понимаю...
Ответить | Ответить с цитатой | Цитировать
# DigRo 28.03.2013 09:42
Цитирую Guest:

должен быть степенью двойки

Извините, заработался.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 28.03.2013 18:24
Бывает :-)
Ответить | Ответить с цитатой | Цитировать
# Guest 04.02.2010 11:36
И еще - зачем использовать прерывание TXC, да еще и с проверкой флага UDR в USART_PutChar() ? Неаккуратненько...
Вот:
#define TX_BUFF_SIZE 8 // must be power of 2
static volatile char TxBuffer[TX_BUFF_SIZE];
static volatile uint8_t TxHead, TxTail;

#pragma vector = USART_UDRE_vect
__interrupt void Tx232Handler(vo id)
{
uint8_t Tmp = TxTail; // use local variable instead of volatile
UDR = TxBuffer[Tmp & (TX_BUFF_SIZE - 1)];
++Tmp;
TxTail = Tmp;
if(Tmp == TxHead) // all transmitted
{
UCSRB &= ~(1
Ответить | Ответить с цитатой | Цитировать
# Guest 04.02.2010 11:39
блин... сдвиги обрезало. заменил сдвиг влево на shl:
__interrupt void Tx232Handler(vo id)
{
uint8_t Tmp = TxTail; // use local variable instead of volatile
UDR = TxBuffer[Tmp & (TX_BUFF_SIZE - 1)];
++Tmp;
TxTail = Tmp;
if(Tmp == TxHead) // all transmitted
{
UCSRB &= ~(1 shl UDRIE); // disable this int
}
}

void putchar_hard (char Byte)
{
uint8_t Tmp = TxHead;
while((uint8_t) (Tmp - TxTail) >= (uint8_t) TX_BUFF_SIZE)
; // buffer full, wait until symbol transmitted in interrupt
TxBuffer[ Tmp & (TX_BUFF_SIZE - 1) ] = Byte;
++Tmp;
__disable_inter rupt();
TxHead = Tmp;
UCSRB |= (1 shl UDRIE);
__enable_interrupt();
}
Ответить | Ответить с цитатой | Цитировать
# Канопус 20.06.2016 07:12
Пример хорош всем кроме одного - проверка количества элемнтов в проверке наличия места сделана не корректно. Это грубая ошибка
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.02.2010 13:13
Оптимизация делает код менее очевидным для начинающего, поэтому иногда ей приходится пренебрегать.
1)Можно обойтись, но со счетчиком код будет короче. Особенно если кольцевой буфер на структурах.
2)Согласен, эффективнее. Возьму на заметку. ;-)
Ответить | Ответить с цитатой | Цитировать
# Guest 12.02.2010 21:43
Как я понимаю, если отправился последний нужный нам символ, возникнет прерывание, но помещение в регистр символа не будет, а выполнится только проверка. Видимо это будет небольшим недостатком, так как получается что-то вроде "пустого" прерывания...
Ответить | Ответить с цитатой | Цитировать
# Pashgan 13.02.2010 10:28
да, последнее прерывание будет "пустое"
Ответить | Ответить с цитатой | Цитировать
# Guest 15.02.2010 13:15
Так по хорошему надо проверку делать на отсылку данных - все или не все
Ответить | Ответить с цитатой | Цитировать
# Silverio 16.02.2010 18:56
Если не тяжело растолкуйте принцип вычисления адреса в массиве:
TxBuffer[ Tmp & (TX_BUFF_SIZE - 1) ]
Ответить | Ответить с цитатой | Цитировать
# gringo 17.02.2010 06:23
Дело не в вычислении адреса, а в подходе к реализации счетчика.

TxBuffer[ Tmp & (TX_BUFF_SIZE - 1) ] = Byte;
++Tmp;

Переменная Tmp инкрементируетс я и на нее накладывается битовая маска. Маска обрезает лишние разряды. Таким образом проверку if производить не нужно.

TxBuffer[Tmp] = Byte;
++Tmp;
if (Tmp == TX_BUFF_SIZE) Tmp = 0;
Ответить | Ответить с цитатой | Цитировать
# Pyku_He_oTTyda 16.05.2010 18:58
USART_GetChar();
для меня непонятно работает.
Считываем массив, а возвращаем символ... Растолкуйте мне, как она работает.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 16.05.2010 20:51
Если приемный буфер не пустой, считываем из него символ, используя в качестве индекса "указатель головы". Счетчик символов при этом уменьшаем на 1. А "указатель головы" увеличиваем на 1.
Ответить | Ответить с цитатой | Цитировать
# Pyku_He_oTTyda 17.05.2010 03:02
Да, действительно так. Я заработался и перепутал if и while.
Поэтому м посчитал, что выход из функции происходит только когда прочтется весь буфер и соответственно вернется только последнее значение.
Ответить | Ответить с цитатой | Цитировать
# foxit 07.07.2010 21:00
А проект для протеуса есть
Ответить | Ответить с цитатой | Цитировать
# Pashgan 05.08.2010 00:22
Проект для Proteus`a такой же как и здесь - http://chipenable.ru/index.php/programming-c/43-avr-usart-uart.html
Ответить | Ответить с цитатой | Цитировать
# Guest 03.08.2010 17:43
PutChar - в широких массах считается передать аcки символ, например

char ch = 'a';
PutChar(ch);

Для передачи строки строят функцию
SendSrting(char *s) в качестве аргумента которой передают указатель на строку или буфер. В теле этой функции последовательно вызывается примитивная передать символ, столько раз, сколько нужно. Было бы неплохо в коде 1)или поправить названия функций 2)или переписать их содержание, т.к. в функции передать символ мы фактически передаем строку (буфер), что вносит изрядную сумятицу в неокрепшие еще умы. Некорректно это методологически .
Ответить | Ответить с цитатой | Цитировать
# Pashgan 05.08.2010 00:28
В функции передать символ мы передаем только символ. В моем неокрепшем уме чего то никакой сумятицы нет.
Ответить | Ответить с цитатой | Цитировать
# Guest 21.08.2010 13:23
Решил для себя сделать либу, и возник вопросец:
А имеем ли мы право в примере от SAB в функции putchar делать так:
while((uint8_t) (Tmp - TxTail) >= (uint8_t) TX_BUFF_SIZE);
формально у нас может случиться перенос, и Head станет меньше Tail в нормальном состоянии, и программа повиснет. Похоже, что без счетчика байт в буфере не обойтись :( Поправьте меня если что не так.
Ответить | Ответить с цитатой | Цитировать
# Ant 25.10.2010 12:01
подскажите пожалуйста, открыл ваш проект, при компиляции пишет, что имена битов не определены. Как исправить?
Error[Pe020]: identifier "RXCIE" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 34
Error[Pe020]: identifier "TXCIE" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 34
Error[Pe020]: identifier "RXEN" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 34
Error[Pe020]: identifier "TXEN" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 34
Error[Pe020]: identifier "URSEL" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 35
Error[Pe020]: identifier "UCSZ1" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 35
Error[Pe020]: identifier "UCSZ0" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 35
Error[Pe020]: identifier "UDRE" is undefined D:\IAR_opit\USA RT2-IAR\USART2\ usart.c 58
Ответить | Ответить с цитатой | Цитировать
# Ant 25.10.2010 12:41
это происходит в режиме Release, в Debug все нормально.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 31.10.2010 20:46
Project - Options - General Options - вкладка System - внизу поставить галочку Enable bit definition .. и эти ошибки исчезнут
Ответить | Ответить с цитатой | Цитировать
# AJ26 19.11.2010 17:58
Доброго времени суток! возникла такая задача: построить клавиатуру по распределенной схеме. 1ый МК (atmega16) совершает опрос клавиатуры (сканирование) и передает код нажатой кнопки по uart. планируется передача 1-3 байт за сеанс передачи. (один байт описывает состояние ctrl, alt, shift, второй содержит номер нажатой клавиши, третий - номер второй нажатой клавиши, если нажато 2 одновременно ) Постоянно пересылать состояние всех клавиш счел нецелесообразны м. Второй МК (pic18f2550) принимает данные по уарт и передает в компьютер по usb, определяясь как стандартное hid устройство. Реализация usb клавиатуры подошла от фреймворка microchip, с этим нет вопросов - работает стабильно. На стороне и принимающего, и передающего МК будет создан циклический буфер.

ПОСТАНОВКА ВОПРОСА: как лучше реализовать обмен по UART - с прерываниями или без ( чтобы не было конфликта прерываний usb и uart ) Больше всего интересует со стороны принимающего мк. Как это вижу я. в цикле ждем завершения очередного приема, проверяя флаг. По завершении приема записываем полученное значение в регистр usb. usb сам с нужной ему частотой инициирует прерывание и передачу данных в ПК. Какие рекомендации и советы можете дать?
Ответить | Ответить с цитатой | Цитировать
# vovka11 29.11.2010 19:29
помогите в проекте под iar компилятор выдал ошибку
Error[Pe020]: identifier "SIZE_BUF" is undefined
немогу понять почему, вроде проект скопировал правильно проверял
возможно настроил неправельно.
Ответить | Ответить с цитатой | Цитировать
# Anton_ 08.02.2011 08:35
У меня ворзникает ситуация, когда txCount = SIZE_BUF_UART и контроллер не входит в прерывания передачи символа. Как побороть данную ситуацию?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 08.02.2011 20:20
Увеличить SIZE_BUF_UART.
Увеличить скорость обмена.
Ответить | Ответить с цитатой | Цитировать
# R_ura 13.03.2011 20:36
Не совсем понятно назначение функций
Цитата:
//возвращает колличество символов находящихся в приемном буфере
unsigned char USART_GetRxCount(void)
{ return rxCount; }
Цитата:
//"очищает" приемный буфер
void USART_FlushRxBuf(void)
{ unsigned char saveSreg = SREG;
cli();
rxBufTail = 0;
rxBufHead = 0;
rxCount = 0;
SREG = saveSreg; }
Предполагаю, что первой функцией в основном цикле получаем текущее значение
rxCount и если оно равно 0, то второй функцией очищаем буфер?? Зачем его очищать, если новые данные пишутся поверх старых?
Поясните, пож..
Ответить | Ответить с цитатой | Цитировать
# R_ura 13.03.2011 21:03
Пардон, не внимательно прочел про очистку буфера. Очищаются не данные в буфере, а указатели и счетчик.
Тем не менее - в вашем примере для WINAvr не увидел использования вышеуказанных функций. Где они используются?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 15.03.2011 19:50
Да, в проектах они не используются. Где используется? Например, когда нужно очистить буфер по команде.
Ответить | Ответить с цитатой | Цитировать
# ciscobsci 29.03.2011 17:26
Вопрос, а как правильно прочитать при приеме RX не 1 символ, а строку из USART ? Желательно пример в коде..
Ответить | Ответить с цитатой | Цитировать
# v3launch 04.06.2013 12:51
таже проблема, пример WINAVR не читает всю посылку, а только 1 символ. Не понимаю, как прочитать остальные символы из usartRxBuf[].
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.06.2013 18:24
Эта функция не реализована. Напиши функцию последовательно го чтения буфера до первого нулевого символа.
Ответить | Ответить с цитатой | Цитировать
# Dmi3 29.04.2011 11:02
Размышления в конце статьи не верны.
Код содержит не меньше одной ошибки.
Нет гарантии атомарности изменения переменых.
Например: ++Count может быть откомпилировано так:
  • прочесть Count в регистр. LDS r16,Count
    увеличить регистр. INC r16
    записать регистр в Count. STS Count,r16

Если между чтением и записью (между LDS и STS) произойдёт прерывание то любые изменения Count в этом прерывании будут потеряны.
Ответить | Ответить с цитатой | Цитировать
# Menudo 23.08.2011 16:13
Цитата:
//возвращает колличество символов находящихся в приемном буфере unsigned char USART_GetRxCoun t(void) { return rxCount; }
Эта функция нужна когда нужно прочитать из массива больше 1 символа за раз. Нужно читать пока функция не стенет выдавать 0.
Ответить | Ответить с цитатой | Цитировать
# Юра 09.10.2011 19:09
Разобрался вроде бы в общих чертах с Юартом. Но возникло несколько вопросов.
1. Если передаваемые команды состоят не из одного байта, а из строки - "imp_X", "imp_Y" и т.д. то как их идентифицироват ь в приемном буфере, если он содержит уже десяток байт?
2. Если произойдет искажение передаваемой команды, можно отправить на передающую сторону об этом сообщение. Но ведь там передающий буфер уже вполне может быть заполнен другими данными, к тому же уже успеют измениться и значения головы, хвоста и счетчика передающего буфера. Т.е. практически непрошедшую команду повторить уже не выйдет?
Ответить | Ответить с цитатой | Цитировать
# дмитриус 29.10.2011 07:27
что-то мне кажется что лучче сдвигать адреса процедур в очереди, чем хранить кол. процедур, хвост и конец. Просто у вас нигде не описано что после записи/чтения символа в последнюю ячейку массива, следующий надо писать/читать в/из первой(нулевой) ячейки. т.е проблема при переходе из конца в начало массива никак не описано.
Ответить | Ответить с цитатой | Цитировать
# дмитриус 29.10.2011 07:28
ну в вашем случае процедуры - это символы. суть та же
Ответить | Ответить с цитатой | Цитировать
# дмитриус 29.10.2011 07:31
txCount++; //инкрементируе м счетчик символов
txBufTail++;- а из-за такой бодяги вы просто вылазите за пределы массива. компилятору и МК конечто же все равно))) МК просто инкрементирует счетчик адресов
Ответить | Ответить с цитатой | Цитировать
# Pashgan 23.11.2011 22:36
Никто никуда не вылезает. Прогоните код в симуляторе и убедитесь в этом. Или покажите мне ситуацию в которой это происходит. Конкретную ситуацию.
Ответить | Ответить с цитатой | Цитировать
# -devil- 29.10.2011 07:39
ну вы б внимательно хотя бы читали, чтоль. там все учтено!!!
зачем по вашему там
if (txBufTail == SIZE_BUF) txBufTail = 0;
?
Ответить | Ответить с цитатой | Цитировать
# ~cosinus~ 01.11.2011 14:10
Цитирую -devil-:
ну вы б внимательно хотя бы читали, чтоль. там все учтено!!!
зачем по вашему там
if (txBufTail == SIZE_BUF) txBufTail = 0;
?


Я бы не согласился с Вами.
Пусть адрес начала буфера (Start_BUF) в памяти равен 25h. Размер буфера 0Ah-байт
Тогда, "хвост" и "голова" выставляются на начало буфера, а именно становятся равны 25h.
Вот тут как раз и получится что будет выход за границы буфера. И "хвост"\"голова " никогда не перейдёт с конца массива на его начало.
Следовательно надо "хвост"\"голову " сравнивать с (SIZE_BUF + Start_BUF+1h).
Другими словами проверить выход за границы.
Ответить | Ответить с цитатой | Цитировать
# Чаба 29.10.2011 07:43
и таки -devil- прав
Ответить | Ответить с цитатой | Цитировать
# ~cosinus~ 01.11.2011 14:13
Так что дмитриус верно подметил Цитата:
. Просто у вас нигде не описано что после записи/чтения символа в последнюю ячейку массива, следующий надо писать/читать в/из первой(нулевой) ячейки. т.е проблема при переходе из конца в начало массива никак не описано.
Ответить | Ответить с цитатой | Цитировать
# Дмитрий А. 03.11.2011 09:14
Посмотрите на эту функцию Код: void USART_PutChar(unsigned char sym)

По первому условию txCount == 0 символ запихивается в UDR, но где txCount становится равным единице? То есть в тело второго условия этой функции программа никогда не попадает и буфер всё время остаётся пустым...
Ответить | Ответить с цитатой | Цитировать
# Pashgan 23.11.2011 21:43
Зачем увеличивать счетчик, если первый символ кладется в буфер USART модуля,а не в массив? Счетчик txCount считает символы находящиеся в кольцевом буфере!
В тело второго условия мы попадем, когда сбросится флаг UDRE. А сбрасывается он при записи в регистр данных (UDR).
Ответить | Ответить с цитатой | Цитировать
# дмитриус 15.11.2011 07:03
но все равно статья хорошая. главное - идея, а остальное умному несложно допилить)))
Ответить | Ответить с цитатой | Цитировать
# MacMaffy 16.11.2011 05:14
Привет, вопрос:
Я не много запутался, прием данных идёт по прерывания?
Почему такой вопрос, собираю робота на РУ по уарт+автономная работа, я хотел бы чтобы по прерыванию приходил сигнал по уарт и сражу же обрабатывался, на роботе стоят ещё пару датчиков, когда сигнала по уарт нет, они работают
Ответить | Ответить с цитатой | Цитировать
# Oxidizer 21.08.2012 23:04
Отличная статья! Большое спасибо! Очень помагло.
Ответить | Ответить с цитатой | Цитировать
# Neitron 22.08.2012 17:29
Подскажите, а что произойдет если переполнится приемный кольцевой буфер?
Ответить | Ответить с цитатой | Цитировать
# _Gringo_ 23.08.2012 20:55
Когда буфер заполнится, туда нельзя будет добавить данные. Соответственно функция USART_PutChar() будет работать в холостую. Можно добавить в нее возврат ошибки в этом случае или ожидание готовности - какой нибудь цикл while. Но это чревато зависоном.
Ответить | Ответить с цитатой | Цитировать
# ir0407 23.11.2012 03:29
1)Функция отправки строки не универсальна.
Вот как отработает функция отправки строки если ей на входе подсунуть указатель на строку во флеше или же на строку лежащую в раме, которую сформировали динамически.
2)Какое может быть решение если строка не влазит в буфер, а увеличивать буфер некуда из-за ограничений по размеру рамы в некоторых камешках. :-) Замену камешка не предлагать... :-*
Ответить | Ответить с цитатой | Цитировать
# SeNiMal 06.12.2012 09:28
Подскажите, чем аргументируется использование именно кольцевого буфера, а не обычного? Короче код? Или еще почему-то?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 09.12.2012 19:23
Просто такая структура данных позволяет асинхронно буферизировать и обрабатывать потоковые данные. Пишем в одно время, а обрабатываем в другое. Обработка и наполнение буфера может происходить при любом его наполнении, при наличии свободного места. Линейный буфер, как правило используют для потоковых данных фиксированной длинны.
Ответить | Ответить с цитатой | Цитировать
# SeNiMal 06.12.2012 11:45
Еще вопрос:
Если буфер полон, то указатели головы и хвоста тоже совпадут. Как определить, полон он или пуст? Если допустить, что при совпадении головы и хвоста - переполнение, то одна ячейка кольцевого буфера не будет использоваться. Как разрешить это противоречие?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 09.12.2012 19:25
Добавить в функции PutChar и GetChar возврат какого-нибудь значения.
Ответить | Ответить с цитатой | Цитировать
# AFZ 27.02.2013 07:56
У меня траблы с отправкой строки в основной программе, выдает вот такую ошибку: function argument #1 of type 'unsigned char [10]' is incompatible with required parameter of type 'unsigned char'. Программу скачал с этой страницы, перепроверил, мб я где накосячил, но увы, всё точно так же, как и в статье. Упорно не хочет компилировать проект в CodeVisionAVR 2.03.4, ругается на отправляемую строку USART_SendStr(" test - OK"); Как это можно исправить?
Ответить | Ответить с цитатой | Цитировать
# JoJo 27.02.2013 09:00
Сделай тему на форуме и выложи там код. Так непонятно в чем может быть ошибка.
Ответить | Ответить с цитатой | Цитировать
# AFZ 27.02.2013 10:33
Зачем создавать тему?
Дело в том, что код скачан отсюда для Code Vision AVR. Всё тоже самое, только инициализацию контроллера вынес в отдельный файл, но это никак не связано с ошибкой. Вообще в версии 2.03.4 не хочет работать с указателями, мб кто-то сталкивался с подобным?
Ответить | Ответить с цитатой | Цитировать
# AFZ 27.02.2013 10:46
Функция загрузки строки в буфер

void USART_SendStr(u nsigned char * data)//ошибка тут, на тип указателя ругается...
{
unsigned char sym;
while(*data){
sym = *data++;
USART_PutChar(s ym);
}
}
Ответить | Ответить с цитатой | Цитировать
# Pashgan 27.02.2013 10:49
Я пользуюсь CodeVision 2.04. Только что скачал код с этой страницы и запустил компиляцию. Проект скомпилировался .
Ответить | Ответить с цитатой | Цитировать
# zoomerland 11.05.2013 12:02
Уважаемый автор! По какой то причине USART_SendStr не работает как надо.
Если просто вызвать USART_SendStr(' test'); то вообще ничего не происходит, а если
char с='AT+CFUN?\r';
USART_SendStr(&с);

то посылает только последний символ строки.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.06.2013 18:22
Установлена не та скорость обмена.
Выложи здесь свой проект, я посмотрю.
http://chipenable.ru/index.php/forum/materialy-sajta-chipenable/3245-vetka-dlya-vremennogo-khraneniya-proektov.html
У меня вот этот код работает.
Код:
char c[] = "AT+CFUN?\r";
USART_SendStr(c);
Ответить | Ответить с цитатой | Цитировать
# Extremist 05.06.2013 16:36
zoomerland
чувак, char - это один символ, а не строка. а строку надо заключать вообщето в двойные кавычки. по идеее нормальный компилятор должен выкинуть ошибку или варнинг хотя бы на этапе предварительной компиляции
Ответить | Ответить с цитатой | Цитировать
# kleef 21.07.2013 06:39
Мне вот что непонятно, в обработчике прерывания по завершению передачи в случае, если у нас передаётся больше одного символа каким образом мы вновь вызываем PutChar? В функции SendStr PutChar вызывается явно если у нас это первый символ, но затем в прерывании идет присваивание UDR второго третьего и т д символов, но каким образом простое присваивание значения UDR вызывает вновь функцию PutChar для осуществления передачи?
Ответить | Ответить с цитатой | Цитировать
# NaDmitrAn 21.10.2013 16:11
Вот такая ситуация, мне надо отправить в порт массив рассчитанных данных, все бы ничего, но вот с окончанием загрузки в буфер проблема, у меня нет данные стоят в ячейках 6F,70, а кольцевой буфер начинается с ячейки 71, тоесть нет нулевого значения, которое программа воспримет как конец строки, и если размер буфера будет хотя-бы 4, то после загрузки второго символа в буфер, программа начнет в следующую ячейку буфера закладывать уже не данные которые закончились, а начало буфера. С текстом-то все просто берешь, ставиш пробел в конце и все работает, как в данных прилепить ячейку с "0" в конце.
Ответить | Ответить с цитатой | Цитировать
# Zlodey 19.02.2015 13:16
Обнаружил ошибку в статье.
Вот здесь

Код://положить символ в буфер
void PutChar(unsigned char sym)
{
if (count < SIZE_BUF){ //если в буфере еще есть место
cycleBuf[tail] = sym; //помещаем в него символ
count++; //инкрементируем счетчик символов
tail++; //и индекс хвоста буфера
if (txBufTail == SIZE_BUF) txBufTail = 0;
}
}


используется одна и та же переменная, названная "tail" и "txBufTail" в разных местах кода
Ответить | Ответить с цитатой | Цитировать
# lenivec 17.09.2015 10:06
Только у меня работает кудряво при отправке буфера длиной более 27 байт? При отправке откуда-то берутся магические нули в конце посылки.
Ответить | Ответить с цитатой | Цитировать
# lenivec 18.09.2015 11:01
Разобрался, проблема в атомарности, volatile вылечил
Ответить | Ответить с цитатой | Цитировать
# Zlodey 09.01.2017 13:22
Заметил грубую ошибку в коде. Если по какой-то причине не забрать байты из буфера, и буфер переполнится, то мы навсегда зависнем в обработчике прерывания USART_RXNE.

Код:
//прерывание по завершению приема
if (rxCount < SIZE_BUF)
{
//если в буфере еще есть место
usartRxBuf[rxBufTail] = UDR;
//считать символ из UDR в буфер
rxBufTail++;
//увеличить индекс хвоста приемного буфера
if (rxBufTail == SIZE_BUF) rxBufTail = 0;
rxCount++;
//увеличить счетчик принятых символов
}
else
{
// если в буфере больше нет места
// мы вынуждены прочитать UDR, иначе повиснем здесь
u8 free = UDR;
}
Ответить | Ответить с цитатой | Цитировать
# Zlodey 09.01.2017 14:24
Или лучше сделать вот так

Код:
//прерывание по завершению приема
u8 RxData = UDR;
//если в буфере еще есть место
if (rxCount < SIZE_BUF)
{
//считать символ из UDR в буфер
usartRxBuf[rxBufTail] = RxData;
//увеличить индекс хвоста приемного буфера
rxBufTail++;
if (rxBufTail == SIZE_BUF) rxBufTail = 0;
//увеличить счетчик принятых символов
rxCount++;
}
Ответить | Ответить с цитатой | Цитировать
# max-1 14.01.2017 21:56
помогите!!! не могу отправить строку по usart`у из камня, вместо строки отправляется только первый символ. РАЗЖУЙТЕ ДЛЯ НАЧИНАЮЩЕГО.
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий

Защитный код
Обновить