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

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
pokk
Вымогатель припоя
Сообщения: 574
Зарегистрирован: Вт ноя 02, 2010 17:46:37

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

Сообщение pokk »

Добрый день, есть 3 Usart +-1UART как сделать инициализацию этого всего с легким переназвачением выводов.
Сначала сделал все в SPL, но это все растянулось на 3 странице, и при переназначение выводов, начал путаться, что где, потом добавил define что бы все настройки были в header, но все равно не айс получилось пришлось тактирование туда вытаскивать и тд опять настроек на 3 странице, и где-то включаешь тактирование не на той шине Usart, или ремапинк теряешь.

Можно как нибудь сделать что бы все настройки были перед глазами? И не надо было листать вниз на 3 страницы.
Даже применив макросы, стало только, чуть чуть легче
http://we.easyelectronics.ru/blog/STM32/3191.html
Реклама
Мурато Мяуконни
Прорезались зубы
Сообщения: 203
Зарегистрирован: Сб ноя 19, 2016 21:05:48

Re: Настройка, кучи выводов, с быстрым переназначением,Stm32

Сообщение Мурато Мяуконни »

Да очень просто - написать на базовом CMSIS. Вам все три одновременно? Пишите всё друг за другом. Или нужен выбор одного из трех? Напишите драйвер и условной компиляцией (#define USART1 .... ifdef USART1 .... endif и #define USED_USART UASRT1), в котором в блоках ifdef ... endif будут описаны отдельные аппаратно-зависимые компиляции, определяемые конкретным значением дефайна перед использованием.
Подпись убрал вместе с автором. aen
Реклама
_kp
Родился
Сообщения: 6
Зарегистрирован: Вс дек 25, 2016 16:56:37

Re: Настройка, кучи выводов, с быстрым переназначением,Stm32

Сообщение _kp »

С библиотекой SPL это делается примерно так.

Описываем конфигурацию GPIO в массиве (код весьма компактен )

Код: Выделить всё

typedef struct {
     GPIO_TypeDef*    GPIOx;
     GPIO_InitTypeDef Config;
  }gpio_init_t;

gpio_init_t pincfg1[]={

    { GPIOA,{  9 , GPIO_Speed_2MHz, GPIO_Mode_AF_PP         } }, //UART1.TX
    { GPIOA,{ 10 , 0              , GPIO_Mode_IN_FLOATING   } }, //UART1.RX
    { GPIOA,{  2 , GPIO_Speed_2MHz, GPIO_Mode_AF_PP   } }, //LED1
    { GPIOA,{  3 , GPIO_Speed_2MHz, GPIO_Mode_AF_PP   } }, //LED2
    { GPIOC,{ 13 , GPIO_Speed_2MHz, GPIO_Mode_AF_PP   } }, //LED13
    { GPIOA,{ 10 , 0              ,  GPIO_Mode_IPU} }, //KEY1

  };
Инициализируем сразу все нужные GPIO.
Опять же, это можно вынести в функцию и передавать разные таблицы инициализации.

Код: Выделить всё

      //Enable clocking GPIO BEFORE set mode
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |  RCC_APB2Periph_AFIO, ENABLE);

        unsigned size= (sizeof(GPIO_inits1) / sizeof(gpio_init_t)) ; //Число элементов в массиве 
        for( unsigned i=0; i< size ; i++ ){
              
              GPIO_Init(pincfg1[i].GPIOx, &pincfg1[i].Config);
        }

Дополнительно, для работы с битами GPIO можно воспользоваться побитным доступом

Код: Выделить всё

  
//Cortex-M* specific fastest bit access (not supported in ARM7,ARM9?)
 #define DEF_GPIO_BIT(ADDR,BIT)  (*((volatile uint32_t *) (32*(((uint32_t)ADDR)-PERIPH_BASE) + (4*BIT) +PERIPH_BB_BASE) ))

 #define LED1    DEF_GPIO_BIT( &GPIOA->ODR,  2 )
 #define LED2    DEF_GPIO_BIT( &GPIOA->IDR,  3 )
 #define LED13   DEF_GPIO_BIT( &GPIOC->ODR, 12 )
 #define KEY1    DEF_GPIO_BIT( &GPIOB->IDR, 15 )
 #define KEY2    DEF_GPIO_BIT( &GPIOC->IDR, 14 )


