Одной из самых полезных функций, включенных в состав микроконтроллеров STM32 стали часы реального времени (RTC). Данный модуль обладает всеми классическими возможностями подобных часов и может иметь отдельное автономное питание от батареи. Такой подход позволяет использовать область памяти часов для резервного хранения данных, а также реализовывать различные схемы энергосбережения.
Основу модуля часов реального времени составляет двоично-десятичный таймер/счетчик. Результат счета отображается в двух 32-разрядных регистрах. Первый из них RTC_TR содержит информацию о времени, второй – RTC_DR представляет собой календарь, включающий год, месяц, неделю и день. Все данные представлены в BCD формате, что позволяет их сразу же использовать для отображения на различных индикаторах. Модуль календаря поддерживает автоматическое определение високосных лет, а также количества дней в текущем месяце. Кроме этого доступны функции перевода на зимнее и летнее время, вызываемые программно.
Среди функций модуля RTC следует отметить два отдельных регистра событий (ALARM_x), с помощью которых можно реализовать будильники, таймеры и т.п. Формат регистров событий аналогичен счетным регистрам, что несколько упрощает их программирование. Еще одним событием, формируемым RTC, может стать сигнал выхода из спящего режима (WakeUP). Данный сигнал формируется периодически, что позволяет легко реализовывать различные системы, критичные к энергопотреблению. Счетчик выхода из спящего режима работает независимо от часов/календаря. Максимальное значение периода сна составляет примерно 48 суток.
Модуль часов расположен в отдельной области памяти, имеющей возможность  внешнего питания от батареи. Регистры RTC оснащены дополнительной защитой от записи, что обеспечивает невозможность случайного повреждения информации в них. Кроме часовых регистров в модуле выполнены 12 регистров резервирования пользовательских данных с разрядностью 32. Эти регистры не обнуляются по сигналу «Сброс» при наличии внешнего источника напряжения, что позволяет хранить в них важную информацию.


Так как мы делаем часы - грех не воспользоваться еще одной замечательной возможностью микроконтроллеров STM32. Читаем документацию и AN3371 Application note Using the hardware real-time clock (RTC) in STM32 F0, F2, F3, F4 and L1 series of MCUs, смотрим примеры, любезно предоставленные производителем нашего микроконтроллера. Пробуем применить:
#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 DotMask = 0x1800;

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
 };

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
}

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->CR1   |= TIM_CR1_CEN | TIM_CR1_ARPE;      // Counter Enable
}

void Configure_RTC(void)
{
  /* Enable the peripheral clock RTC */
  /* (1) Enable the LSI */
  /* (2) Wait while it is not ready */
  /* (3) Enable PWR clock */
  /* (4) Enable write in RTC domain control register */
  /* (5) LSI for RTC clock */
  /* (6) Disable PWR clock */
  RCC->CSR |= RCC_CSR_LSION; /* (1) */
  while((RCC->CSR & RCC_CSR_LSIRDY)!=RCC_CSR_LSIRDY) /* (2) */
  {
    /* add time out here for a robust application */
  }
  RCC->APB1ENR |= RCC_APB1ENR_PWREN; /* (3) */
  PWR->CR |= PWR_CR_DBP; /* (4) */
  RCC->BDCR = (RCC->BDCR & ~RCC_BDCR_RTCSEL) | RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_1; /* (5) */
  RCC->APB1ENR &=~ RCC_APB1ENR_PWREN; /* (7) */
}

void Init_RTC(uint32_t Time)
{
  /* RTC init mode */
  /* Configure RTC */
  /* (1) Write access for RTC registers */
  /* (2) Enable init phase */
  /* (3) Wait until it is allow to modify RTC register values */
  /* (4) set prescaler, 40kHz/128 => 312 Hz, 312Hz/312 => 1Hz */
  /* (5) New time in TR, 24-hour format */
  /* (6) Disable init phase */
  /* (7) Disable write access for RTC registers */
  RTC->WPR = 0xCA; /* (1) */
  RTC->WPR = 0x53; /* (1) */
  RTC->ISR |= RTC_ISR_INIT; /* (2) */
  while((RTC->ISR & RTC_ISR_INITF)!=RTC_ISR_INITF) /* (3) */
  {
    /* add time out here for a robust application */
  }
  RTC->PRER = 0x007F0137; /* (4) */
  RTC->TR = RTC_TR_PM | Time; /* (5) */
  RTC->ISR &=~ RTC_ISR_INIT; /* (6) */
  RTC->WPR = 0xFE; /* (7) */
  RTC->WPR = 0x64; /* (7) */
}

void Copy2Buffer()
{
    uint16_t n, k = 0x00FF, c = 3;
    DigitIndex = 0x01;  
    for (n = 0; n < 28; n++)
    {
        if(n==7)
        {
            k = 0x017F;
            c = 2;
            DigitIndex = 0x01;
        }
        if(n==14)
        {
            k = 0x027F;
            c = 1;
            DigitIndex = 0x01;
        }
        if(n==21)
        {
            k = 0x047F;
            c = 0;
            DigitIndex = 0x01;
        }       
        if(DigitBuffer[c] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & k ;
        else DataBuffer[n] = k;
        if(n==0) DataBuffer[n] ^= DotMask;
        DigitIndex <<= 0x01;
    }
}
   
int main(void)
{
    volatile uint32_t TimeToCompute = 0;
    volatile uint32_t DateToCompute = 0;
    uint8_t Su, LastSu = 0;
    GPIOInit();   
    DMAInit();   
    TIM3Init();
    Configure_RTC();
    Init_RTC(0);
   
    while (1)
    {
        while(LastSu == Su)
        {   
            TimeToCompute = RTC->TR; /* get time */
            DateToCompute = RTC->DR; /* need to read date also */
            Su = (uint8_t)(TimeToCompute & RTC_TR_SU);
        }
        LastSu = Su;
        DigitBuffer[1] = seg7[(uint8_t)((TimeToCompute & RTC_TR_ST)>>4)];
        DigitBuffer[0] = seg7[(uint8_t)(TimeToCompute & RTC_TR_SU)];
        DigitBuffer[3] = seg7[(uint8_t)((TimeToCompute & RTC_TR_MNT)>>12)];
        DigitBuffer[2] = seg7[(uint8_t)((TimeToCompute & RTC_TR_MNU)>>8)];
        DotMask ^= 0x0800;
        Copy2Buffer();
     }
}

Убрал ненужный теперь bin2bcd и регулировку яркости, чуть оптимизировал copy2buffer. На индикатор выводятся минуты и секунды, но можно вывести и часы и день недели, месяц, год. Точечки мигают - 1 с светятся, 1 с нет. Обновление на индикаторе раз в секунду.
Размер программы: Code=916 RO-data=236 RW-data=64 ZI-data=1024.
Поставленная задача считаю выполнена - часы работают, добавить кнопки для установки времени , будильников и прочего - рутинная работа, совсем не трудная.
В общем оказалось, ничего уж такого сложного в этих армах как бы и нет - постепенно, не напрягаясь, можно освоить работу с портами, таймерами, пдп, ртц и другими модулями этих замечательных микроконтроллеров. И начинающим  непонятно зачем учится на устаревших микроконтроллерах, если все равно необходимо знание английского и знание языка программирования Си. А чтобы учится, производители микроконтроллеров на ядре ARM (в частности ST) сделали все возможное. Это и дешевые отладочные пплаты со встроенным программатором-отладчиком и множество примеров работы с периферией и неплохая документация.