Решил все таки ради интереса избавится от наследия HAL - вычистил проект от всего "лишнего". Та же програмка типа секундомер.
c HAL, там правда осталась только ХАЛовская настройка тактирования:
Program Size: Code=2236 RO-data=280 RW-data=36 ZI-data=1028
очищенная:
Program Size: Code=720 RO-data=236 RW-data=12 ZI-data=1028
Если оставить чистый main:
Program Size: Code=300 RO-data=208 RW-data=0 ZI-data=1024
Получается стартап и настройка тактирования (а она очень мощная) заняли всего то жалких 300 байт. Наша програмка занимает совсем немного - 420 байт, что вполне так на уровне - может даже и лучше будет результат чем у стареньких, неохота сравнивать. Правда ассемблерщики на стареньких могут побороться, но думаю оно того не стоит.

И так, такой способ динамической индикации (в прерывании таймера) имеет один существенный недостаток. Например нам надо дрыгать ногами с точными временными интервалами - известный датчик DS18B20 требует именно такого подхода. Выставили в "1" ногу порта и ожидаем пока не истечет нужное время, а тут бац и прерывание и все наши времена полетели в неизвестную даль. Конечно это все легко обходится, но требует лишних телодвижений. А тут разработчики микроконтроллеров на ядре АРМ подложили нам приятный сюрприз в виде DMA (есть еще в STM8L и вроде в XMEGAх).
 

Пришло время рассказать об одной потрясающей штуке под названием DMA(ПДП) – то есть прямой доступ к памяти (Direct Memory Access). Поясню что же это такое.
В двух словах – прямой доступ к памяти позволяет перемещать данные без(!) участия центрального процессора. То есть процессор работает себе преспокойненько, не отвлекается ни на что, а DMA в этот момент может пересылать огромные массивы данных, например, в USART. Думаю сразу понятно насколько это полезно, ведь процессору теперь не надо отвлекаться от основной полезной работы.
А в контроллерах STM32F10x даже не один, а два контроллера прямого доступа к памяти! И, соответственно, у каждого несколько каналов (у DMA1 – 7, а у DMA2 – 5).


Пробуем применить для динамической индикации. Один хороший товарищ HHIMERA уже прошел тяжелый джедайский путь в освоении DMA и любезно выложил код на известном форуме "Казус"  на Казус
Воспользуемся его трудами - копипастим и чуть правим под нашу задачу:
#include "stm32f0xx.h"

#define SegA    (uint8_t)1<<0
#define SegB    (uint8_t)1<<1
#define SegC    (uint8_t)1<<2
#define SegD    (uint8_t)1<<3
#define SegE    (uint8_t)1<<4
#define SegF    (uint8_t)1<<5
#define SegG    (uint8_t)1<<6
#define SegH    (uint8_t)1<<7

#define digit_0    (uint8_t)(SegA | SegB | SegC | SegD | SegE | SegF)
#define digit_1    (uint8_t)(SegB | SegC)
#define digit_2    (uint8_t)(SegA | SegB | SegG | SegE | SegD)
#define digit_3    (uint8_t)(SegA | SegB | SegG | SegC | SegD)
#define digit_4    (uint8_t)(SegF | SegG | SegB | SegC)
#define digit_5    (uint8_t)(SegA | SegF | SegG | SegC | SegD)
#define digit_6    (uint8_t)(SegA | SegF | SegE | SegD | SegC | SegG)
#define digit_7    (uint8_t)(SegA | SegB | SegC)
#define digit_8    (uint8_t)(SegA | SegB | SegC | SegD | SegE | SegF | SegG)
#define digit_9    (uint8_t)(SegA | SegB | SegC | SegD | SegF | SegG)

const uint8_t seg7[10] =
{
    digit_0,
    digit_1,
    digit_2,
    digit_3,
    digit_4,
    digit_5,
    digit_6,
    digit_7,
    digit_8,
    digit_9
};

