|
|
  |
Возможность использования OS::channel в прерываниях |
|
|
|
Jan 16 2013, 08:54
|

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

|
QUOTE (abutorin @ Jan 16 2013, 05:29)  USART_ClearITPendingBit(this->PORT,USART_IT_TXE); Это очистка признака возникновения прерывания, в у STM32 большинство битов (признаков) прерывания необходимо очищать вручную. К сожалению я использую для передачи DMA, поэтому у меня нет примера для UART c каналом. Без ОС рабочий код у меня выглядит так: CODE INLINE void uart::handler() { uint32_t Status = pUART->SR; Status &= pUART->CR1; // mask disabled ints
if(Status & USART_SR_RXNE) { uint8_t Data = pUART->DR; if(Rx_buffer.has_place()) Rx_buffer.put(Data); } if(Status & USART_SR_TXE) { pUART->DR = Tx_buffer.get(); if(!Tx_buffer.has_data()) pUART->CR1 &= ~USART_CR1_TXEIE; } } Как видите, никаких ручных очисток. QUOTE (abutorin @ Jan 16 2013, 05:29)  А в какой момент происходи включение прерываний? в suspend я не нашел включение прерываний. Из suspend() вызывается TKernelAgent::reschelule(), который в свою очередь вызывет TKernel::shed(), в котором есть такой код: CODE do { enable_context_switch(); DUMMY_INSTR(); disable_context_switch(); } while(CurProcPriority != SchedProcPriority); // until context switch done Вот тут произойдет переключение на другой процесс, а уже этот процесс (если нет активных - то процесс Idle) выполняется с разрешенными прерываниями, т.е. при восстановлении его контекста прерывания будут разрешены.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 16 2013, 11:33
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Цитата(Сергей Борщ @ Jan 16 2013, 12:54)  К сожалению я использую для передачи DMA, поэтому у меня нет примера для UART c каналом. У меня логика построена на том, что я с уартом работаю как с потоком байт, я не знаю сколько байт отправляюи сколько получу. Если даже с таким подходом это можно реализовать через DMA то был бы очень признателен за пример. Если с прерываниями стало ясно.то опять возникает вопрос в чем же тогда проблема?
|
|
|
|
|
Jan 17 2013, 10:15
|

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

