Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Возможность использования OS::channel в прерываниях
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
Страницы: 1, 2
abutorin
Добрый вечер.
Пробую сделать передачу по USART с использование буфера. Для межпроцессорного удобства очень нравится использовать OS::channel. Возможно ли его использовать и в прерываниях? Точнее интересует процедура получения элемента из канала?
AHTOXA
Использовать канал в прерывании можно. Учтите только, что если вы в него что-то заталкиваете, а он полон, то будет сделана попытка усыпить прерванный процесс и дождаться освобождения (или при доставании из пустого канала). Вероятно, это не то поведение, которое ожидается в прерыванииsm.gif
Поэтому проверяйте наличие свободного места при запихивании чего-то в канал, и наличие данных при доставании из канала.
Примерно вот так:
Код
void uart_t::irq_handler(){
    uint16_t status = USARTx->SR;
    if (status & USART_SR_RXNE){
        uint8_t ch = USARTx->DR;
        if (RxChannel.get_free_size())
            RxChannel.push(ch);
    }
            
    if (status & USART_SR_TXE){
        if (TxChannel.get_count()){
            char ch = 0;
            TxChannel.pop(ch);
            USARTx->DR = ch;
        }
        else
            disable_tx_interrupt();
    }
}
abutorin
Спасибо, проверку заполнености, проверку пустоты я уже учел.
Vasya777
Мне кажется для обмена по интерфейсам связи лучше использовать message, там есть специальный метод для вызова из прерываний. А уже в обычном процессе складывать сообщения в буфер. Поправьте, если я что-то не так сказал, я ещё новичёк.
abutorin
Цитата(AHTOXA @ Jan 14 2013, 00:45) *
Использовать канал в прерывании можно. Учтите только, что если вы в него что-то заталкиваете, а он полон, то будет сделана попытка усыпить прерванный процесс и дождаться освобождения (или при доставании из пустого канала). Вероятно, это не то поведение, которое ожидается в прерыванииsm.gif
Поэтому проверяйте наличие свободного места при запихивании чего-то в канал, и наличие данных при доставании из канала.


Примерно так и делал.
Код
    
class usart_t
{
public:
    OS::channel<uint8_t,4> Rxbuf;
    OS::channel<uint8_t,4> Txbuf;
    USART_TypeDef * PORT;

INLINE void it_handler ()
    {

        if (USART_GetITStatus(this->PORT,USART_IT_TXE) != RESET)
        {
            {
                if(this->Txbuf.get_count())
                {
                    uint8_t data;
                    this->Txbuf.pop(data);
                    USART_SendData(this->PORT,data);
                }
                else
                {
                    USART_ITConfig(this->PORT, USART_IT_TXE, DISABLE);
                }

            }

            USART_ClearITPendingBit(this->PORT,USART_IT_TXE);
        }

    }

    void send(const uint8_t & data)
    {
        this->Txbuf.push(data);
        USART_ITConfig(this->PORT, USART_IT_TXE, ENABLE);

    }


Размер буфера 4 элемента. заполняю его в одном из потоков:
Код
        
      for(i=0;i<8;i++)
        {
            usart.send(i+0x30);
        }