После чего можно писать такой код (без всяких битовых масок)

Код: Выделить всё

   LED13 =!LED13;
   LED1  = KEY1;
   if(!KEY2) LED2=0;
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: Настройка, кучи выводов, с быстрым переназначением,Stm32

Сообщение Reflector »

Код: Выделить всё

template<int usart>
class Usart
{
public:
    template<class Tx, class Rx, uint32_t af_tx = 7, uint32_t af_rx = af_tx>
    static void init(uint32_t baudRate)
    {
        Tx::mode(PinMode::AF_PushPull_HighSpeed, af_tx);
        Rx::mode(PinMode::AF_PullUp, af_rx);
        ...
    }
    ...
};

using Usart1 = Usart<USART1_BASE>;
using Usart2 = Usart<USART2_BASE>;
...

Код: Выделить всё

Usart1 usart1;
Usart6 usart6;

usart1.init<PinA<9>, PinA<10>>(115200);
usart6.init<PinC<6>, PinC<7>, 8>(9600);
char a = usart1.get();
Реклама
Эиком - электронные компоненты и радиодетали
_kp
Родился
Сообщения: 6
Зарегистрирован: Вс дек 25, 2016 16:56:37

Re: Настройка, кучи выводов, с быстрым переназначением,Stm32

Сообщение _kp »

Reflector, можно более полный исходник Вашего примера? Или сслылку на подобный подход.
Реклама
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: Настройка, кучи выводов, с быстрым переназначением,Stm32

Сообщение Reflector »

_kp писал(а):Reflector, можно более полный исходник Вашего примера? Или сслылку на подобный подход.
Можно более полный(но все равно обрезанный) пример касающийся работы с портами. Он под F0/F3 и т.п., у F4 есть еще VeryHighSpeed, на F1 все совсем по-другому... И нужен компилятор С++11, у меня gcc.
Спойлер

Код: Выделить всё

enum class PinMode : uint32_t
{
	Floating = 0x00,	// PUPDR[6:5] : OTYPER[4] : OSPEEDR[3:2] : MODER[1:0]
	PullUp = 0x20,
	PullDown = 0x40,
	Analog = 0x03,
	OpenDrain_LowSpeed = 0x11,
	OpenDrain_MediumSpeed = 0x15,
	OpenDrain_HighSpeed = 0x1D,
	OpenDrain_LowSpeed_PullUp = 0x31,
	OpenDrain_MediumSpeed_PullUp = 0x35,
	OpenDrain_HighSpeed_PullUp = 0x3D,
	PushPull_LowSpeed = 0x01,
	PushPull_MediumSpeed = 0x05,
	PushPull_HighSpeed = 0x0D,
	AF_Floating = 0x02,
	AF_PullUp = 0x22,
	AF_PullDown = 0x42,
	AF_OpenDrain_LowSpeed = 0x12,
	AF_OpenDrain_MediumSpeed = 0x16,
	AF_OpenDrain_HighSpeed = 0x1E,
	AF_OpenDrain_LowSpeed_PullUp = 0x32,
	AF_OpenDrain_MediumSpeed_PullUp = 0x36,
	AF_OpenDrain_HighSpeed_PullUp = 0x3E,
	AF_PushPull_LowSpeed = 0x02,
	AF_PushPull_MediumSpeed = 0x06,
	AF_PushPull_HighSpeed = 0x0E
};

template<uint32_t gpio, uint32_t pin, uint32_t af_>
struct PinT
{
	static auto base() { return (GPIO_TypeDef*)gpio; }
	const static uint32_t _af = af_;

	PinT() {}
	PinT(PinMode mode) { init(mode); }
	PinT(PinMode mode, uint32_t af) : PinT(mode) { altFunc(af); }

	static void init(PinMode mode)
	{
		pinMode(base(), pin, mode);
	}

	static void init(PinMode mode, uint32_t af)
	{
		pinMode(base(), pin, mode);
		altFunc(af);
	}

