реклама на сайте
подробности

 
 
> Работа с UART в пакетном режиме и ОС
Pavel V.
сообщение Nov 15 2010, 08:03
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 211
Регистрация: 3-06-06
Пользователь №: 17 742



Как правильно организовать работу с пакетным протоколом (что-то наподобие WAKE в собственной реализации) в условиях использования операционки?

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

Ответный пакет формируется в функции обработки команды и запускается отправка по прерыванию.

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

В этом случае возникает необходимость использования чего-то вроде кольцевого буфера для предотвращения потери данных.

Как правильно реализовать такую схему при наличии операционки?

Пока видится только такая схема работы:
1. Процесс, который занимается отправкой данных в порт, занят только тем, что висит в вечном channel.pop() из кольцевого буфера и в случае появления данных выполняет отправку байта в порт.
2. Процесс, который обрабатывает асинхронные события. Например, нажали кнопку. Процесс в этом случае вызывает некую функцию SendInputAsync() в которой формируется пакет и делается необходимое количество channel.push(). Затем процесс уходит в Sleep() на заданное настройками время.

Возможно, необходимо сделать процесс на каждое асинхронное событие?

Смущает передача управления для отправки каждого байта. Можно, конечно, вызывать channel.pop() с таймаутом и воспользоваться дополнительным событием, которое будет устанавливаться только по добавлению в канал полного пакета. Но как-то громоздко получается.

Прошу помощи у более опытных товарищей smile.gif


--------------------
Good News Everyone!
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
Сергей Борщ
сообщение Nov 15 2010, 13:03
Сообщение #2


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Pavel V. @ Nov 15 2010, 10:03) *
1. Процесс, который занимается отправкой данных в порт, занят только тем, что висит в вечном channel.pop() из кольцевого буфера и в случае появления данных выполняет отправку байта в порт.
А почему бы не повесить кольцевой буфер на UART (и опустошать его в прерывании), а сам процесс отправки пакета не обрамить мутексом? Тогда любой процесс сможет оправлять пакеты асинхронно. Что-то типа такого (не wake, но подобный протокол):
CODE
class bus_frame
{
public:
typedef uint16_t address_t;
protected:
enum
{
STA = 0xC0,
STO = 0xC1,
CTL = 0x7F,
TRANSPARENCY = 0x20,
};
typedef crc::ccitt crc_t;
};

class frame : public bus_frame
{
public:
frame(iodevice & uart);
frame(iodevice & uart, address_t address);
~frame();

void send(void const * buffer, size_t size);
void send(uint8_t data); // calculate CRC and send transparently

protected:
void send_transparently(uint8_t data);
static OS::TMutex Mutex;
iodevice & Uart;
crc_t CRC;
};


frame::frame(iodevice & uart, address_t address)
: Uart(uart)
{
Mutex.Lock();
Uart.send(STA);
send(address >> 0);
send(address >> 8);
}

frame::frame(iodevice & uart)
: Uart(uart)
{
Mutex.Lock();
Uart.send(STA);
}

frame::~frame()
{
send_transparently(~CRC >> 0);
send_transparently(~CRC >> 8);
Uart.send(STO);
Mutex.Unlock();
}

void frame::send(uint8_t data)
{
CRC.calculate(data);
send_transparently(data);
}

void frame::send_transparently(uint8_t data)
{
if((data == CTL) || (data == STA) || (data == STO))
{
Uart.send(CTL);
data ^= TRANSPARENCY;
}
Uart.send(data);
}

void frame::send(void const * buffer, size_t size)
{
uint8_t const * pSrc = (uint8_t const *)buffer;
while(size--)
send(*pSrc++);
}

void Test(bus_frame::address_t destination)
{
frame Frame(Uplink, destination);
Frame.send("Tipa test", sizeof("Tipa test"));
}


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Pavel V.
сообщение Nov 15 2010, 13:50
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 211
Регистрация: 3-06-06
Пользователь №: 17 742



Цитата(Сергей Борщ @ Nov 15 2010, 16:03) *
А почему бы не повесить кольцевой буфер на UART (и опустошать его в прерывании), а сам процесс отправки пакета не обрамить мутексом? Тогда любой процесс сможет оправлять пакеты асинхронно. Что-то типа такого (не wake, но подобный протокол):

Сергей, огромное спасибо! Вы уже не первый раз мне помогаете дельным советом.

Идею Вашу понял, очень интересно! Насколько я понял, константы STA и STO служат признаками начала и конца пакета. Почему их отправку Вы поместили именно в конструктор и деструктор, а не в функцию send()?

Функция Uart.send() помещает данные в кольцевой буфер, который опустошается в прерываниях? Не могли бы Вы показать как сделано это место? Интересует сама функция Uart.send() и обработчик прерывания.

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