        OS::sleep(1000);

Задача проверить работоспособность при заполнении буфера до отказа. В результата камень зависает в прерывании. Причем смотрю терминалом на компьютере, нормально отправляется только 2 байта.

Поэкспериментировал немного с размером буфера и размером отправляемых данных, если размер буфера поставить 5 и отправлять 8 байт то все работает нормально, размер буфера 14 количество отправляемых данных 16 тоже нормально. На лицо правило размер буфера = количество отправляемых данных - 2.
_Артём_
Не знаю почему зависает, наверное что-то неправильно ...
Не пойму, в чём сермяга посать всюду this->?
abutorin
Цитата(_Артём_ @ Jan 15 2013, 23:26) *
Не знаю почему зависает, наверное что-то неправильно ...
Не пойму, в чём сермяга посать всюду this->?

Объектный подход, да можно былобы статичным класом обойтись, думаю это не критично.

Посмотрел поглубже, помоему нашел в чем проблема:
Код
template<typename T, uint16_t Size, typename S>
void OS::channel<T, Size, S>::push(const T& item)
{
    TCritSect cs;

    while(!pool.get_free_size())
    {
        // channel is full, suspend current process until data removed
        suspend(ProducersProcessMap);
    }

    pool.push_back(item);
    resume_all(ConsumersProcessMap);
}

Судя по коду, push выполняется с запретом прерывания. Как я понимаю работу TCritSect, запрет снимается в деструкторе, так вот деструктор не вызовется, т.к. потребитель канала находится в прерывании.
Получается что канал использовать с прерываниями не получится.
_Артём_
Цитата(abutorin @ Jan 15 2013, 21:31) *
Объектный подход,

Объектный - это хорошо, только this-> зачем всегда писать? попробуйте без него. Писанины меньше.

Цитата(abutorin @ Jan 15 2013, 21:31) *
Посмотрел поглубже, помоему нашел в чем проблема:

Проблема в отсутствии метода push_isr. А вот почему его нет - вопрос? Наверное руки недошли...Применять то можно судя по примерам выше.

Цитата(abutorin @ Jan 15 2013, 21:31) *
т.к. потребитель канала находится в прерывании.

Хуже того - будет усыплён процесс прерванный прерыванием (Idle например) и наверное не только это .
abutorin
Проблему решил использовав События:
Код
class usart
{
public:
    usr::ring_buffer<uint8_t, 10, uint8_t> TxPool;

    OS::TEventFlag TxNotFull;

    USART_TypeDef * PORT;

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);
        }

    }

    INLINE bool full ()
    {
        TCritSect cs;

        return !this->TxPool.get_free_size();

    }

    void send(const uint8_t & data)
    {

        this->TxNotFull.clear();

        while (this->full())
        {
            this->TxNotFull.wait();
        }

        this->TxPool.push(data);

        USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

    }

Решение черновое в плане оформления класа, но концептуально думаю досточно простое и надежное. Оно правда подразумевает что заполнять буфер будет только один поток, т.к. из средств синхронизации используется только критическая секция на проверку заполненности.

Цитата(_Артём_ @ Jan 16 2013, 00:10) *
Объектный - это хорошо, только this-> зачем всегда писать? попробуйте без него. Писанины меньше.

если писать this то IDE подставляет выбор членов класса ). Так что this писать зачастую короче чем помнить точное название члена.
_Артём_
Цитата(abutorin @ Jan 15 2013, 22:14) *
если писать this то IDE подставляет выбор членов класса ). Так что this писать зачастую короче чем помнить точное название члена.

На что только не пойдёт человек, лишь бы новое не осваивать.
IDE у вас какая-то устаревшая.

А IO-регистры подсказывает?
abutorin
Цитата(_Артём_ @ Jan 16 2013, 00:20) *
На что только не пойдёт человек, лишь бы новое не осваивать.
IDE у вас какая-то устаревшая.

А IO-регистры подсказывает?


Второйдовод писать всегда, этоодним взглядом на код понятно что это член класса а не какаянибудь переменная локальная. IDE у меня Eclipse под линуксом. Последнее время работают только с STM32 и использую стандартную библиотеку, до регистров решил не опускатся. Сейчас больше интересует "бизнес-логика" алгоритмов.
_Артём_
Цитата(abutorin @ Jan 15 2013, 22:24) *
Второйдовод писать всегда, этоодним взглядом на код понятно что это член класса а не какаянибудь переменная локальная.

Если писать всега this, то сомнений не возникнет...

Цитата(abutorin @ Jan 15 2013, 22:24) *
а не какаянибудь переменная локальная.

обычно так делаю
Код
if (local_variable != GlobalVariable)

, то есть через правила именования, но они у каждого свои, да.

Цитата(abutorin @ Jan 15 2013, 22:24) *
IDE у меня Eclipse под линуксом.

Ошибся, IDE - не устаревшая.

Цитата(abutorin @ Jan 15 2013, 22:24) *
Последнее время работают только с STM32 и использую стандартную библиотеку, до регистров решил не опускатся. Сейчас больше интересует "бизнес-логика" алгоритмов.


"решил не опускатся" или решил не подниматься?

Цитата(abutorin @ Jan 15 2013, 22:24) *
Сейчас больше интересует "бизнес-логика" алгоритмов.

Логика у вас... в смысле логика тоже дело...
Сергей Борщ
QUOTE (abutorin @ Jan 15 2013, 21:31) *
Судя по коду, push выполняется с запретом прерывания. Как я понимаю работу TCritSect, запрет снимается в деструкторе, так вот деструктор не вызовется, т.к. потребитель канала находится в прерывании.
Прерывания будут разрешены во время suspend() при передаче управления другому процессу. Что касается вашей проблемы - мне кажется причина в том, что вы делаете USART_ClearITPendingBit(this->PORT,USART_IT_TXE); даже если данных не было и ничего не было передано. И когда данные в канале появляются - у процессора нет причин вызвать прерывание и отправить их. Хотя я не очень хорошо помню, как этот механизм (pending) работает в кортексах. Также полагаю, что объект типа OS::TCritSect вы в начале обработчика прерывания создаете. Если нет - это тоже может быть причиной.
abutorin
Цитата(Сергей Борщ @ Jan 16 2013, 02:46) *
Прерывания будут разрешены во время suspend() при передаче управления другому процессу. Что касается вашей проблемы - мне кажется причина в том, что вы делаете USART_ClearITPendingBit(this->PORT,USART_IT_TXE); даже если данных не было и ничего не было передано. И когда данные в канале появляются - у процессора нет причин вызвать прерывание и отправить их. Хотя я не очень хорошо помню, как этот механизм (pending) работает в кортексах. Также полагаю, что объект типа OS::TCritSect вы в начале обработчика прерывания создаете. Если нет - это тоже может быть причиной.

USART_ClearITPendingBit(this->PORT,USART_IT_TXE); Это очистка признака возникновения прерывания, в у STM32 большинство битов (признаков) прерывания необходимо очищать вручную. Прерывание по USART_IT_TXE выключается командой USART_ITConfig(this->PORT, USART_IT_TXE, DISABLE); только в случае если канал пустой. А в процедуре которая заполняет канал send это прерывание всегда включаетсся.
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

Посмотрел еще раз
Код
   void OS::TService::suspend(TProcessMap volatile & waiters_map)
    {
        TProcessMap PrioTag = cur_proc_prio_tag();
    
        set_prio_tag(waiters_map, PrioTag);                   // put current process to wait map
        clr_prio_tag(ready_process_map(), PrioTag);           // remove current process from ready map
    
    #if scmRTOS_DEBUG_ENABLE == 1
        cur_proc_waiting_for() = this;                        // catch current service address to process debug data
    #endif

    #if scmRTOS_PROCESS_RESTART_ENABLE == 1
        cur_proc_waiting_map() = &waiters_map;
    #endif
        
        reschedule();
        
    #if scmRTOS_DEBUG_ENABLE == 1
        cur_proc_waiting_for() = 0;                           // remove current service address from process debug data
    #endif
        
    #if scmRTOS_PROCESS_RESTART_ENABLE == 1
        cur_proc_waiting_map() = 0;
    #endif
        
    }

void OS::channel<T, Size, S>::push(const T& item)
{
    TCritSect cs;

    while(!pool.get_free_size())
    {
        // channel is full, suspend current process until data removed
        suspend(ProducersProcessMap);
    }

    pool.push_back(item);
    resume_all(ConsumersProcessMap);
}

А в какой момент происходи включение прерываний? в suspend я не нашел включение прерываний.
_Артём_
Цитата(abutorin @ Jan 16 2013, 05:29) *
А в какой момент происходи включение прерываний? в suspend я не нашел включение прерываний.

В suspend произойдёт перепланировка (reschedule), запустится на выполнение очередной готовый процесс и где-то в конце этой процедуры будут разрешены прерывания.
AHTOXA
TXE очищается записью в DR. Вручную его чистить не надо, он read-only.
Сергей Борщ
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) выполняется с разрешенными прерываниями, т.е. при восстановлении его контекста прерывания будут разрешены.
AHTOXA
Цитата(Сергей Борщ @ Jan 16 2013, 14:54) *
Код
    Status &= pUART->CR1;   // mask disabled ints

