Jump to content

    
Serhiy_UA

Ввод спец.регистров UART в классы C++ для STM32

Recommended Posts

Работаю с STM32F103C8 в IAR на С++. Создаю класс, общий для всех трех UART данного контроллера. С функциями-членами и обращениями к создаваемым в ОЗУ переменным-членам класса все работает. А вот как в функциях класса обращаться непосредственно к специальным (аппаратным) регистрами UART?

В частности хочу создать в классе функцию UARTx::println(char* text) для вывода на монитор ПК. Здесь попытка обращения, например, к спец.регистру USART->DR одного из UART сразу вызывает ошибку. С вводом/выводом и работой на монитор с каждым отдельным UART проблем нет. Просто хочу обобщить все три UART в одном классе.  

Может, кто подскажет?

Share this post


Link to post
Share on other sites
12 minutes ago, Serhiy_UA said:

Создаю класс, общий для всех трех UART данного контроллера

Какой в этом смысл?

Логично на каждый UART создать свой класс, например, наследовав его от некого базового класса, обладающего базовым набором функционала, свойственного всем трем юартам.

При наследовании (или создании экземпляра) передается указатель на базовый адрес нужно юарта.

Share this post


Link to post
Share on other sites
16 минут назад, Serhiy_UA сказал:

В частности хочу создать в классе функцию UARTx::println(char* text) для вывода на монитор ПК. Здесь попытка обращения, например, к спец.регистру USART->DR одного из UART сразу вызывает ошибку.

Вы можете завести в классе UART поле USART_TypeDef* uart, и инициализировать его в конструкторе.

Share this post


Link to post
Share on other sites

На примере драйвера SPI (смысл тот же).

В хидере в private есть такая структура

class SspBus : public AbstractSSPDriver {
public:
    enum class Bus : std::uint8_t {
        One,
        Two,
        nLast,
    };
    static SspBus &instance( Bus bus, Setup_t setup ) {
        configASSERT(bus != Bus::nLast);
        static SspBus ssp_obj[] = {
            SspBus(Bus::One, setup),
            SspBus(Bus::Two, setup),
        };
        const auto idx = static_cast<int>(bus);
        configASSERT(idx < ARRAY_SIZE(ssp_obj));
        return ssp_obj[idx];
    }
    TRetVal setup( Setup_t setup ) override;
    TRetVal open( const void * param = NULL );
    TRetVal txrx( AbstractSSPDriver::MsgType &spiCfg );
    TRetVal writeb( std::uint8_t byte );
    static void flush();
    static void irqHandler1() {
        irqHandler(Bus::One);
    }
    static void irqHandler2() {
        irqHandler(Bus::Two);
    }
private:
    static const auto BUS_NUM = static_cast<int>(Bus::nLast);    
    struct ControlData {
        IRQn_Type irqn;
        SPI_TypeDef * const pRegs;
        MsgType * pMsg;
        TaskHandle_t tskHndl;
        size_t rxcounter;
    };
    Bus m_hdwNum;
    Setup_t m_setup;
    static ControlData m_ctrlData[BUS_NUM];

    SspBus( Bus bus, Setup_t setup );
    SspBus( const SspBus & ) = delete;
    SspBus( const SspBus && ) = delete;
    void gpioConfig();
    void disableNvicIrq();
    void enableNvicIrq();
    std::uint32_t getBaudRateBits() const;
    std::uint32_t getModeBits() const;
    TRetVal setupDma();
    TRetVal setupIrq();
    TRetVal disableDma();
    TRetVal disableIrq();
    auto getBusIdx() const {
        return static_cast<size_t>(m_hdwNum);
    }
    static void irqHandler( Bus bus );
};

В сишнике вверху есть такое

SspBus::ControlData SspBus::m_ctrlData[BUS_NUM] = {
    {
        .irqn   = SPI1_IRQn,
        .pRegs  = SPI1,
    },
    {
        .irqn = SPI2_IRQn,
        .pRegs  = SPI2,
    }
};

Прерывание, например, устроено так

