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

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

USART_ClearITPendingBit(this->PORT,USART_IT_TXE); Это очистка признака возникновения прерывания, в у STM32 большинство битов (признаков) прерывания необходимо очищать вручную.
К сожалению я использую для передачи DMA, поэтому у меня нет примера для UART c каналом. Без ОС рабочий код у меня выглядит так:
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;
    }
}

Как видите, никаких ручных очисток.

 

 

А в какой момент происходи включение прерываний? в suspend я не нашел включение прерываний.
Из suspend() вызывается TKernelAgent::reschelule(), который в свою очередь вызывет TKernel::shed(), в котором есть такой код:
        do
        {
            enable_context_switch();
            DUMMY_INSTR();
            disable_context_switch();
        } 
        while(CurProcPriority != SchedProcPriority); // until context switch done

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

 

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


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

    Status &= pUART->CR1;   // mask disabled ints

А это зачем? Чтобы не реагировать на RXNE и TXE при маскировке этих прерываний?

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


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

А это зачем? Чтобы не реагировать на RXNE и TXE при маскировке этих прерываний?
Да, на все запрещенные. В данном случае на TXE. RXNE у меня не запрещается.

 

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


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

К сожалению я использую для передачи DMA, поэтому у меня нет примера для UART c каналом.

У меня логика построена на том, что я с уартом работаю как с потоком байт, я не знаю сколько байт отправляюи сколько получу. Если даже с таким подходом это можно реализовать через DMA то был бы очень признателен за пример.

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

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


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

Да, на все запрещенные. В данном случае на TXE. RXNE у меня не запрещается.

У меня такого нет, и работает нормально. Это что получается, я при запрещённых прерываниях TXE всё равно каждый раз при входе в прерывание влетаю в эту ветку обработчика?

Проверил - точно, влетаю. Неаккуратненько...

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


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

Если с прерываниями стало ясно.то опять возникает вопрос в чем же тогда проблема?
Вы заводите в начале обработчика объект типа OS::TISRW (я в предыдущем сообщении описался, указав его как OS::TCritSect)? Без него прерывание не сможет при необходимости вызвать перепланирование.

 

У меня логика построена на том, что я с уартом работаю как с потоком байт, я не знаю сколько байт отправляюи сколько получу. Если даже с таким подходом это можно реализовать через DMA то был бы очень признателен за пример.
Как-то так, хотя мне самому этот код не очень нравится:

//****  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. Если же передается готовая строка из флеша или ОЗУ - буфер не используется.

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


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

Как-то так, хотя мне самому этот код не очень нравится:

 

Да, я понял. Но мои вопросы этот метод нерешит т.к. посылка делается по одному байту (как поток), тут изначально нельзя знать какой объем займет посылка. Плохо что DMA не умеет работать как кольцевой буфер. Тогда хотябы можно было накапливать данные и отсылать по таймауту в случае отсутствия новых.

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


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

Плохо что DMA не умеет работать как кольцевой буфер.
Некое подобие можно сформировать. Укладывать данные в кольцевой буфер, а по таймауту или в прерывании окончания DMA перезагружать регистры DMA на очередной готовый участок этого буфера.

 

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


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

Некое подобие можно сформировать. Укладывать данные в кольцевой буфер, а по таймауту или в прерывании окончания DMA перезагружать регистры DMA на очередной готовый участок этого буфера.

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

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


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

Насчёт работы с UART по DMA вот вам три темы:

Там немножко разные подходы, выберите подходящий.

Я делал по примеру от kan35 в конце третьей темы, работает.

 

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


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

Насчёт работы с UART по DMA вот вам три темы:

Там немножко разные подходы, выберите подходящий.

Я делал по примеру от kan35 в конце третьей темы, работает.

Спасибо, понял что нужно копать в направлении режима cilcularmode DMA. Попробую потом на досуге, сейчас сделал прием и передачу через прерывания. Код передачи выкладывал выше, прием сделал по образу и подобию, На ненагруженном камне прием уверенный на скорости в 0,5 мбит/с

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


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

Спасибо, понял что нужно копать в направлении режима cilcularmode DMA.
Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор.

сейчас сделал прием и передачу через прерывания.
А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал :)

 

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


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

Ой. Вам ведь не нужно, чтобы УАСПП постоянно повторял содержимое всего буфера, включая мусор.

 

Для приемника:

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

Для передатчика:

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

 

А в чем причина была, почему не работало? Обычно принято сообщать - чтобы тот, кто будет потом искать решение своей подобной проблемы нашел ответ, да и отвечавшим вам тоже любопытно - кто из них угадал :)

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

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


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

Как обещал ранее выкладываю код который у меня работает.

 

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

Отказ от канала в результате вызван тем, что канал больше предназначен для межпроцессного взаимодействия а не между процессом и прерыванием. Инициализацию порта необходимо выполнять самостоятельно. Сразу хочу заметить что данная реализация сделана на коленке и имеет достаточный потенциал для улучшения, сразу видно что целесообразно сделать данный класс через шаблон со статичными членами, с указанием порта как параметра и использовать данные методы без создания экземпляра класса.

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


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

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

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

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

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

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

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

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

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

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