|
QUOTE (abutorin @ Jan 16 2013, 13:33)  Если с прерываниями стало ясно.то опять возникает вопрос в чем же тогда проблема? Вы заводите в начале обработчика объект типа OS::TISRW (я в предыдущем сообщении описался, указав его как OS::TCritSect)? Без него прерывание не сможет при необходимости вызвать перепланирование. QUOTE (abutorin @ Jan 16 2013, 13:33)  У меня логика построена на том, что я с уартом работаю как с потоком байт, я не знаю сколько байт отправляюи сколько получу. Если даже с таким подходом это можно реализовать через DMA то был бы очень признателен за пример. Как-то так, хотя мне самому этот код не очень нравится: CODE //**** uart.h: ***** #ifndef UART_H__ #define UART_H__ #include <stdint.h> #include <stm32f10x.h> #include <scmRTOS.h> #include <string.h>
#include <stdarg.h> #include <stdio.h>
class uart { public: uart(USART_TypeDef * usart, uint32_t dma_channel) : Tx_ready(OS::TEventFlag::efOn) , pUART(usart) , DMA_channel_no(dma_channel - 1) , DMA_channel((DMA_Channel_TypeDef *)(DMA1_Channel1_BASE + (dma_channel - 1) * 0x14)) {}
void send(const uint8_t &byte); void send(char const * string); void send(char * string, OS::TMutex * pLock); void send_HEX(uint8_t data); void send_HEX(uint16_t data); void new_line(); void vprintf(char const * format, va_list args); void printf(char const * format, ...); bool is_transmitting() {return !(pUART->SR & USART_SR_TXE);}
bool hasinput() {return Rx_buffer.get_count();} bool receive(uint8_t & data, timeout_t timeout); bool receive(char & data, timeout_t timeout); uint8_t receive() { uint8_t Data; Rx_buffer.pop(Data); return Data;}
void speed_setup(uint_fast32_t baudrate) { pUART->BRR = (PCLK1_FREQ + baudrate / 2) / baudrate; } void tx_dma_handler(); void rx_handler(); protected: static size_t const RX_BUFF_SIZE = 16;
OS::TEventFlag Tx_ready; uint8_t Tx_byte_buffer; OS::TMutex Tx_byte_buffer_lock;
OS::TMutex Tx_buffer_lock; char Tx_buffer[80];
OS::TMutex * pDMA_buffer_lock; void setup_tx_dma(uint8_t const * pSrc, size_t size, OS::TMutex *pLock = 0);
OS::channel<uint8_t volatile, RX_BUFF_SIZE> Rx_buffer; private: USART_TypeDef * pUART; uint_fast32_t DMA_channel_no; DMA_Channel_TypeDef * DMA_channel; }; // =========== receiving ============== INLINE void uart::rx_handler() { uint8_t Data = pUART->DR; if(Rx_buffer.get_free_size()) Rx_buffer.push(Data); }
inline bool uart::receive(uint8_t & symbol, timeout_t timeout) { if(timeout <= 0 || !Rx_buffer.pop(symbol, timeout)) return false; return true; }
inline bool uart::receive(char & symbol, timeout_t timeout) { uint8_t data; if(timeout <= 0 || !Rx_buffer.pop(data, timeout)) return false; symbol = char(data); return true; }
// ========== transmitting ===========
INLINE void uart::tx_dma_handler() { DMA1->IFCR = DMA_IFCR_CTCIF1 << (4 * DMA_channel_no); DMA_channel->CCR = 0 ; Tx_ready.signal_isr(); if(pDMA_buffer_lock) pDMA_buffer_lock->unlock_isr(); }
#endif //UART_H__
//**** uart.cpp ****** #include "uart.h" #include <string.h>
void uart::setup_tx_dma(uint8_t const * pSrc, size_t size, OS::TMutex * pLock) { Tx_ready.wait(); pDMA_buffer_lock = pLock; // --------- DMA setup ----------- DMA_channel->CPAR = uintptr_t(&pUART->DR); DMA_channel->CMAR = uintptr_t(pSrc); DMA_channel->CNDTR = size; DMA_channel->CCR = 0 | 1 * DMA_CCR1_EN // Channel enable | 1 * DMA_CCR1_TCIE // Transfer complete interrupt enable | 0 * DMA_CCR1_HTIE // Half Transfer interrupt enable | 0 * DMA_CCR1_TEIE // Transfer error interrupt enable | 1 * DMA_CCR1_DIR // Data transfer direction: Memory->Peripheral | 0 * DMA_CCR1_CIRC // Circular mode | 0 * DMA_CCR1_PINC // Peripheral increment mode | 1 * DMA_CCR1_MINC // Memory increment mode | 0 * DMA_CCR1_PSIZE_0 // Peripheral size: 8 bits | 0 * DMA_CCR1_MSIZE_0 // Memory size: 8 bits | 1 * DMA_CCR1_PL_0 // Channel Priority level: higher than lowest, conversion frequency is low enough | 0 * DMA_CCR1_MEM2MEM // Memory to memory mode disabled ;
}
void uart::send(uint8_t const &byte) { Tx_byte_buffer_lock.lock(); Tx_byte_buffer = byte; setup_tx_dma(&Tx_byte_buffer, 1, &Tx_byte_buffer_lock); }
void uart::send(char const * pString) { setup_tx_dma((uint8_t const *)pString, strlen(pString)); }
void uart::send(char * pString, OS::TMutex * pLock) { setup_tx_dma((uint8_t const *)pString, strlen(pString), pLock); }
void uart::vprintf(char const * format, va_list args) {
Tx_buffer_lock.lock(); vsprintf (Tx_buffer, format, args); send(Tx_buffer, &Tx_buffer_lock); }
void uart::printf(char const * format, ...) { va_list args; va_start (args, format); uart::vprintf (format, args); va_end (args); }
uart Serial(USART2, 7); uart & Console = Serial;
extern "C" void DMA1_Channel7_IRQHandler(void) { OS::TISRW ISR_wrapper; Console.tx_dma_handler(); }
extern "C" void USART2_IRQHandler(void) { OS::TISRW ISR_wrapper; Console.rx_handler(); }
Тут готовность DMA передается через флаг Tx_ready, а буфер передачи (если он используется для данной посылки) защищен мутексом Tx_byte_buffer_lock. Если же передается готовая строка из флеша или ОЗУ - буфер не используется.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 17 2013, 12:25
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Цитата(Сергей Борщ @ Jan 17 2013, 14:15)  Как-то так, хотя мне самому этот код не очень нравится: Да, я понял. Но мои вопросы этот метод нерешит т.к. посылка делается по одному байту (как поток), тут изначально нельзя знать какой объем займет посылка. Плохо что DMA не умеет работать как кольцевой буфер. Тогда хотябы можно было накапливать данные и отсылать по таймауту в случае отсутствия новых.
|
|
|
|
|
Jan 17 2013, 13:20
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Цитата(Сергей Борщ @ Jan 17 2013, 17:12)  Некое подобие можно сформировать. Укладывать данные в кольцевой буфер, а по таймауту или в прерывании окончания DMA перезагружать регистры DMA на очередной готовый участок этого буфера. Боюсь тогда алгоритм получится очень сложный а сказать уверенно что прерываний станет сильно меньше не получится, здесь наверное проще тогда сдлеать через накопление буфера и отправкой потом через ДМА когда буфер переполнен или по таймауту если данных больше нет.
|
|
|
|
|
Jan 18 2013, 05:37
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Цитата(AHTOXA @ Jan 17 2013, 22:31)  Насчёт работы с UART по DMA вот вам три темы: Там немножко разные подходы, выберите подходящий. Я делал по примеру от kan35 в конце третьей темы, работает. Спасибо, понял что нужно копать в направлении режима cilcularmode DMA. Попробую потом на досуге, сейчас сделал прием и передачу через прерывания. Код передачи выкладывал выше, прием сделал по образу и подобию, На ненагруженном камне прием уверенный на скорости в 0,5 мбит/с
|
|
|
|
|
Jan 18 2013, 07:41
|

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