#define SEG_GPIO_ODR GPIOB->ODR
#define SEGA GPIO_ODR_0
#define SEGB GPIO_ODR_1
#define SEGC GPIO_ODR_2
#define SEGD GPIO_ODR_3
#define SEGE GPIO_ODR_4
#define SEGF GPIO_ODR_5
#define SEGG GPIO_ODR_6
#define AN_GPIO_ODR GPIOB->ODR
#define AN1 GPIO_ODR_7
#define AN2 GPIO_ODR_8
#define AN3 GPIO_ODR_9
#define AN4 GPIO_ODR_10
#define DOT_GPIO_ODR GPIOB->ODR
#define CDOT GPIO_ODR_11
#define ADOT GPIO_ODR_12

__IO uint8_t SegmentIndex = 0x01;
__IO uint8_t DigitIndex = 0;
__IO uint32_t Tick = 0;

uint16_t Counter = 0;
uint8_t DigitBuffer[4];

uint16_t DataBuffer[4*7]=
{
    0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF,
    0x017F, 0x017F, 0x017F, 0x017F, 0x017F, 0x017F, 0x017F,
    0x027F, 0x027F, 0x027F, 0x027F, 0x027F, 0x027F, 0x027F,
    0x047F, 0x047F, 0x047F, 0x047F, 0x047F, 0x047F, 0x047F
 };

uint16_t BufferTIM[4*7]=
{
    500, 500, 500, 500, 500, 500, 500,
    500, 500, 500, 500, 500, 500, 500,
    500, 500, 500, 500, 500, 500, 500,
    500, 500, 500, 500, 500, 500, 500   
};

void Bin2Bcd(uint16_t value) // HHIMERA(c) copypast
{
    uint8_t *p = DigitBuffer;
    while (value > 0)
    {
        *p++ = seg7[value % 10];
        value /= 10;
    }
}

void GPIOInit(void)
{
    /* GPIOB Periph clock enable */
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    GPIOB->MODER |=  (GPIO_MODER_MODER0_0 | GPIO_MODER_MODER1_0 |
                      GPIO_MODER_MODER2_0 | GPIO_MODER_MODER3_0 |
                      GPIO_MODER_MODER4_0 | GPIO_MODER_MODER5_0 |
                      GPIO_MODER_MODER6_0 | GPIO_MODER_MODER7_0 |
                      GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0 | GPIO_MODER_MODER10_0 |
                      GPIO_MODER_MODER11_0 | GPIO_MODER_MODER12_0) ;     /* Configure PB0-PB12 in output  mode  */
    GPIOB->OTYPER &= ~( GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1 |
                        GPIO_OTYPER_OT_2 | GPIO_OTYPER_OT_3 |
                        GPIO_OTYPER_OT_4 | GPIO_OTYPER_OT_5 |
                        GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7 |
                        GPIO_OTYPER_OT_8 | GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10 |
                        GPIO_OTYPER_OT_11 | GPIO_OTYPER_OT_12) ;    // Ensure push pull mode selected--default
    GPIOB->OSPEEDR |= ( GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1 |
                        GPIO_OSPEEDER_OSPEEDR2 | GPIO_OSPEEDER_OSPEEDR3 |
                        GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5 |
                        GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7 |
                        GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9 | GPIO_OSPEEDER_OSPEEDR10 |
                        GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12);     //Ensure maximum speed setting (even though it is unnecessary)
    GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR0 | GPIO_PUPDR_PUPDR1 |
                      GPIO_PUPDR_PUPDR2 | GPIO_PUPDR_PUPDR3 |
                      GPIO_PUPDR_PUPDR4 | GPIO_PUPDR_PUPDR5 |
                      GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7 |
                      GPIO_PUPDR_PUPDR8 | GPIO_PUPDR_PUPDR9 | GPIO_PUPDR_PUPDR10 |
                      GPIO_PUPDR_PUPDR11 | GPIO_PUPDR_PUPDR12);     //Ensure all pull up pull down resistors are disabled
    DOT_GPIO_ODR ^= ADOT;     // Set  pin ADOT
}   