void SspBus::irqHandler( Bus bus ) {
    BaseType_t xRecTaskWoken = pdFALSE;
    const auto idx = static_cast<int>(bus);
    configASSERT(idx < ARRAY_SIZE(m_ctrlData));
    auto &ctrl = m_ctrlData[idx];
    const auto status = ctrl.pRegs->SR;
    if (status & ( SPI_SR_OVR | SPI_SR_CRCERR | SPI_SR_MODF )) {
        // Для мастера нужно только обработать OVR и CRCERR
        configASSERT(0);
    }
    while (( ctrl.pRegs->SR & SPI_SR_FTLVL ) != SPI_SR_FTLVL && ctrl.pMsg->length) {
        static const std::uint8_t DUMMY_TXDATA = 0x77;
        if (ctrl.pMsg->txbuffer)
            ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) ) = *ctrl.pMsg->txbuffer++;
        else
            ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) ) = DUMMY_TXDATA;
        if (--ctrl.pMsg->length == 0) {
            ctrl.pRegs->CR2 &= ~SPI_CR2_TXEIE;
        }
        while (ctrl.pRegs->SR & SPI_SR_FRLVL) {
            if (ctrl.pMsg->rxbuffer && ctrl.rxcounter) {
                *ctrl.pMsg->rxbuffer++ = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) );
                if (--ctrl.rxcounter == 0) {
                    ctrl.pRegs->CR2 &= ~SPI_CR2_RXNEIE;
                }
            } else
                volatile auto dummy = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) );
        }
    }
    while (ctrl.pRegs->SR & SPI_SR_FRLVL) {
        if (ctrl.pMsg->rxbuffer && ctrl.rxcounter) {
            *ctrl.pMsg->rxbuffer++ = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) );
            if (--ctrl.rxcounter == 0) {
                ctrl.pRegs->CR2 &= ~SPI_CR2_RXNEIE;
            }
        } else
            volatile auto dummy = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) );
    }
    if (ctrl.rxcounter == 0 || ( ctrl.pMsg->length  == 0 && ctrl.pMsg->rxbuffer  == nullptr )) {
        vTaskNotifyGiveFromISR(ctrl.tskHndl, &xRecTaskWoken);
        portYIELD_FROM_ISR(xRecTaskWoken);
    }
}

Принци, думаю, понятен)

58 minutes ago, Forger said:

Логично на каждый UART создать свой класс

А зачем? Это же куча писанины. При этом периферия, обычно не сильно отличается. Я имею в виду одноимённую периферию (3 uart, 2 spi, 5 таймеров). И конструировать такой класс логично фабрикой.

Share this post


Link to post
Share on other sites
9 minutes ago, haker_fox said:

А зачем? Это же куча писанины.

Не правда. Никакой "кучи писанины" тут нет. Речь шла про экземпляры класса, а не их реализацию.

Описать/реализовать же нужно всего ОДИН класс с базовым функционалом, свойственным всем юартам.

 

 

 

Share this post


Link to post
Share on other sites

Как это сделано у меня:

 

1. Создаем экземпляр нужного порта

Spoiler

	class Thread : public OS::Thread<kStackSize>
	{
....
	private:
....
		Hardware::SerialPortUSART6 serialPort; // Экземпляр нужного порта USART
		Hardware::Pin<PC6> pinTX;
		Hardware::Pin<PC7> pinRX;
      
		void serialPortIrqVector(); // Обработчик прерываний по приему байтов от USART
.....
	};

 

 

2. Настраиваем порт, подключаем обработчик прерываний и запускаем

Spoiler

	serialPort.setPinTx(pinTX);
	serialPort.setPinRx(pinRX);
	
	auto vector = DELEGATE(&Thread::serialPortIrqVector, this);
	serialPort.initialize();
	serialPort.installVector(vector);
	serialPort.setModeAsynchronous();
	serialPort.setStopBits1_0();
	serialPort.setParityNone();
	serialPort.setFrameSizeTo8bits();
	serialPort.setOversamplingModeTo16bits();
	serialPort.setBaudRate(GPS_PORT_BAUDRATE);
	serialPort.irq.rxNotEmpty.enable();
	serialPort.run();

 

 

3. Реализуем обработчики прерываний

Spoiler

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void xxxxxxxx ::serialPortIrqVector()
{
	if (serialPort.irq.rxNotEmpty.isPending())
	{
		rxByte = serialPort.read();
 ......
		semaphoreByteRx.signal();
	}
  
 .....
}

 

 

 

 

 

Библиотечная часть (общая для всех проектов на выбранное семейство)

USART.hpp:

Spoiler

class SerialPort
{
  public:
  SerialPort(uint32_t baseAddr, Interrupt::Index irqIndex, PinAlternativeFunction altFunction);
  virtual ~SerialPort();

  using BaudRate = uint32_t;

  void initialize();	
  void installVector(stm32f4::Interrupt::Vector& vector);

  void run();
  void stop();

  void setBaudRate(BaudRate);

  void setPinTx(const AbstractPin&);
  void setPinRx(const AbstractPin&);
  void setPinCTS(const AbstractPin&);
  void setPinRTS(const AbstractPin&);

  void setModeAsynchronous();
  void setModeSynchronous();

  .....
};

.............
  
  
  class SerialPortUSART1 : public SerialPort
  {
    public:
    SerialPortUSART1() : SerialPort(USART1_BASE, USART1_IRQn, AF_USART1) { }

    struct 
    {
      Irq<USART1_BASE, USART_CR1_PCE, USART_SR_PE> 		parityError;
      Irq<USART1_BASE, USART_CR1_IDLEIE, USART_SR_IDLE> 	idleDetected;
      Irq<USART1_BASE, USART_CR1_RXNEIE, USART_SR_RXNE> 	rxNotEmpty;
      Irq<USART1_BASE, USART_CR1_TCIE, USART_SR_TC> 		txComplete;
      Irq<USART1_BASE, USART_CR1_TXEIE, USART_SR_TXE> 	txEmpty;
      .....
    } irq;
  };