А это зачем? Чтобы не реагировать на RXNE и TXE при маскировке этих прерываний?
Сергей Борщ
QUOTE (AHTOXA @ Jan 16 2013, 12:18) *
А это зачем? Чтобы не реагировать на RXNE и TXE при маскировке этих прерываний?
Да, на все запрещенные. В данном случае на TXE. RXNE у меня не запрещается.
abutorin
Цитата(Сергей Борщ @ Jan 16 2013, 12:54) *
К сожалению я использую для передачи DMA, поэтому у меня нет примера для UART c каналом.

У меня логика построена на том, что я с уартом работаю как с потоком байт, я не знаю сколько байт отправляюи сколько получу. Если даже с таким подходом это можно реализовать через DMA то был бы очень признателен за пример.
Если с прерываниями стало ясно.то опять возникает вопрос в чем же тогда проблема?
AHTOXA
Цитата(Сергей Борщ @ Jan 16 2013, 17:27) *
Да, на все запрещенные. В данном случае на TXE. RXNE у меня не запрещается.

У меня такого нет, и работает нормально. Это что получается, я при запрещённых прерываниях TXE всё равно каждый раз при входе в прерывание влетаю в эту ветку обработчика?
Проверил - точно, влетаю. Неаккуратненько...
Сергей Борщ
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. Если же передается готовая строка из флеша или ОЗУ - буфер не используется.
abutorin
Цитата(Сергей Борщ @ Jan 17 2013, 14:15) *
Как-то так, хотя мне самому этот код не очень нравится:


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

Боюсь тогда алгоритм получится очень сложный а сказать уверенно что прерываний станет сильно меньше не получится, здесь наверное проще тогда сдлеать через накопление буфера и отправкой потом через ДМА когда буфер переполнен или по таймауту если данных больше нет.
AHTOXA
Насчёт работы с UART по DMA вот вам три темы:

Там немножко разные подходы, выберите подходящий.
Я делал по примеру от kan35 в конце третьей темы, работает.
abutorin
Цитата(AHTOXA @ Jan 17 2013, 22:31) *
Насчёт работы с UART по DMA вот вам три темы:

Там немножко разные подходы, выберите подходящий.
Я делал по примеру от kan35 в конце третьей темы, работает.

Спасибо, понял что нужно копать в направлении режима cilcularmode DMA. Попробую потом на досуге, сейчас сделал прием и передачу через прерывания. Код передачи выкладывал выше, прием сделал по образу и подобию, На ненагруженном камне прием уверенный на скорости в 0,5 мбит/с
Сергей Борщ
QUOTE (abutorin @ Jan 18 2013, 07:37) *
Спасибо, понял что нужно копать в направлении режима cilcularmode DMA.
Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор.
QUOTE (abutorin @ Jan 18 2013, 07:37) *
сейчас сделал прием и передачу через прерывания.
А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал sm.gif
abutorin
Цитата(Сергей Борщ @ Jan 18 2013, 11:41) *
Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор.


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

Цитата(Сергей Борщ @ Jan 18 2013, 11:41) *
А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал sm.gif

Код я выкладывал ранее. Я в итоге сделал без канала. Пришел к выводу что канал больше предназначен для межпроцессного взаимодействия, а в случае с УСАПП это больше ожидание одним потоком выполнения некоторого действия переферией. Доберусь до дома выложу код полностью.
abutorin
Как обещал ранее выкладываю код который у меня работает.

Код
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();
    }
};

Отказ от канала в результате вызван тем, что канал больше предназначен для межпроцессного взаимодействия а не между процессом и прерыванием. Инициализацию порта необходимо выполнять самостоятельно. Сразу хочу заметить что данная реализация сделана на коленке и имеет достаточный потенциал для улучшения, сразу видно что целесообразно сделать данный класс через шаблон со статичными членами, с указанием порта как параметра и использовать данные методы без создания экземпляра класса.
AHTOXA
USART_ClearITPendingBit(this->PORT,USART_IT_TXE); - бессмысленная операция. (как я уже писал выше, этот флаг не сбрасывается программно, он read-only).
USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); - лучше перенести в ветку else.
Ну а вообще, OS::channel делает всё то же самое, так что это в некотором роде "закат солнца вручную":)
Цитата(abutorin @ Jan 19 2013, 00:52) *
канал больше предназначен для межпроцессного взаимодействия а не между процессом и прерыванием.

