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

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

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

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

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

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


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

12 minutes ago, Serhiy_UA said:

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

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

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

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

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


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

16 минут назад, Serhiy_UA сказал:

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

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

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


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

На примере драйвера 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 таймеров). И конструировать такой класс логично фабрикой.

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


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

9 minutes ago, haker_fox said:

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

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

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

 

 

 

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


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

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

 

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

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

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

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


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

1 hour ago, Forger said:

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

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

1 hour ago, Forger said:

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

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

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


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

10 minutes ago, haker_fox said:

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

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

Quote

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

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

 

 

to ТС:

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

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

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

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

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


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

48 minutes ago, Forger said:

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

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

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

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


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

1 minute ago, k155la3 said:

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

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

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

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


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

1 hour ago, Forger said:

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

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

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

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

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

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

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

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


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

9 minutes ago, Serhiy_UA said:

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

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

 

9 minutes ago, Serhiy_UA said:

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

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

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

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

 

9 minutes ago, Serhiy_UA said:

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

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

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

 

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


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

5 hours ago, Serhiy_UA said:

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

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

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

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


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

6 hours ago, Serhiy_UA said:

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

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

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


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

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

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

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

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

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

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

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

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

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