.....
  
  class SerialPortUART5 : public SerialPort
  {
    public:
    SerialPortUART5() : SerialPort(UART5_BASE, UART5_IRQn, AF_UART5) {}

    struct 
    {
      Irq<UART5_BASE, USART_CR1_PCE, USART_SR_PE> 		parityError;
      Irq<UART5_BASE, USART_CR1_IDLEIE, USART_SR_IDLE> 	idleDetected;
      Irq<UART5_BASE, USART_CR1_RXNEIE, USART_SR_RXNE> 	rxNotEmpty;
      Irq<UART5_BASE, USART_CR1_TCIE, USART_SR_TC> 		txComplete;
      Irq<UART5_BASE, USART_CR1_TXEIE, USART_SR_TXE> 		txEmpty;
      .....
    } irq;
  };

 

 

В USART.cpp находится реализация базового класса SerialPort, все возможные отличия (если они есть) различных юсартов друг от друга скрыты внутри.

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

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

Share this post


Link to post
Share on other sites
1 hour ago, Forger said:

Описать/реализовать же нужно всего ОДИН класс с базовым функционалом, свойственным всем юартам

Так автор топика, как я понимаю, это и делает.

1 hour ago, Forger said:

Речь шла про экземпляры класса, а не их реализацию.

Простите, я вас просто не понял в первый раз) без обид)

Share this post


Link to post
Share on other sites
10 minutes ago, haker_fox said:

Так автор топика, как я понимаю, это и делает.

я с ходу подумал, что речь идет про один класс на все три юарта сразу: 

Quote

общий для всех трех UART данного контроллера

Ну, типа один контейнер для всех юартов сразу. 

 

 

to ТС:

Не совсем понятно, речь идет про:

1. некие косяки в коде или

2. это был обобщенный вопрос по методологии проектирования в целом?

Если первое, то код в студию. На второе - ответы прозвучали.

Share this post


Link to post
Share on other sites
48 minutes ago, Forger said:

я с ходу подумал, что речь идет про один класс на все три юарта сразу: 

Именно это и следует из формулировки ТС. Если не подключать толкования.

Хотя есть шанс, что это и требуется, например для какого-то хитрого коммутатора, где в одном объекте (экземпляре) сосуществуют 3 канала.

Share this post


Link to post
Share on other sites
1 minute ago, k155la3 said:

Именно это и следует из формулировки ТС.

Вот отсюда и возникает логичный вопрос: зачем?

Впрочем, судя по очень высокой активности ТС в его же теме, этот вопрос переходит в разряд риторических :)

Share this post


Link to post
Share on other sites
1 hour ago, Forger said:

Не совсем понятно, речь идет про:

1. некие косяки в коде или

2. это был обобщенный вопрос по методологии проектирования в целом?

Если первое, то код в студию. На второе - ответы прозвучали.

Благодарен за информацию. Для наработки заделов, имею работающую связку ПК + STM32 + GSM_SIM800L +  GPS_Neo-M8, где задействованы три UART, все работает. Захотелось улучшить код за счет одного объединенного класса, используя основное преимущество С++, это сокращать размер кода. У меня сейчас есть общий класс с функциями и набором данных, который все UART при необходимости используют, но там нет обращений к аппаратуре UART-ов. Есть также отдельные функции для каждого UART, уже с обращением к аппаратным регистрам, ну и у каждого UART свой собственный обработчик прерываний.

Мне казалось, что уже разработаны методы для подобной ситуации, поэтому и мои вопросы. Идея с наследованиями мне понравилась, надо попробовать. Как мне кажется, аналогичный класс есть в семействе arduino, но там можно создавать только софт UARTы, хотя это несколько иное. Наверное и на ПК есть одинаковые аппаратно независимо работающие узлы, и там возможно как-то это все объединяют в классах.... 

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

Share this post


Link to post
Share on other sites
9 minutes ago, Serhiy_UA said:

Захотелось улучшить код за счет одного объединенного класса, используя основное преимущество С++, это сокращать размер кода.

Не это - его основное преимущество, вовсе не это ))

 

9 minutes ago, Serhiy_UA said:

Захотелось улучшить код за счет одного объединенного класса

Это происходит сверху вниз, а не снизу вверх )) 

Другими словами, сначала следует спроектировать сам проект, а потом уже углубляться в его недры ))

Более того, коли проект работает, то лучше его не трогать. Тем более инструментарием, в котором опыта по свей видимости еще малова-то ) Без обид ))

 

9 minutes ago, Serhiy_UA said:

Мне казалось, что уже разработаны методы для подобной ситуации

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

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

 

Share this post


Link to post
Share on other sites
5 hours ago, Serhiy_UA said:

. . . В частности хочу создать в классе функцию UARTx::println(char* text) для вывода на монитор ПК. . . .

Как Вы предполагаете использовать этот класс, приведите пример создания (вызов констукторов), и пару строк - как предполагаете использовать.

Можете выложить код (пусть даже нерабочий и черновой) реализации UARTx::println(char* text) и объявления класса (то что UARTxClass.h). 

Share this post


Link to post
Share on other sites
6 hours ago, Serhiy_UA said:

Здесь попытка обращения, например, к спец.регистру USART->DR одного из UART сразу вызывает ошибку

Ты забыл написать, какую именно ошибку это вызывает.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.