Можете обосновать это утверждение?
abutorin
Цитата(AHTOXA @ Jan 19 2013, 08:43) *
USART_ClearITPendingBit(this->PORT,USART_IT_TXE); - бессмысленная операция. (как я уже писал выше, этот флаг не сбрасывается программно, он read-only).

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

Цитата(AHTOXA @ Jan 19 2013, 08:43) *
USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); - лучше перенести в ветку else.

В какую ветку? Очистка флага нужно всегда когда прерываниеуже обработано.
Цитата(AHTOXA @ Jan 19 2013, 08:43) *
Ну а вообще, OS::channel делает всё то же самое, так что это в некотором роде "закат солнца вручную":)

Можете обосновать это утверждение?


У OS::channel есть дополнительные члены для хранения списка производителей и потребителей, у него более крупная процедура получения первого элемента. Это все годится для межпроцессного взаимодействия, в моем случае все проще и статичнее.
AHTOXA
Цитата(abutorin @ Jan 19 2013, 23:19) *
В какую ветку? Очистка флага нужно всегда когда прерываниеуже обработано.

Код
            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);  // << вот сюда
            }
            // USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); // << отсюда.

Всё же почитайте документацию, использование библиотек не отменяет необходимости её читать. Тогда всё будет работать как надо.
Цитата(abutorin @ Jan 19 2013, 23:19) *
У OS::channel есть дополнительные члены для хранения списка производителей и потребителей, у него более крупная процедура получения первого элемента. Это все годится для межпроцессного взаимодействия, в моем случае все проще и статичнее.

Взамен этих членов вы завели пару штук OS::TEventFlag (TxNotFull, RxNotEmpty). Это явно тяжелее. С остальным тоже не согласен. Но это, конечно, дело вкуса.
abutorin
Цитата(AHTOXA @ Jan 19 2013, 21:42) *
Код
            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);  // << вот сюда
            }
            // USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); // << отсюда.

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

Взамен этих членов вы завели пару штук OS::TEventFlag (TxNotFull, RxNotEmpty). Это явно тяжелее. С остальным тоже не согласен. Но это, конечно, дело вкуса.

В случае использования OS::channel в какой момент будет вызыватся перепланировка процессов?
_Артём_
Цитата(abutorin @ Jan 19 2013, 19:56) *
В случае использования OS::channel в какой момент будет вызыватся перепланировка процессов?

Если речь о прерываниях, то перепланировка вызовется в ISR_Exit в любом случае.
abutorin
Цитата(AHTOXA @ Jan 19 2013, 21:42) *
Код
            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);  // << вот сюда
            }
            // USART_ClearITPendingBit(this->PORT,USART_IT_RXNE); // << отсюда.

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

Взамен этих членов вы завели пару штук OS::TEventFlag (TxNotFull, RxNotEmpty). Это явно тяжелее. С остальным тоже не согласен. Но это, конечно, дело вкуса.

Решил последовать вашим рекомендациям и проверить оба варианта, свой и с использовапние канала

Код
template <typename _Tp,typename BufSizeType,BufSizeType TxbufSize,BufSizeType RxbufSize>
class usart
{
private:
    OS::channel<_Tp, TxbufSize, BufSizeType> TxPool;
    OS::channel<_Tp, RxbufSize, BufSizeType> RxPool;

    bool RxBufferOverflow;

public:

    USART_TypeDef * PORT;

    INLINE bool overflow ()
    {
        return RxBufferOverflow;
    }

    bool open (bool)
    {
        USART_ITConfig(PORT, USART_IT_RXNE, ENABLE);
        return true;
    }

    INLINE void it_handler ()
    {
        OS::TISRW ISRW;

        if (USART_GetITStatus(PORT,USART_IT_TXE) != RESET)
        {
            if(TxPool.get_count())
            {
                _Tp data;
                TxPool.pop(data);
                USART_SendData(PORT,data);
            }
            else
            {
                USART_ITConfig(PORT, USART_IT_TXE, DISABLE);
            }
        }

        if (USART_GetITStatus(PORT,USART_IT_RXNE) != RESET)
        {
            if (RxPool.get_free_size())
            {
                RxPool.push(USART_ReceiveData(PORT));
            }
            else
            {
                RxBufferOverflow = true;
                USART_ClearITPendingBit(PORT,USART_IT_RXNE);
            }
        }

    }

