ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Пятница
22 ноября
1208268 Топик полностью
VladislavS. (13.05.2022 22:42, просмотров: 504) ответил Dingo на Хочется универсального. Как бы сделать выбор значений для инициализации пинов под периферию для stm32f407 ?
Я себе делал библиотеку классов для работы с GPIO. Хардкорненько получается. 

1. Каждый пин это шаблонный класс, который знает идентификатор и тип своего GPIO-порта, свою позицию в порту, свою альтернативную функцию и начальное состояние. И умеет базовые действмия типа set, clear, write, read, toggle, mode выполнять через свой GPIO-порт.


2. GPIO-порт знает как на контроллере выполнять основные действия, знает начальное состояние порта при включении, содержит маску задействованных в нём пинов. Умеет делать множество архитектурнозависимых оптимизаций для операций с портом.


3. Класс для задания режимов работы порта. Он собирает в единое список пинов, портов, других списков. Группирует их по портам. Проверяет список на дублирование. Вычисляет все незадействованные пины и задаёт им значение по умоланию. Вычисляет для каждого порта какие из регистров для задания режимов надо менять, а какие нет. Опционально, исключает из модифицируемых регистров те, значения которых при включении находятся в нужном значении.


Процесс конфигурации начинается с "обзывания" пинов согласно схеме. Так как пины это типы, то в ход пускаем using.


Затем прописываем режимы и начальные состояния с помощью списков типов. Собираем списки в один и вызываем метод задания конфигурации.



Листинг установки режимов и состояний 32 ног контроллера STM32G431 из кода с картинки. Можно обратить внимание, что нет ни одной операция "чтение-модификация-запись", далеко не во все регистры произведена запись, использованы 32, 16 и 8-битные записи в регистры. Все вычисления и оптимизации сделаны на этапе компиляции.

MOV      R1,#+1207959552
LDR.N    R2,??__low_level_init_0+0x30
STR      R2,[R1, #+0]   
MOVS     R3,#+36        
STRB     R3,[R1, #+15]  
MOVS     R2,#+16        
LDR.W    R12,??__low_level_init_0+0x34
STR      R12,[R1, #+8]  
STRB     R2,[R1, #+34]  
MOV      R3,#+50529027  
STR      R3,[R1, #+24]  
MOVW     R2,#+39613     
STRH     R2,[R1, #+1024]
MOVS     R3,#+0         
STRB     R3,[R1, #+1037]
MOVW     R2,#+53187     
STRH     R2,[R1, #+1032]
LDR.N    R3,??__low_level_init_0+0x38
STR      R3,[R1, #+1056]


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


То что пины и списки пинов это типы, позволяет строить из них библиотеку шаблонных классов. Например, примитивный класс светодиода. Описываем что он умеет: вкл, выкл, поменять состояние, прочитать состояние. Описываем это один раз, задавая пин шаблонным параметром. При этом как в железе будет этот пин меняться нас не волнует совсем, хоть по радиоканалу, компилятор сам всё сделает при инстанцировании.

template <typename TPin, bool invert=false>
struct TLed final
{  
  static inline void On()  { invert ? TPin::clear() : TPin::set(); }
  static inline void Off() { invert ? TPin::set() : TPin::clear(); }
  static inline void Toggle() { TPin::toggle(); }
  static inline bool Read() { return invert ? !TPin::read() : TPin::read(); }
  inline TLed& operator=(bool state)
  {
    if(state) On(); else Off();
    return *this;
  }
  inline operator bool() const { return Read(); }
};

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

// Статический метод шаблонного класса
static inline void Init(SPI_BR PRESC, CPOL CPOL_=CPOL::_0, CPHA CPHA_=CPHA::_0)
{
  base()->CR2 = _VAL2FLD(SPI_CR2_DS,8-1) | SPI_CR2_FRXTH;
  base()->CR1 = _VAL2FLD(SPI_CR1_BR,PRESC) | SPI_CR1_MSTR | SPI_CR1_SPE | SPI_CR1_SSI | SPI_CR1_SSM | uint32_t(CPOL_) | uint32_t(CPHA_);
}

//Вызов в любом месте программы заинлайнится
SPI::spi3::Init(SPI::SPI_BR::PCLK_DIV2, SPI::CPOL::_0, SPI::CPHA::_1);
        MOV      R2,#+5888      
        LDR.N    R1,??__low_level_init_0+0x3C
        STR      R2,[R1, #+4]   
        MOVW     R3,#+837       
        STR      R3,[R1, #+0]