Перейти к содержанию
    

Возможность использования OS::channel в прерываниях

Добрый вечер.

Пробую сделать передачу по USART с использование буфера. Для межпроцессорного удобства очень нравится использовать OS::channel. Возможно ли его использовать и в прерываниях? Точнее интересует процедура получения элемента из канала?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Использовать канал в прерывании можно. Учтите только, что если вы в него что-то заталкиваете, а он полон, то будет сделана попытка усыпить прерванный процесс и дождаться освобождения (или при доставании из пустого канала). Вероятно, это не то поведение, которое ожидается в прерывании:)

Поэтому проверяйте наличие свободного места при запихивании чего-то в канал, и наличие данных при доставании из канала.

Примерно вот так:

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне кажется для обмена по интерфейсам связи лучше использовать message, там есть специальный метод для вызова из прерываний. А уже в обычном процессе складывать сообщения в буфер. Поправьте, если я что-то не так сказал, я ещё новичёк.

Изменено пользователем Vasya777

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Использовать канал в прерывании можно. Учтите только, что если вы в него что-то заталкиваете, а он полон, то будет сделана попытка усыпить прерванный процесс и дождаться освобождения (или при доставании из пустого канала). Вероятно, это не то поведение, которое ожидается в прерывании:)

Поэтому проверяйте наличие свободного места при запихивании чего-то в канал, и наличие данных при доставании из канала.

 

Примерно так и делал.

    
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.

Изменено пользователем abutorin

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не знаю почему зависает, наверное что-то неправильно ...

Не пойму, в чём сермяга посать всюду this->?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не знаю почему зависает, наверное что-то неправильно ...

Не пойму, в чём сермяга посать всюду 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, запрет снимается в деструкторе, так вот деструктор не вызовется, т.к. потребитель канала находится в прерывании.

Получается что канал использовать с прерываниями не получится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Объектный подход,

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

 

Посмотрел поглубже, помоему нашел в чем проблема:

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

 

т.к. потребитель канала находится в прерывании.

Хуже того - будет усыплён процесс прерванный прерыванием (Idle например) и наверное не только это .

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблему решил использовав События:

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

    }

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

 

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

если писать this то IDE подставляет выбор членов класса ). Так что this писать зачастую короче чем помнить точное название члена.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

если писать this то IDE подставляет выбор членов класса ). Так что this писать зачастую короче чем помнить точное название члена.

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

IDE у вас какая-то устаревшая.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

IDE у вас какая-то устаревшая.

 

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

 

Второйдовод писать всегда, этоодним взглядом на код понятно что это член класса а не какаянибудь переменная локальная. IDE у меня Eclipse под линуксом. Последнее время работают только с STM32 и использую стандартную библиотеку, до регистров решил не опускатся. Сейчас больше интересует "бизнес-логика" алгоритмов.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

а не какаянибудь переменная локальная.

обычно так делаю

if (local_variable != GlobalVariable)

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

 

IDE у меня Eclipse под линуксом.

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

 

Последнее время работают только с STM32 и использую стандартную библиотеку, до регистров решил не опускатся. Сейчас больше интересует "бизнес-логика" алгоритмов.

 

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

 

Сейчас больше интересует "бизнес-логика" алгоритмов.

Логика у вас... в смысле логика тоже дело...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Судя по коду, push выполняется с запретом прерывания. Как я понимаю работу TCritSect, запрет снимается в деструкторе, так вот деструктор не вызовется, т.к. потребитель канала находится в прерывании.
Прерывания будут разрешены во время suspend() при передаче управления другому процессу. Что касается вашей проблемы - мне кажется причина в том, что вы делаете USART_ClearITPendingBit(this->PORT,USART_IT_TXE); даже если данных не было и ничего не было передано. И когда данные в канале появляются - у процессора нет причин вызвать прерывание и отправить их. Хотя я не очень хорошо помню, как этот механизм (pending) работает в кортексах. Также полагаю, что объект типа OS::TCritSect вы в начале обработчика прерывания создаете. Если нет - это тоже может быть причиной.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прерывания будут разрешены во время 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 я не нашел включение прерываний.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А в какой момент происходи включение прерываний? в suspend я не нашел включение прерываний.

В suspend произойдёт перепланировка (reschedule), запустится на выполнение очередной готовый процесс и где-то в конце этой процедуры будут разрешены прерывания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...