Как то меня всю дорогу немного раздражало, что конфигурацию пинов у F1 нужно задавать в двух регистрах (GPIOx->CRL и GPIOx->CRH) -- младшие восемь в одном регистре, а старшие в другом. Намедни подумалось, что хорошо бы обращаться к GPIOx-CRy, как к 64-битной переменной и читать или писать ее за один раз, благо Cortex-M3 такое умеет. В даташите разглядел, что эти регистры расположены по соседним адресам в порядке от младшего к старшему, значит, все подходит. С учетом, что регистры находятся в самом начале структуры GPIO, достаточно привести указатель GPIOx к типу "указатель на uint64_t", чтобы задуманное заработало. Концептуально инициализация всего порта за один присест может выглядеть следующим образом:
Код: Выделить всё
#define PIN_CFG(PIN, MODE) ((uint64_t)(MODE) << ((PIN) * 4))
#define I_ANALOG (0ULL << 2)
#define I_FLOAT (1ULL << 2)
#define I_PULL (2ULL << 2)
#define O_PP (0ULL << 2)
#define O_OD (1ULL << 2)
#define O_AF (2ULL << 2)
#define O_10MHZ (1ULL)
#define O_2MHZ (2ULL)
#define O_50MHZ (3ULL)
*(uint64_t *) GPIOA = (
PIN_CFG(0, O_AF + O_OD + O_10MHZ) | /* TIM2_CH1 PWM (OUTPUT, OPEN DRAIN, MEDIUM SPEEED) */
PIN_CFG(1, I_PULL) | /* EXTI1 (INPUT, PULL-DOWN) */
PIN_CFG(2, I_PULL) | /* USART2 RX (INPUT, PULL-UP) */
PIN_CFG(3, O_AF + O_2MHZ) | /* USART2 TX (OUTPUT, PUSH-PULL, LOW SPEED) */
PIN_CFG(4, I_ANALOG) | /* - unused - */
PIN_CFG(5, I_ANALOG) | /* - unused - */
PIN_CFG(6, I_ANALOG) | /* - unused - */
PIN_CFG(7, I_ANALOG) | /* - unused - */
PIN_CFG(8, O_2MHZ) | /* GPIO (OUTPUT, PUSH-PULL, LOW SPEEED) */
PIN_CFG(9, I_ANALOG) | /* - unused - */
PIN_CFG(10, I_ANALOG) | /* - unused - */
PIN_CFG(11, I_ANALOG) | /* - unused - */
PIN_CFG(12, I_ANALOG) | /* - unused - */
PIN_CFG(13, I_PULL) | /* SWDIO (INPUT, PULL-UP) */
PIN_CFG(14, I_PULL) | /* SWCLK (INPUT, PULL-DOWN) */
PIN_CFG(15, I_ANALOG) /* - unused - */
);
Даже с выключенной оптимизацией компилятор GCC приводит этот пример к виду:
Код: Выделить всё
4A0A ldr r2, =0x40010800
F64A038D movw r3, #0xA88D
4C09 ldr r4, =0x08800002
E9C23400 strd r3, r4, [r2, #0]
Всего четыре команды, что весьма компактно. Здесь я пока не очень понимаю, получается ли запись в регистр атомарной, но выглядит похоже.
Единственный момент, который немного ухудшает стройность подхода -- это включение PULL-UP на входах, если такое требуется. В этом случае придется дополнить инициализацию порта чем-то наподобие
Код: Выделить всё
GPIOA->BSRR = ( /* Configure pulled-up pins */
GPIO_BSRR_BS2 | /* Set PA2(USART2 RX) pull-up mode */
GPIO_BSRR_BS13 /* Set PA13 (SWDIO) pull-up mode */
);
Желаю слышать критику, чем вышеозначенный подход плох, если он действительно плох.