Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: очередь данных для вывода через USART
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
skopus
USART настроен на скорость 115200.

Проблема заключается в следующем :

Необходимо за 100 тактов таймера частотой Fosc/8 = 7.373/8 Mhz = 921.5 Khz выести через USART минимум 10 байт данных. Лучше даже 15.
Есть следующая функция, написанная на IAR C 3.11 :
Код
void MA_PutCharCh0_USART( unsigned char Data )
{
    /*--- Wait for USART data register empty ---*/
    while( !(UCSR0A & __BIT_MASK( UDRE0 ) ) )
    {
        /*--- Do nothing ---*/
       ;
    }
    /*--- Send a character ---*/
    UDR0 = Data;
} /* MA_PutCharCh0_USART */

Очень много теряется на ожидание - я успеваю в этом случае выводить только 2 байта данных.

Я пробовал организовать очередь вывода через линейный однонаправленный список - но здесь нет шансов. Функция malloc(..) выполняется очень долго.

Думаю что алгоритм должен быть таким :

1. Занести данные в очередь вывода
2. Если не установлен UDRE0, выйти из процедуры, иначе занести данные в UDR0 и удалить их из буффера
Все это нужно повторять как-то циклически, причем параллельно с программой прерывания...

Может можно организовать очередь вывода на ASMе без использования динамической памяти?
И как тогда запараллелить с прерыванием вывод данных из очереди в USART ?

Уже второй день не могу придумать как это реализовать в IAR C.
Сейчас MA_PutCharCh0_USART() вызывается в теле прерывания всего два раза sad.gif
defunct
Пользуйтесь прерываниями.

Данные складывайте в Fifo.
По прерыванию Tx переходите к отправке сл. байта.


вот заготовка:

Код
#include <io.h>
#include "com_routines.h"
#ifndef UDR
#define UDR UDR0
#endif
#define QUEUE_LENGTH (32)

char COMQueue[QUEUE_LENGTH];
unsigned char QueueHead, QueueTail;
unsigned char QueueBusy = 0;


void InitCOMQueue()
{
   QueueHead = 0;
   QueueTail = 0;
}


// Вызывать эту функцию в основном цикле программы
void CheckCOMQueue()
{
    if (!QueueBusy)
    if (QueueHead != QueueTail)
    {
      QueueBusy = 1;
      UDR = COMQueue[QueueHead];
      QueueHead++;
      if (QueueHead > QUEUE_LENGTH)
         QueueHead = 0;
    }
}

// А эту функцию в обработчике прерывания UART_TX
void CharSent()
{
  QueueBusy = 0;
}


void SendByte(unsigned char data)
{
  COMQueue[QueueTail] = data;
  QueueTail++;
  if (QueueTail > QUEUE_LENGTH)
     QueueTail = 0;
}
KRS
Лучше всего циклический буфер на двух указателях (примерно как еще в древних BIOS клавиатурных)
Для простоты кода отправлять данные имеет смысл только в прерывании, а после укладки байтов в очередь прерывание разрешать, а в прерывании если байтов для отправки нет запрещать их.

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

Код
char UartBuf[64];
volatile char UartPut=0;
volatile char UartGet=0;

//класть байты в очередь
char Tmp;
Tmp=UartPut;
UartBuf[Tmp++]=.....
Tmp&=63;
UartBuf[Tmp++]=.....
Tmp&=63;
UartPut=Tmp; //сохраняем указатель
UCSRB|=(1<<UDRIE); //разрешаем прерывание не важно было оно разрешено или нет



// а обработчик прерывания

char Tmp;
Tmp=UartGet;
if (Tmp==UartPut) {
  UCSRB&=~(1<<UDRIE); //запрещаем  прерывание байтов на отправку нет
} else {
  UDR=UartBuf[Tmp++];
  Tmp&=63;
  UartGet=Tmp; //сохраняем указатель
}
Old1
На эту тему есть appnote AVR306 c примерами.
AVR306.pdf
AVR306.zip
Обратите внимание на файл UART2.c (в архиве avr306.zip).
defunct
Цитата(Old1 @ Mar 1 2006, 10:13) *
Обратите внимание на файл UART2.c (в архиве avr306.zip).

Сильно подвязан к платформе. Код, который я привел с легкостью переносится на любой МК.
skopus
Цитата(defunct @ Mar 1 2006, 10:47) *
Пользуйтесь прерываниями.

Данные складывайте в Fifo.
По прерыванию Tx переходите к отправке сл. байта.


вот заготовка:

Код
#include <io.h>
#include "com_routines.h"
#ifndef UDR
#define UDR UDR0
#endif
#define QUEUE_LENGTH (32)

char COMQueue[QUEUE_LENGTH];
unsigned char QueueHead, QueueTail;
unsigned char QueueBusy = 0;


void InitCOMQueue()
{
   QueueHead = 0;
   QueueTail = 0;
}


