Доброго времени суток!
Есть проект с использованием RTX-kernel. Нужно организовать прием/передачу данных по UART. Т.к. скорости маленькие 4800/9600, то хочется снизить время бесполезного простоя системы в ожидании готовности приемника или передатчика - есть масса другой работы по обработке данных. Вот раздумываю как это сделать правильнее, красивее, с точки зрения RTOS.
С приемом, в общем, более-менее понятно. Включены прерывания по приему символа (ПДП пока не используется) и в обработчике принятый символ через mailbox отправляется задаче обрабатывающей прием.
Передачу, на данный момент сделал так:
Разрешил прерывания по готовности передатчика, в обработчике прерывания устанавливается событие "передатчик готов", а уже задача, ответственная за передачу, по этому событию пишет данные в буфер UART'a.
Минусы: обработчик прерывания один. Поэтому, когда нет данных для передачи, но идет прием, обработчик прерывания вызванный приемником, ставит и событие готовности передатчика (бит в статусе USART TXRDY ведь установлен). При этом получается что будет установка уже установленного события. Есть ли тут криминал, не знаю. В доках по этому поводу ничего не нашел.
Рассматривал вариант организации передачи тоже через mailbox. Во-первых мне показалось это излишним усложнением. во-вторых, обработчик прерывания вызывается при освобождении передатчика. А если данные появились позже?
Ну и третий вариант, просто сделать задачу передачи с низшим приоритетом, и просто пулингом ждать освобождения передатчика, без прерываний. Другие задачи будут ее о необходимости прерывать. Уж совсем как-то некрасиво, да и задача передатчика не самая низшая по приоритетности.
В общем, интересно, кто какие подходы использует?
Не думаю что это имеет значение, но на всякий случай, контроллер AT91SAM7X...
Vichkins
Feb 28 2011, 06:37
вот как сделано у меня, прерывания для передачи не используются, только для приёма
контроллер у меня lpc1343, но я думаю под ваш допилить можно
переменные:
Код
static char recvbuf[256];
static char sendbuf[256];
static U8 txridx=0;
static U8 txwidx=0;
static U8 rxridx=0;
static U8 rxwidx=0;
OS_TID t_UARTTxTask;
OS_TID t_UARTRdTask;
__task void uartTXTask(void);
и остальное
Код
void UART_SendData(char Data)
{
sendbuf[txwidx++]=Data;
os_evt_set (OS_FLAG_UART_TXDATAPENDING, t_UARTTxTask);
}
uint8_t UART_ReceiveData(void)
{
if (rxridx == rxwidx) {
t_UARTRdTask = os_tsk_self ();
os_evt_wait_or (OS_FLAG_UART_RECEIVED, 0xffff);
os_evt_clr (OS_FLAG_UART_RECEIVED, t_UARTRdTask);
}
return (recvbuf[rxridx++]);
}
uint8_t UART_DataAvailable(void)
{
uint8_t tmp = rxwidx - rxridx;
return tmp<0?256-tmp:tmp;
}
__task void uartTXTask (void) {
while(1) {
os_evt_wait_or(OS_FLAG_UART_TXDATAPENDING, 0xffff);
while(txridx<txwidx)
{
LPC_UART->THR = sendbuf[txridx++] & UART_THR_MASKBIT;
while (!(LPC_UART->LSR & UART_LSR_THRE));
}
os_evt_clr(OS_FLAG_UART_TXDATAPENDING, t_UARTTxTask);
}
}
void __irq UART_IRQHandler(void)
{
uint8_t ch;
if (LPC_UART->IIR & UART_IIR_INTID_MASK == UART_IIR_INTID_RDA){
ch = (uint8_t)(LPC_UART->RBR & UART_RBR_MASKBIT);
recvbuf[rxwidx++]=ch;
isr_evt_set (OS_FLAG_UART_RECEIVED, t_UARTRdTask);
}
NVIC_ClearPendingIRQ(UART_IRQn);
return;
}
Цитата(Vichkins @ Feb 28 2011, 08:37)