	static void altFunc(uint32_t af)
	{
		int shift = (pin & 7) * 4;
		base()->AFR[pin >> 3] = base()->AFR[pin >> 3] & ~(0x0F << shift) | (af << shift);
	}

	static void set() { base()->BSRR = 1 << pin; }
	static void clear() { base()->BSRR = 0x10000 << pin; }
	static void inverse() { base()->ODR ^= (1 << pin); }
	static void write(bool data) { base()->BSRR = (0x10000 << pin) | (data << pin);	}
	static bool read() { return base()->IDR & (1 << pin); }

private:
	static void pinMode(GPIO_TypeDef* gpio_, uint32_t pin_, PinMode mode);
};

template<uint32_t gpio, uint32_t pin, uint32_t af_>
void PinT<gpio, pin, af_>::pinMode(GPIO_TypeDef* gpio_, uint32_t pin_, PinMode mode)
{
	uint32_t moder = uint32_t(mode) & 3;
	uint32_t ospeedr = (uint32_t(mode) >> 2) & 3;
	uint32_t otyper = (uint32_t(mode) >> 4) & 1;
	uint32_t pupdr = (uint32_t(mode) >> 5) & 3;

	gpio_->MODER = gpio_->MODER & ~(3 << pin_ * 2) | (moder << pin_ * 2);
	gpio_->OTYPER = gpio_->OTYPER & ~(1 << pin_) | (otyper << pin_);
	gpio_->OSPEEDR = gpio_->OSPEEDR & ~(3 << pin_ * 2) | (ospeedr << pin_ * 2);
	gpio_->PUPDR = gpio_->PUPDR & ~(3 << pin_ * 2) | (pupdr << pin_ * 2);
}

template<uint32_t pin, uint32_t af_ = 0>
using PinA = PinT<GPIOA_BASE, pin, af_>;

template<uint32_t pin, uint32_t af_ = 0>
using PinB = PinT<GPIOB_BASE, pin, af_>;

template<uint32_t pin, uint32_t af_ = 0>
using PinC = ......
Имея такой шаблонный класс можно написать:

Код: Выделить всё

PinA<5> led(PinMode::PushPull_HighSpeed);
led.inverse();  // или PinA<5>::inverse();
Класс статический, ничего лишнего не генерится, по сути просто вызывается две функции, одна из которых инлайнится. Но это 1 пин, если хочется инициализировать порт целиком, то нужно написать другой класс, принимающий маску вместо номера пина:

Код: Выделить всё

GpioA<0x00FC> pa;  // GpioA<0b1111'1100> pa;
pa.altFunc(5);
Если заглянуть в altFunc, то внутри вызывается две функции:
Спойлер

Код: Выделить всё

static void altFunc(uint32_t af) __attribute__((always_inline))
{
	if (pinsMask & 0x00FF)  base()->AFR[0] = base()->AFR[0] & ~qmask(pinsMask) | (qmul(af) & qmask(pinsMask));
	if (pinsMask & 0xFF00)  base()->AFR[1] = base()->AFR[1] & ~qmask(pinsMask >> 8) | (qmul(af) & qmask(pinsMask >> 8));
}

static constexpr uint32_t qmask(uint32_t val) __attribute__((always_inline))
{
	uint32_t mask = 0;
	for (int i = 0; i < 8; i++)
	{
		mask >>= 4;
		if (val & 1) mask |= 0xF0000000;
		val >>= 1;			
	}
	return mask;
}

static constexpr uint32_t qmul(uint32_t af) __attribute__((always_inline))
{
	return af | (af << 4) | (af << 8) | (af << 12) | (af << 16) | (af << 20) | (af << 24) | (af << 28);
}
Но поскольку С++ потенциально способен генерить более эффективный код, чем С, у которого нет шаблонов и constexpr, от этого всего останется(в релизе, естественно) только:

Код: Выделить всё

GPIOA->AFR[0] = GPIOA->AFR[0] & ~0xFFFFFF00 | 0x55555500;
Надеюсь основная идея понятна :)
Реклама
Ответить

Вернуться в «ARM»