// Вызывать эту функцию в основном цикле программы
void CheckCOMQueue()
{
    if (!QueueBusy)
    if (QueueHead != QueueTail)
    {
      QueueBusy = 1;
      UDR = COMQueue[QueueHead];
      QueueHead++;
      if (QueueHead > QUEUE_LENGTH)
         QueueHead = 0;
    }
}

// А эту функцию в обработчике прерывания UART_TX
void CharSent()
{
  QueueBusy = 0;
}


void SendByte(unsigned char data)
{
  COMQueue[QueueTail] = data;
  QueueTail++;
  if (QueueTail > QUEUE_LENGTH)
     QueueTail = 0;
}


в смысле в основном цикле?
это сделать
Код
while(1)
{
  CheckCOMQueue();
}

а в моем прерывании просто использовать SendByte() столько раз, сколько хочу отправить байт?
defunct
Цитата(skopus @ Mar 1 2006, 10:24) *
в смысле в основном цикле?

Основной цикл - этот тот что в main() крутится..
Цитата
это сделать
Код
while(1)
{
  CheckCOMQueue();
}

а в моем прерывании просто использовать SendByte() столько раз, сколько хочу отправить байт?

Да, но в разумных пределах! длина очереди определеяется константой QUEUE_LENGTH
viakon
Ты пытаешься за 108мкс передать 10 байтов длиной 87 мкс, вот у тебя 2 байта максимум и получается. Увеличивай скорость уарта или временной интервал
arttab
Ага: на 115200 на 1 байт уйдет 86,8 мксек. на 10 байт 0,868 мсек. Это реальность. А максимум скорости передачи, действительно надо по прерыванию - буфер пуст.
skopus
Цитата(viakon @ Mar 1 2006, 11:30) *
Ты пытаешься за 108мкс передать 10 байтов длиной 87 мкс, вот у тебя 2 байта максимум и получается. Увеличивай скорость уарта или временной интервал


увеличить временной интервал невозможно.
А скорость уарта должна быть из стандартного ряда
Валера1968
если тебе нужна стандартная скорость 115200 то ты всяко не успеешь передать 10 байт, тебе минимум в пять раз надо поднять скорость передачи
skopus
ну поэтому я и создал эту тему.
Ща попробую через очередь сделать
viakon
Цитата(skopus @ Mar 2 2006, 14:59) *
ну поэтому я и создал эту тему.
Ща попробую через очередь сделать

За 108 мкс посылку длиной 870 мкс никак не передать, хоть какие очереди создавай.
ArtemK
Цитата
ну поэтому я и создал эту тему.
Ща попробую через очередь сделать

Видимо, Вы не совсем поняли, о чем говорилось в четырех предыдыдущих сообщениях. При выбранном Вами битрейте 115200 невозможно отправить 10 байт за 108 мкс даже при самой оптимальной по скорости программе и дело вовсе не в буферизации.
defunct
Цитата(ArtemK @ Mar 2 2006, 12:21) *
Видимо, Вы не совсем поняли, о чем говорилось в четырех предыдыдущих сообщениях. При выбранном Вами битрейте 115200 невозможно отправить 10 байт за 108 мкс даже при самой оптимальной по скорости программе и дело вовсе не в буферизации.


Автор вопроса нигде не говорил, что ему надо отправить 10 байт за 108 мкс. Упоминалось лишь только то, что надо "отправить по UART" 10 байт накопленных за 100 тиков таймера. Отправить 10 байт можно "мгновенно" если их физически не отправлять, а просто складывать в очередь.. Потом выбирать из очереди на текущей скорости UART'а хоть на 9600. Главное здесь чтобы очередь была достаточного объема, чтобы хранить все подготовленные к отправке данные.
beer_warrior
Цитата
Необходимо за 100 тактов таймера частотой Fosc/8 = 7.373/8 Mhz = 921.5 Khz выести через USART минимум 10 байт данных. Лучше даже 15.

2 defunct - за 100 тактов, через 100 подъедут новые 10 байт

2 skopus, а почему такие жесткие условия опишите задачу - может
придумается более легкий путь решения?
skopus
я сделал как советует defunct. все работает отлично. Просто супер. Огромное спасибо.

Задача - цифровая фильтрация
нужно различить комбинацию частот например 0,4 секунды частота 316 гц, потом 0,4...1 с частота 1080гц

Есть два идентичных канала на встроенном ацп. Сигнал считывается, пропускается параллельно через 2 полосных БИХ фильтра баттерворта 2 порядка. Каждые 16 тактов происходит интегрирование сигнала - то есть еще два фильтра но уже НЧ.
В итоге на каждом 16 такте нужно за прерывание выполнить расчет для 8 фильтров с частотой дискретизации 4167 гц. Я еле-еле укладываюсь в прерывание. Почти неделю код на Си оптимизировал.
Частота кварца 7.372 Mhz - это тоже жесткое условие.

Смысл в том чтоб в процедуре отправке данных по USART не было ожидания. То есть если данные еще не отправились, продолжаем считать, как только отправились, достаем из очереди, помещаем в UDR0 и считаем дальше

Теоретически можно обрабатывать и 4 канала, но это нужно писать тогда на асэмблере все.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.