Ну вот теперь видим - программы для армов можно писать и с HAL и без или сочетая и то и то. Ну мигать светодиодом точно ;). Мне кажется написание такой программы для начинающего будет приятным вдохновением перед решением более трудных задач. Наверное HAL будет нужен для написания боле сложных программ, типа работы с графикой для Chrom-ART Accelerator, TCP/IP, USB, RTOS где производитель предлагает свои библиотечные решения на основе HAL  
И так теперь у нас есть таймер с прерываниями, точно так же как и у стареньких микроконтроллеров, теперь можно попробовать применить старые разработки, отлично работающие на пиках и аврах. Для отображения часов на индикаторе нам понадобится динамическая индикация - тут и пригодится таймер с прерываниями. Забегая вперед, скажу - у стм32 есть другая замечательная возможность организовать динамическую индикацию. Но вначале проверим работают ли устаревшие технологии. Ну в общем занимаемся копипастом своих, а может чужих решений. Програмка простая - считает секунды и должна показывать на семисегментном индикаторе:
#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_11

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

uint16_t Counter = 0;
uint8_t DigitBuffer[4];
void SystemClock_Config(void);

//=============================================================================
// TIM3 Interrupt Handler
//=============================================================================
void TIM3_IRQHandler(void)
{
    if(TIM3->SR & TIM_SR_UIF) // if UIF flag is set
    {
        TIM3->SR &= ~TIM_SR_UIF; // clear UIF flag
        AN_GPIO_ODR &= ~( AN1 | AN2 | AN3 | AN4); // anodes off
        SEG_GPIO_ODR |= (SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG); //segment off
        SEG_GPIO_ODR &= (uint8_t)~SegmentIndex; // on one segment
        switch(DigitIndex)
        {
        case 0:
            if(DigitBuffer[3] & SegmentIndex) AN_GPIO_ODR |= AN1;
            break;
        case 1:
            if(DigitBuffer[2] & SegmentIndex) AN_GPIO_ODR |= AN2;
            break;
        case 2:
            if(DigitBuffer[1] & SegmentIndex) AN_GPIO_ODR |= AN3;
            break;
        case 3:
            if(DigitBuffer[0] & SegmentIndex) AN_GPIO_ODR |= AN4;
            break;
        default:
            break;
        }
        SegmentIndex <<= (uint8_t)0x01;
        if (SegmentIndex == 0)
        {
            SegmentIndex = (uint8_t)0x01;
            if(++DigitIndex == 4) DigitIndex = 0;
        }
        if(Tick > 0) Tick--;
        //GPIOB->ODR ^= GPIO_ODR_11;
    }
}

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

int main(void)
{
    //HAL_Init();
    /* Configure the system clock */
    SystemClock_Config();
    /* System interrupt init*/
    //HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
    /* 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
    GPIOB->BSRRL = GPIO_BRR_BR_12;     // Set
    /* TIM3 clock enable */
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    TIM3->PSC = 48 - 1;       // Set prescaler to 48 = 48000000Hz/48 = 1000000 Hz = 1 us
    TIM3->ARR = 500 - 1;         // Auto reload value 500 = 500us
    TIM3->DIER = TIM_DIER_UIE; // Enable update interrupt (timer level)
    TIM3->CR1 = TIM_CR1_CEN;   // Enable timer
    NVIC_EnableIRQ(TIM3_IRQn); // Enable interrupt from TIM3 (NVIC level)
    while (1)
    {
        Bin2Bcd(Counter);
        Counter++;
        Tick = 2000;
        while (Tick);
    }
}

Работает: