Standard Peripherals Library (она же SPL) - это модуль аппаратной абстракции  (HAL) для микроконтроллеров STM8/STM32 за авторством STMicroelectronics. Эта библиотека представляет собой описание регистров, констант и функций для взаимодействия с периферией. Необходимость HAL объясняется просто: бизнес-логика не должна напрямую зависеть от платформы на которой она работает. В этом случаи возможна безболезненная замена платформы. А еще становится доступно модульное тестирование за счет подмены функций HAL на mock-функции.

Сборка библиотеки

В оригинальной версии эта библиотека не совместима с компилятором SDCC. Но сторонними разработчиками разработан публичный патч

Компилятору необходимо передать дефайн DEVICE=STM8SXXX. STM8SXXX - модель микроконтроллера, полный список можно посмотреть в заголовочном файле stm8s.h

Часть файлов нужно включать в сборку условно в зависимости от модели микроконтроллера. Пример из скрипта сборки:

set(UART2_PRESENT
    "STM8S105"
    "STM8S005"
    "STM8AF626x"
)
if(${DEVICE} IN_LIST UART2_PRESENT)
    set(SPL_SOURCES ${SPL_SOURCES} ${SPL_SOURCE_DIR}/stm8s_uart2.c)
endif()
mark_as_advanced(UART2_PRESENT)

Также потребуется несколько дополнительных файлов. Шаблоны есть в документации к библиотеке.

stm8s_conf.h

В этом файле в зависимости от модели микроконтроллера (дефайн DEVICE) подключаются заголовочные файлы доступной перферии и определяется макрос assert_param, при помощи которого библиотека проверяет входные параметры функций. Проверка параметров мне сейчас не требуется, поэтому я удалил строчку #define USE_FULL_ASSERT    (1)) для развертывания макроса assert_param в пустое выражение. Этот файл включается в stm8s.h (основной загловочный файл SPL).

stm8s_it.h

Тут находятся объявления обработчиков прерываний. Включается в stm8s.h

stm8s_it.c

Определения обработчиков прерываний. Непосредственно для сборки SPL он не нужен, но потребуется при компоновке с ней.

Hello world with SPL

Тестовый пример будет таким:

#include <stm8s.h>

#define LED_GPIO_PORT  (GPIOB)
#define LED_GPIO_PINS  (GPIO_PIN_ALL)

void delay(uint32_t t) {
    while (t--)
        ;
}

void main(void) {
    GPIO_Init(LED_GPIO_PORT,
              (GPIO_Pin_TypeDef)LED_GPIO_PINS,
              GPIO_MODE_OUT_PP_LOW_FAST);

    for (;;) {
        GPIO_WriteReverse(LED_GPIO_PORT,
                          (GPIO_Pin_TypeDef)LED_GPIO_PINS);
        delay(10000UL);
    }
}

Функция GPIO_Init выполняет настройку порта (выход, push-pull). Функция GPIO_WriteReverse изменяет состояние порта для мигания светодиодом. Назначения функций и констант понятны из их имен.

К сборке бинарника (в главном CMakeLists.txt) добавляем добавляем опеределения обработчиков прерывания (stm8s_it.c) и линкуем его с SPL:

set(DEVICE "STM8S103")
include_directories(${CMAKE_SOURCE_DIR})

add_subdirectory(vendor/STM8S_StdPeriph_Lib)

add_executable(blink
    main.c
    stm8s_it.c
)
target_link_libraries(blink spl)

Таймер и прерывание при его переполнении

Пример для таймера TIM4. Обработчик прерывания таймера в stm8s_it.c:

 /*
  *  Current frenq master clock: 16MHz HSI / 8 (default prescaler) = 2MHz
  *  Irq overflow frenq: 2MHz / (256 * 128 (max prescaler)) = ~60Hz
  */
 INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
 {
     static uint8_t c = 0;

     c++;
     if (c == 15) {
         GPIO_WriteReverse(LED_GPIO_PORT,
                           (GPIO_Pin_TypeDef)LED_GPIO_PINS);
         c = 0;
     }

     TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
 }

Для повторных срабатываний прерывания необходимо очищать флаг вызовом TIM4_ClearITPendingsBit.
Так же внутри реализован импровизированый делитель, чтобы мигание можно было наблюдать без осцилографа.

В функции main настраивается порт, таймер, разрешаются прерывания, включается счет таймера и запускается бесконечный цикл.

int main(void) {
    GPIO_Init(LED_GPIO_PORT,
              (GPIO_Pin_TypeDef)LED_GPIO_PINS,
              GPIO_MODE_OUT_PP_LOW_FAST);

    TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_ARR_RESET_VALUE);
    TIM4_ClearFlag(TIM4_FLAG_UPDATE);
    TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);

    enableInterrupts();

    TIM4_Cmd(ENABLE);

    for (;;)
        ;
}

Исходники тут.