|
QUOTE (abutorin @ Jan 18 2013, 07:37)  Спасибо, понял что нужно копать в направлении режима cilcularmode DMA. Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор. QUOTE (abutorin @ Jan 18 2013, 07:37)  сейчас сделал прием и передачу через прерывания. А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 18 2013, 09:14
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Цитата(Сергей Борщ @ Jan 18 2013, 11:41)  Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор. Для приемника: Если я правильно понял то кольцевой режим работает так, что после того как счетчик добирается до конца выделено памяти он просто сбрасывает его на начальное значение и работает так дальше. А передача всеравно срабатывает по нужному событию. Там правда усложнится процес получения одного байта, нужно вести свой счетчик хвоста, но в результате такой подход гарантирует автоматическое хранение N последних пришедших байт. Если правда данные вовремя не считывать то они начинают затиратся, но затираются "аккуратно" N последних байт всеравно получены последовательно. Вообщем идея заманчивая. Для передатчика: Прийдется использовать еще и таймер и отправлять данные пачкой при заполнении буфера или истечении таймаута ожидания. Надо только. Тогда по идее все тоже будет достаточно ненакладно. Цитата(Сергей Борщ @ Jan 18 2013, 11:41)  А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал  Код я выкладывал ранее. Я в итоге сделал без канала. Пришел к выводу что канал больше предназначен для межпроцессного взаимодействия, а в случае с УСАПП это больше ожидание одним потоком выполнения некоторого действия переферией. Доберусь до дома выложу код полностью.
|
|
|
|
|
Jan 18 2013, 18:52
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 3-09-12
Пользователь №: 73 374

|
Как обещал ранее выкладываю код который у меня работает. Код class usart_transport { private: usr::ring_buffer<uint8_t, 30, uint8_t> TxPool; usr::ring_buffer<uint8_t, 30, uint8_t> RxPool;
OS::TEventFlag TxNotFull; OS::TEventFlag RxNotEmpty;
public:
USART_TypeDef * PORT;
bool open (bool) { USART_ITConfig(this->PORT, USART_IT_RXNE, ENABLE); return true; }
INLINE void it_handler () { if (USART_GetITStatus(this->PORT,USART_IT_TXE) != RESET) { if(this->TxPool.get_count()) { USART_SendData(this->PORT,this->TxPool.pop()); this->TxNotFull.signal_isr(); } else { USART_ITConfig(this->PORT, USART_IT_TXE, DISABLE); } USART_ClearITPendingBit(this->PORT,USART_IT_TXE); }
if (USART_GetITStatus(this->PORT,USART_IT_RXNE) != RESET) { if (this->RxPool.get_free_size()) { this->RxPool.push(USART_ReceiveData(this->PORT)); this->RxNotEmpty.signal_isr(); } else { //здесь должна быть обработка переполнения буфера } USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); }
}
INLINE bool full () { TCritSect cs;
return !this->TxPool.get_free_size();
} INLINE bool empty () { TCritSect cs;
return !this->RxPool.get_count(); }
void send(const uint8_t & data) { this->TxNotFull.clear();
while (this->full()) { this->TxNotFull.wait(); }
{ TCritSect cs; this->TxPool.push(data); }
USART_ITConfig(this->PORT, USART_IT_TXE, ENABLE); }
uint8_t get () { this->RxNotEmpty.clear();
while (this->empty()) { this->RxNotEmpty.wait(); }
TCritSect cs;
return this->RxPool.pop(); } }; Отказ от канала в результате вызван тем, что канал больше предназначен для межпроцессного взаимодействия а не между процессом и прерыванием. Инициализацию порта необходимо выполнять самостоятельно. Сразу хочу заметить что данная реализация сделана на коленке и имеет достаточный потенциал для улучшения, сразу видно что целесообразно сделать данный класс через шаблон со статичными членами, с указанием порта как параметра и использовать данные методы без создания экземпляра класса.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|