вот как сделано у меня, прерывания для передачи не используются, только для приёма
контроллер у меня lpc1343, но я думаю под ваш допилить можно
и остальное
Код
uint8_t UART_ReceiveData(void)
{
if (rxridx == rxwidx) {
t_UARTRdTask = os_tsk_self ();
os_evt_wait_or (OS_FLAG_UART_RECEIVED, 0xffff);
os_evt_clr (OS_FLAG_UART_RECEIVED, t_UARTRdTask);
}
return (recvbuf[rxridx++]);
}
void __irq UART_IRQHandler(void)
{
uint8_t ch;
if (LPC_UART->IIR & UART_IIR_INTID_MASK == UART_IIR_INTID_RDA){
ch = (uint8_t)(LPC_UART->RBR & UART_RBR_MASKBIT);
recvbuf[rxwidx++]=ch;
isr_evt_set (OS_FLAG_UART_RECEIVED, t_UARTRdTask);
}
NVIC_ClearPendingIRQ(UART_IRQn);
return;
}
Наверное так, с буфером даже лучше, по эффективности чем с mailbox'ами. Надо попробовать сравнить...
А вот с передачей:
Код
LPC_UART->THR = sendbuf[txridx++] & UART_THR_MASKBIT;
while (!(LPC_UART->LSR & UART_LSR_THRE));
собственно вот этого цикла while я и хотел избежать. Из-за малой скорости предачи это ожидание будет относительно долгое, а в это время могли бы выполняться некоторые менее приоритетные задачи...
Vichkins
Mar 1 2011, 21:16
ну тогда наверно лучше сигналить задаче передачи из прерывания, я пробовал так делать, но тк пока всё не в железе а в симуляторе keil, то передача в терминал производилась очень медленно и я сделал с циклом
судя по вот этой табличке
http://www.keil.com/support/man/docs/rlarm...timing_spec.htm, можно прикинуть что при использовании метода "сигналить задаче передачи из прерывания", при частоте 60МГц на ARM7 мы вряд ли сможем достич скоростей выше 100000
как вариант могу предложить сделать вариант с while и поставить достаточно быстрое переключение round-robin, если возможно
можно ещё попробовать так, правда не знаю как будет работать
Код
LPC_UART->THR = sendbuf[txridx++] & UART_THR_MASKBIT;
os_tsk_pass();
while (!(LPC_UART->LSR & UART_LSR_THRE));
теоретически так будет больше времени отдаватся остальным задачам
serbor
Mar 13 2011, 19:41
Тоже приценивался к этой операционке. А что она работает только как раунд-робин? Мне показалось, что там есть возможность вытеснения.
Цитата(serbor @ Mar 13 2011, 21:41)

Тоже приценивался к этой операционке. А что она работает только как раунд-робин? Мне показалось, что там есть возможность вытеснения.
нет, не только. Раунд-робин можно включать или нет в конфигурационном файле. Т.е на этапе компиляции можно выбрать пользоваться раунд-робин или обычной вытесняющей многозадачностью.
sinc_func
Mar 22 2011, 18:16
В подобной задаче я ориентируюсь на такой вариант
-использование FIFO UARTa (для LPCxxx) должно дать временной буфер где-то в 15-30 ms,
что много больше чем типичный tick,
- под обслуживание UARTa выделяется чисто отдельная задача с круговым опросом
статуса UARTa по приему, статуса по передаче и связью с кучей других задач через
выделенный mailbox(ы)...и никаких прерываний,
- такую пробную версию я сделал на Round-Robin-е - впечатление- приятное ..
- при отсутсвии каких-то "шевелений" в статусах этой задачи и необходимости чего-то
"окучивать" - эта задача просто отдает активный статус через os_tsk_pass() другой задаче
Конечно, это простое решение можно покритиковать - но это простое решение
Цитата(sinc_func @ Mar 22 2011, 20:16)

- при отсутсвии каких-то "шевелений" в статусах этой задачи и необходимости чего-то
"окучивать" - эта задача просто отдает активный статус через os_tsk_pass() другой задаче
Цитата
The os_tsk_pass function passes control to the next task of the same priority in the ready queue. If there is no task of the same priority in the ready queue, the current task continues and no task switching occurs.
Нсколько я понял из описания, задаче с меньшим приоритетом os_tsk_pass управлени не отдаст. Кто-нить проверял как оно на практике?
Round-robin не пользую.
sinc_func
Apr 8 2011, 13:19
Цитата(Shein @ Mar 25 2011, 13:01)

Нсколько я понял из описания, задаче с меньшим приоритетом os_tsk_pass управлени не отдаст. Кто-нить проверял как оно на практике?
Round-robin не пользую.
У себя в задачах я ориентируюсь пока только на Round-robin
(не хочется напрягаться и тянуть без должной мотивации систему приоритетов)
Vichkins этот код похоже взят из примеров Keil. Я так и не понял одну вещь.
Смещение для записи в буфер ничем не ограничено.
txwidx++ перелезет за размеры буфера. Как это у вас работало не понимаю.
Код
void UART_SendData(char Data)
{
sendbuf[txwidx++]=Data;
os_evt_set (OS_FLAG_UART_TXDATAPENDING, t_UARTTxTask);
}
Vichkins
Feb 25 2012, 00:10
uriy
код был не из примеров
переменная txwidx типа uint8_t и sendbuf[256], всё просто
Спасибо, слона то я и не заметил. Я использую этот же код в контроллере, но я добавил проверку индекса, у меня размер буфера не равен 256 байтам. Из контроллера отсылается эхо. Если закидывать в UART файл размером в десятки килобайт то в какой-то момент эхо пропадает. Перестает работать либо putchar либо getchar бывает по-разному. Судя по всему где-то зависает на ожидании события. У вас не было подобного?