--------------------
Good News Everyone!
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Nov 15 2010, 14:22
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (Pavel V. @ Nov 15 2010, 15:50) *
Идею Вашу понял, очень интересно! Насколько я понял, константы STA и STO служат признаками начала и конца пакета. Почему их отправку Вы поместили именно в конструктор и деструктор, а не в функцию send()?
Потому что функцию send() можно вызывать несколько раз во время передачи пакета.
CODE
void raw_message::send(frame & link) const
{
    link.send(Type);
    link.send(&Header, sizeof(Header));
    link.send(this + 1, Size);
}
void Test()
{
    frame Frame(Uplink);
    pMessage->send(Frame);
}

QUOTE (Pavel V. @ Nov 15 2010, 15:50) *
Функция Uart.send() помещает данные в кольцевой буфер, который опустошается в прерываниях? Не могли бы Вы показать как сделано это место? Интересует сама функция Uart.send() и обработчик прерывания.
Под MSP430F2618 у меня нет, вот под AVR:
CODE
#define UART_H__
#include <stdint.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <scmRTOS.h>
#define UART_RX_BUFF_SIZE 64
#define UART_TX_BUFF_SIZE 64

ISR(USART_RXC_vect);
ISR(USART_UDRE_vect);


class uart_t
{
public:
uart_t() {};
bool hasinput();
uint8_t receive();
template <typename T>
bool receive(T & item, timeout_t timeout = 0)
{
return RxBuffer.read((uint8_t * const)&item, sizeof(T), timeout);
}
void send(uint8_t byte);
void sendHEX(uint8_t byte);
void send(PGM_P pString);
bool tx_complete() { return UCSRA & ( 1 << TXC ); }
private:
friend void USART_RXC_vect();
friend void USART_UDRE_vect();
void RXC_Handler();
void UDRE_Handler();
private:
OS::channel<uint8_t, UART_RX_BUFF_SIZE> RxBuffer;
OS::channel<uint8_t, UART_TX_BUFF_SIZE> TxBuffer;
};
inline bool uart_t::hasinput (void)
{
return RxBuffer.get_count();
}


extern uart_t UART;
#endif // UART_H__
// ========= uart.cpp ==============
#include <avr/interrupt.h>
#include "Hardware.h"
#include "UART.h"

uint8_t uart_t::receive ()
{
uint8_t Data;
RxBuffer.pop(Data);
return Data;
}

void uart_t::send(uint8_t byte)
{
TxBuffer.push(byte);
UCSRB |= (1<<UDRIE); // UCSRB in io-space, no critical section required
}
#include <avr/pgmspace.h>
void uart_t::sendHEX(uint8_t byte)
{
static char const PROGMEM Table[] = "0123456789ABCDEF";
send(pgm_read_byte(&Table[byte >> 4]));
send(pgm_read_byte(&Table[byte & 0x0F]));
}
void uart_t::send(PGM_P pString)
{
char c;
while( (c = pgm_read_byte(pString++)) )
{
send©;
}
}

inline void uart_t::RXC_Handler()
{
char RxData = UDR;
if(RxBuffer.get_free_size())
{
RxBuffer.push(RxData);
}
}

OS_INTERRUPT void USART_RXC_vect()
{
OS::TISRW isr;
UART.RXC_Handler();
}

inline void uart_t::UDRE_Handler()
{
UCSRB &= ~(1<<UDRIE);
sei();
{
if(TxBuffer.get_count())
{
uint8_t Data;
TxBuffer.pop(Data);
UDR = Data;
cli(); // avoid recursive call
UCSRB |= (1<<UDRIE);
}
}
}

OS_INTERRUPT void USART_UDRE_vect()
{
OS::TISRW isr;
UART.UDRE_Handler();
}

uart_t UART;


QUOTE (Pavel V. @ Nov 15 2010, 15:50) *
Я когда экспериментировал с модулем UART (на процессоре MSP430F2618), при постоянно разрешенных прерываниях по отправке, у меня были сложности. Поэтому в текущей реализации я запрещаю прерывание по отправке после отправки пакета целиком, а по попадании пакета в буфер отправки, соответственно, это прерывание разрешаю (тавтология какая-то получилась, но Вы, наверное, поняли что я имею в виду).
В AVR иначе и не сделаешь. Там нет возможности программно сбросить флаг готовности передатчика, и мне кажется это правильно: все передали (буфер пуст), нет необходимости в дальнейших прерываниях - запретили прерывания. Появилась необходимость (положили что-то в буфер) - разрешили прерывания, и если передатчик готов (флаг взведен) - сразу побежали в обработчик. А если прерывание было разрешено, опустошило буфер и отключило само себя до того, как в send() его разрешили снова - в обработчике есть проверка на пустоту буфера. Минимум лишних движений.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post



Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 30th June 2025 - 02:26
Рейтинг@Mail.ru


Страница сгенерированна за 0.01406 секунд с 7
ELECTRONIX ©2004-2016