void DMAInit(void)
{   
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
       
    DMA1_Channel3->CPAR = (uint32_t)&GPIOB->ODR;     // DMA channel x peripheral address register
    DMA1_Channel3->CMAR = (uint32_t)DataBuffer;     // DMA channel x memory address register   
    DMA1_Channel3->CNDTR = 4*7;                       // DMA channel x number of data register

    DMA1_Channel3->CCR  |=  DMA_CCR_MSIZE_0;         // Memory size 16 bit
    DMA1_Channel3->CCR  |=  DMA_CCR_PSIZE_0;         // Peripheral size 16 bit

    DMA1_Channel3->CCR  |=  DMA_CCR_PL_1;            // Channel Priority level High
    DMA1_Channel3->CCR  |=  DMA_CCR_MINC;            // Memory increment mode
    DMA1_Channel3->CCR  |=  DMA_CCR_CIRC;            // Circular mode
    DMA1_Channel3->CCR  |=  DMA_CCR_DIR;             // Data transfer direction Memory -> Peripheral

    DMA1_Channel3->CCR  |=  DMA_CCR_EN;              // Channel enable
   
    //---------------------------
    DMA1_Channel4->CPAR = (uint32_t)&TIM3->ARR;      // DMA channel x peripheral address register
    DMA1_Channel4->CMAR = (uint32_t)BufferTIM;      // DMA channel x memory address register   
    DMA1_Channel4->CNDTR = 4*7;                   // DMA channel x number of data register

    DMA1_Channel4->CCR  |=  DMA_CCR_MSIZE_0;         // Memory size 16 bit
    DMA1_Channel4->CCR  |=  DMA_CCR_PSIZE_0;         // Peripheral size 16 bit

    DMA1_Channel4->CCR  |=  DMA_CCR_PL;              // Channel Priority level Very High
    DMA1_Channel4->CCR  |=  DMA_CCR_MINC;            // Memory increment mode
    DMA1_Channel4->CCR  |=  DMA_CCR_CIRC;            // Circular mode
    DMA1_Channel4->CCR  |=  DMA_CCR_DIR;             // Data transfer direction Memory -> Peripheral

    DMA1_Channel4->CCR  |=  DMA_CCR_EN;              // Channel enable
}

void TIM3Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;   // TIM3 clock enable

    TIM3->PSC  = 48 - 1;  
    TIM3->ARR  = 500 - 1;

    TIM3->CCR1 = 25;                                   
           
    TIM3->DIER  |= TIM_DIER_UDE;                    // Upload DMA Enable
    TIM3->DIER  |= TIM_DIER_CC1DE;
           
    TIM3->CR1   |= TIM_CR1_CEN | TIM_CR1_ARPE;      // Counter Enable
}
void Copy2Buffer()
{
    uint16_t n;
    DigitIndex = 0x01;  
    for (n = 0; n < 7; n++)
    {
        if(DigitBuffer[3] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x00FF ;
        else DataBuffer[n] = 0x00FF;
        DigitIndex <<= 0x01;
    }
    DigitIndex = 0x01;  
    for (n = 7; n < 14; n++)
    {
        if(DigitBuffer[2] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x017F ;
        else DataBuffer[n] = 0x017F;
        DigitIndex <<= 0x01;
    }
    DigitIndex = 0x01;  
    for (n = 14; n < 21; n++)
    {
        if(DigitBuffer[1] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x027F ;
        else DataBuffer[n] = 0x027F;
        DigitIndex <<= 0x01;
    }
    DigitIndex = 0x01;  
    for (n = 21; n < 28; n++)
    {
        if(DigitBuffer[0] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x047F ;
        else DataBuffer[n] = 0x047F;
        DigitIndex <<= 0x01;
    }       
}
   
int main(void)
{
    GPIOInit();   
    DMAInit();   
    TIM3Init();

    Counter = 1234;   
    Bin2Bcd(Counter);
    Copy2Buffer(); 
   
    while (1)
    {
    }
}

Програмка должна отобразить на индикаторе "1234", что она успешно и делает - фото не выкладываю - поверьте на слово.