    void send(const _Tp & data)
    {
        TxPool.push(data);
        USART_ITConfig(PORT, USART_IT_TXE, ENABLE);
    }

    _Tp get ()
    {
        _Tp data;
        RxPool.pop(data);
        return data;
    }
};

Из того что не делал ранее это добавил в обработчик прерывания OS::TISRW. С таким кодом передача работает нормально, а вот при попытке принять данные камень зависает. Сразу должен заметить что размер кода увеличился на 176 байт. Размер занимаемой RAMна 4 байта.
AHTOXA
Цитата(abutorin @ Jan 20 2013, 00:42) *
С таким кодом передача работает нормально, а вот при попытке принять данные камень зависает.

У меня такой вариант работает нормально. Наверное какая-то мелочь осталась незамеченной. Например, забыли включить приёмsm.gif
Цитата(abutorin @ Jan 20 2013, 00:42) *
Сразу должен заметить что размер кода увеличился на 176 байт. Размер занимаемой RAMна 4 байта.

Ну, это мелочиsm.gif
abutorin
Цитата(AHTOXA @ Jan 19 2013, 23:12) *
У меня такой вариант работает нормально. Наверное какая-то мелочь осталась незамеченной. Например, забыли включить приёмsm.gif

Весь остальной код остался неизменный. Менял только код данного класса. Идея использовать готовый код для решения своих задач мне всгда нравится, я потому и поднял эту тему. После того как не смог с вашей помощью решить поставленную задачу попробовал сделать свой вариант. В результате оказалось что мой вариант получается несколько компактнее. Для законченности все же остается понять почему не работает.
Вы какого размера пакеты отправляет и посылает? Я в свое время заметил что такой код работает нормально если посылаешь и принимаешь пакет меньше чем размер буфера то проблем нет. А вот как только объем больше то и происходят зависания.
AHTOXA
Насчёт компактности - думаю, что случайно так вышло. По ОЗУ должен быть паритет (У каждого EventFlag есть TProcessMap и TValue, а у каждого channel - два TProcessMap). А по коду - если вы будете использовать channel где-то ещё (а вероятность этого ~100%), то этот код всё равно добавится.
С другой стороны, код у channel проверенный в плане межпроцессного взаимодействия (моменты запрещения прерываний, ожидание, и проч.). А с самодельным вариантом может всплыть что-то.
Почему не работает - неясно. Вы как-то не совсем внятно объясняете, что именно не работает. Приём одного байта - работает?
Тот код, который вы показали, содержит только функции записи/чтения одного символа. А вы пишете, что зависает при приёме блока.
abutorin
Цитата(AHTOXA @ Jan 19 2013, 23:36) *
А по коду - если вы будете использовать channel где-то ещё (а вероятность этого ~100%), то этот код всё равно добавится.

Если параметры шаблано отличаются, то код начинает дублироватся. Так что тут код всегда будет больше.

Цитата(AHTOXA @ Jan 19 2013, 23:36) *
С другой стороны, код у channel проверенный в плане межпроцессного взаимодействия (моменты запрещения прерываний, ожидание, и проч.). А с самодельным вариантом может всплыть что-то.

Неверное использование проверенного кода тоже может вызвать проблемы.

Цитата(AHTOXA @ Jan 19 2013, 23:36) *
Почему не работает - неясно. Вы как-то не совсем внятно объясняете, что именно не работает.

Есть некий проект (кода много чтобы выкладывать), пробую использовать оба варианта класса который описывал. Еще раз повторю, для чисторы эксперимента меняю только код данного класса, все остальное остается прежним. Так вот при использовании канала похоже чтокамень на чемто зависает, сказать где не могу, знаю только что процесс с наивысшим приоритетом перестает работать.
Цитата(AHTOXA @ Jan 19 2013, 23:36) *
Приём одного байта - работает?

По одному байту не использую.

Цитата(AHTOXA @ Jan 19 2013, 23:36) *
Тот код, который вы показали, содержит только функции записи/чтения одного символа. А вы пишете, что зависает при приёме блока.


Блок передаю в цикле по одному байту.
AHTOXA
Цитата(abutorin @ Jan 20 2013, 02:01) *
Если параметры шаблано отличаются, то код начинает дублироватся. Так что тут код всегда будет больше.

А если не отличаются, то не начнёт. Значит - не всегдаsm.gif
Ну да ладно, в любом случае мне плевать на такой прирост кода.
Цитата(abutorin @ Jan 20 2013, 02:01) *
Неверное использование проверенного кода тоже может вызвать проблемы.

Поэтому я и заинтересовался. Если есть проблемы с использованием OS::channel в прерываниях, то я очень хочу знать про них.
Цитата(abutorin @ Jan 20 2013, 02:01) *
Так вот при использовании канала похоже чтокамень на чемто зависает, сказать где не могу, знаю только что процесс с наивысшим приоритетом перестает работать.

А увеличение размера буфера больше размера максимального пакета избавляет от зависаний?
abutorin
Цитата(AHTOXA @ Jan 20 2013, 00:21) *
А увеличение размера буфера больше размера максимального пакета избавляет от зависаний?

Да.
Я писал про это на второй странице помоему.
AHTOXA
Ну, с тех пор что-то же поменялось. Так что это было неочевидно.
В общем, я не знаю, чем помочь. Может быть вы сделаете минимальный проект, где проблема проявляется, и выложите его?
abutorin
Цитата(AHTOXA @ Jan 20 2013, 09:31) *
В общем, я не знаю, чем помочь. Может быть вы сделаете минимальный проект, где проблема проявляется, и выложите его?

Тогда пока предлагаю отложить этот вопрос.
Резюме по теме можно сделать такое:
По мнению разработчиков OS:channel можно использовать в прерываниях с некоторыми замечаниями "обязательна проверка на наличие свободного места или данных для для получения из него".
Лично у меня использовать его не получилось по неустановленным причинам. На мой субъективный взгляд использование OS:channel является более накладным (по размеру кода) чем использование собсвтенного кольцевого буфера (из библиотеки usrlib) и двух OS::TEventFlag.

PS. забыл как на форуме можно подправить название темы, чтобы вписать в нее "решено"
сарматъ
я так и не понял, а функции типа push_isr() не нужны?и если не нужны то почему?
_Артём_
Цитата(сарматъ @ Sep 4 2013, 23:26) *
я так и не понял, а функции типа push_isr() не нужны?и если не нужны то почему?


Не нужны.


Код
void OS::TChannel::push(uint8_t x)
{
    TCritSect cs;

    while (!Cbuf.get_free_size())
    {
        // channel is full, suspend current process
        suspend(ProducersProcessMap);
    }

////.....


В случае, если кончилось место для записи в фифо, процесс который пишет в буфер засыпает (channel is full, suspend current process). Если запись происходит в прерывании и место кончилось, то кого усыплять-то? Такая ситуация говорит о том что либо буфер мал, либо данные поступают слишком быстро, либо данные читаются слишком медленно.




сарматъ
там могло бы быть что либо такого плана

CODE
template<typename T, uint16_t Size, typename S>
void OS::channel<T, Size, S>::push_isr(const T& item)
{
  TCritSect cs;
if(pool.get_free_size_isr())
{
pool.push_back_isr(item);
resume_all_isr(ConsumersProcessMap);
}
}
Сергей Борщ
QUOTE (сарматъ @ Sep 5 2013, 07:39) *
там могло бы быть что либо такого плана
Всего предусмотреть невозможно. Но выход есть:
CODE
template<typename T, uint16_t Size, typename S = uint8_t>
class my_channel_with_isr : public channel<T, Size, S>
{
public:
      void push_isr(const T& item);
}
сарматъ
да я уже подумал над этим правда придется не наследовать а копировать шаблон - чтоб в него поместить уже наследника от ring_buffer иначе внутри my_channel_with_isr будет два объекта типа ring_buffer, а если править непосредственно текст системы то совместимость пропадет(
dxp
QUOTE (сарматъ @ Sep 5 2013, 13:24) *
да я уже подумал над этим правда придется не наследовать а копировать шаблон - чтоб в него поместить уже наследника от ring_buffer иначе внутри my_channel_with_isr будет два объекта типа ring_buffer, а если править непосредственно текст системы то совместимость пропадет(

Почему два ring_buffer? Наследование не размножает потроха предка.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.