STM8S + SDCC: Программирование БЕЗ SPL, система тактирования

разделы: STM8 , дата: 11 апреля 2018г.

Систему тактирования я уже бегло рассматривал в случае использования SPL. Стандартная периферийная библиотека, на мой взгляд, предлагается фирмой STM в качестве инструмента для быстрого освоения семейства микроконтроллеров STM8, программистами ранее не работавшими с ними. Сейчас я считаю, что она совершенно лишняя. Хотя, в примерах я использую именованные константы взятые из заголовочных файлов SPL.

Документация которая понадобится для прочтения статьи: Reference Manual STM8S - RM0016, глава 9. В качестве целевого микроконтроллера я буду использовать 20-пиновый STM8S103F3P6.

    Содержание:
  1. Система тактирования STM8S, ключевые особенности;
  2. Подключение и отключение периферии к шинам тактирования. Регистры CLK_ICKR, CLK_PCKENR1 и CLK_PCKENR2;
  3. Внутренний высокочастотный генератор HSI. Регистр CLK_CKDIVR;
  4. Подключение внешнего кварца к HSE генератору тактовой частоты. Регистры CLK_ECKR, CLK_CMSR, CLK_SWR и CLK_SWCR;
  5. Модуль безопасности системы тактирования CSS. Регистр CLK_CSSR;
  6. Тактирование микроконтроллера от низкочастотного внутреннего генератора LSI;
  7. Тактирование микроконтроллера от внешнего часового генератора DS3231;

Скачать полные исходники со сборочными файлам и скомпилированными прошивками, можно по ссылке в конце статьи.

1. Система тактирования STM8S, ключевые особенности

Система тактирования микроконтроллеров STM8S представлена на следующей картинке:

    Особенности системы тактирования микроконтроллеров STM8S:
  1. В системе имеется два тактовых генератора, высокочастотный и низкочастотный.
  2. Внутренний низкочастотный генератор - LSI, работает на частоте 128 кГц, и имеет погрешность ±12%. Сторожевой таймер и система автопробуждения тактируется только от него.
  3. В случае AWU - системы автопробуждения, через параметр Option Bytes - CKAWUSEL, возможно назначить внешний источник тактового сигнала HSE с предделителем в качестве источника тактирования AWU.
  4. В роли высокочастотного генератора могут выступать: а) внутренний генератор - HSI работающий на частоте 16 МГц и имеющий предделитель на 1, 2, 4, 8; б) внешний генератор с частотой до 24 МГц для stm8s2xx серии и до 16 МГц для чипов stm8s1xx серии; в) внутренний генератор работающий от внешнего кварца на частотах 1-24(16 для stm8s1xx) МГц.
  5. Система тактирования имеет модуль безопасности CSS которая может отслеживать работу внешнего генератора или кварца и в случае неисправности оных, переключить микроконтроллер на работу от внутреннего генератора с предделителем равным 8, т.е. на частоту 2 МГц.
  6. Все тактовые генераторы могут включаться и отключаться.
  7. Все периферия при старте микроконтроллера, по умолчанию находится в включенном состоянии. Она может быть отключена через регистры CLK_PCKENR1 и CLK_PCKENR2.
  8. Микроконтроллер может подать тактовый сигнал на выход CCO.

Регистры отвечающие из систему тактирования и их значения по умолчанию представлены на следующей таблице:

Дефолтные настройки системы тактирования можно изменять через Options Bytes. Они разные для различных серий STM8S. В случае STM8S103 они выглядят так:

2. Подключение и отключение периферии к шинам тактирования. Регистры CLK_ICKR, CLK_PCKENR1 и CLK_PCKENR2

Начнем с простого. В качестве шаблонного проекта возьмем Blink на таймере TIM4 из предыдущей статьи.

В главной функции main() заменим строки:

    // Set fCPU = 16MHz
    CLK_CKDIVR=0;

на вызов функции следующего содержания:

static inline void clk_setup() {
   // Set fCPU = 16MHz
    CLK_CKDIVR=0;
}

С этой функцией и будем в дальнейшем работать.

За подключение периферии к шине тактирования отвечают два регистра: CLK_PCKENR1 и CLK_PCKENR2

Т.к. в программе из периферии используется лишь таймер TIM4, в функцию clk_setup() следует добавить строки:

    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;

Внутренними генераторам HSI и LSI управляет регистр CLK_ICKR:

В установках по умолчанию для этого регистра, низкочастотный LSI - генератор выключен, а высокочастотный HSI генератор включен. Это установки полностью соответствуют нашим запросам и регистр CLK_ICKR пока можно не трогать.

3. Внутренний высокочастотный генератор HSI. Регистр CLK_CKDIVR

Чип STM8S после включения питания стартует с внутренним 16 МГц генератором тактового сигнала HSI. Выход частоты с него можно менять установкой предделителя fHSI равному 1, 2, 4, или 8. По умолчанию применяется предделитель равный 8.

Частотой процессора можно управлять с помощью предделителя fCPU. По умолчанию он равен 1.

Предделители fHSI и fCPU устанавливаются через регистр CLK_CKDIVR:

Если записать в регистр значение CLK_PRESCALER_HSIDIV1, т.е. ноль, т.о. микроконтроллер будет работать на частоте 16 МГц. Светодиод при этом будет мигать с полупериодом в 1 секунду. Однако если понизить частоту процессора до 1 МГц командой:

CLK_CKDIVR=CLK_PRESCALER_HSIDIV1|CLK_PRESCALER_CPUDIV16;

то светодиод все-равно будет мигать с полупериодом в 1 секунду. Почему? Т.к. задержка формируется через таймер, и если посмотреть на схему тактирования:

то видно, что предделитель fCPU никак на периферию не влияет. Он влияет только на частоту процессора. Если вместо задержки на таймере и использовать задержку на счетчике, например из примера предыдущей статьи: Передача параметров из Си в ассемблерную функцию, то она уже будет работать должным образом.

4. Подключение внешнего кварца к HSE генератору тактовой частоты. Регистры CLK_ECKR, CLK_CMSR, CLK_SWR и CLK_SWCR

При подключении внешнего кварца, генератору HSE требуется некоторое время для стабилизации частоты. По умолчанию, это время равно 2048 тактам. Это значение можно менять через параметр Option Bytes - HSECNT.

В связи с этим, существует два варианта переключения на HSE: автоматический и ручной.

Автоматический вариант предполагает минимум действий со стороны программиста. Требуется всего пара команд, чтобы указать новый источник тактового сигнала и дать команду коммутатору на переключение на него. Далее микроконтроллер все сделает сам. Он включит генератор HSE, дождется его стабилизации, и после автоматически переключится на него. Пользовательская программа при этом не ждет переключения, она занимается своими делами.

Блок-схема алгоритма представлена на картинке ниже:

Пример программы с реализацией такого алгоритма у меня получился таким:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_hse_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_128;         // =7, prescaler =128
//  TIM4_ARR  = 124;                        // freq Timer IRQ =1kHz
    TIM4_ARR  = 16;                         // freq Timer IRQ =1kHz/2MHz HSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main(void) {
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    enableInterrupts();
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2 MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_hse_auto();
}

static void switch_to_hse_auto() {
    // Enables Clock switch
    CLK_SWCR |= CLK_SWCR_SWEN;
    // Enable HSE
    CLK_SWR = CLK_SOURCE_HSE;
}

Здесь таймер настроен на мигание светодиода с полупериодом в 1 секунду, при частоте генератора 2 МГц. Если поставить кварц с другой частотой, то частота мигания светодиода тоже измениться. Ну и конечно же, при "горячем" вынимание кварца, скажем, из беспаячной макетки мигание светодиода остановится.

Для переключения генератора тактового сигнала используется управляющий регистр коммутатора:

Для указания целевого генератора используется регистр CLK_SWR:

После переключения генератора, содержимое регистра CLK_SWR копируется в CLK_CMSR:

Программу можно модифицировать с использованием прерывания для отключения внутреннего генератора HSI:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

void IRQ_Handler_CLK(void) __interrupt(2)
{
    // turn off HSI
    CLK_ICKR &= (uint8_t)(~CLK_ICKR_HSIEN);
    // Clear Pending Flag
    CLK_SWCR &= (uint8_t)~(CLK_SWCR_SWIF);
}

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_hse_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_128;         // =7, prescaler =128
//  TIM4_ARR  = 124;                        // freq Timer IRQ =1kHz
    TIM4_ARR  = 16;                         // freq Timer IRQ =1kHz/2MHz HSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main( void ) {
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    enableInterrupts();
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_hse_auto();

}

static void switch_to_hse_auto() {
    // Enables Clock switch & Switch interrupt
    CLK_SWCR |= (CLK_SWCR_SWEN | CLK_SWCR_SWIEN);
    // Enable HSE
    CLK_SWR = CLK_SOURCE_HSE;
}

Если когда-либо потребуется отключить HSE, например после обратного переключения на HSI, тогда следует воспользоваться регистром CLK_ECKR:

Алгоритм для ручного переключения генератора представлен на блок-схеме ниже:

Реализация этого алгоритма у меня получилась такой:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

void IRQ_Handler_CLK(void) __interrupt(2)
{
    CLK_SWCR &= (uint8_t)~(CLK_SWCR_SWIF);
    // Enables Clock switch
    CLK_SWCR |= (CLK_SWCR_SWEN);
}

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static uint8_t switch_to_hse_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_128;         // =7, prescaler =128
//  TIM4_ARR  = 124;                        // freq Timer IRQ =1kHz
    TIM4_ARR  = 16;                         // freq Timer IRQ =1kHz/2MHz HSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main( void ) {
    enableInterrupts();
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    if (switch_to_hse_auto())
        CLK_ICKR &= (uint8_t)(~CLK_ICKR_HSIEN);// turn off HSI

}

static uint8_t switch_to_hse_auto() {
    // Enables Switch interrupt
    CLK_SWCR |= (CLK_SWCR_SWIEN);
    // Enable HSE
    CLK_SWR = CLK_SOURCE_HSE;

    wfi();

    while((CLK_SWCR & CLK_SWCR_SWBSY) != 0 );

    return (CLK_CMSR == CLK_SOURCE_HSE) ? 1 : 0;
}

5. Модуль безопасности системы тактирования CSS. Регистр CLK_CSSR

Модуль безопасности системы тактирования CSS позволяет отслеживать работу генератора HSE и при неполадках с его стороны, автоматически переключать такирование на внутренний генератор HSI. При этом для HSI устанавливается предделитель равный 8, генератор HSE отключается, выставляются служебные флаги.

CSS конфигурируется через регистр CLK_CSSR:

Простейший случай использования CSS добавляет всего две строчки к предыдущему примеру:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_hse_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_128;         // =7, prescaler =128
//  TIM4_ARR  = 124;                        // freq Timer IRQ =1kHz
    TIM4_ARR  = 16;                         // freq Timer IRQ =1kHz/2MHz HSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main(void) {
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    enableInterrupts();
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_hse_auto();
}

static void switch_to_hse_auto() {
    // Enables Clock switch
    CLK_SWCR |= CLK_SWCR_SWEN;
    // Enable HSE
    CLK_SWR = CLK_SOURCE_HSE;

    // waiting for switch to HSE
    while((CLK_SWCR & CLK_SWCR_SWBSY) != 0 );
    // Enable CSS
    CLK_CSSR |= CLK_CSSR_CSSEN;
}

Обращаю внимание, что перед включением CSS нужно дождаться переключения на HSE.

Можно использовать прерывание чтобы установить свой предделитель HSI при срабатывании CSS. Т.к. прерывание у системы тактирования одно на всех, нужно либо конфигурировать его на какое-то одно событие, либо парсить флаги при входе в обработчик прерывания.

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

void IRQ_Handler_CLK(void) __interrupt(2)
{
    // Clear Pending Flag & Disable Interrupt
    CLK_CSSR &= ~(CLK_CSSR_CSSD|CLK_CSSR_CSSDIE);
    // Set fCPU = 16MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV1;
}

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_hse_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_128;         // =7, prescaler =128
//  TIM4_ARR  = 124;                        // freq Timer IRQ =1kHz
    TIM4_ARR  = 16;                         // freq Timer IRQ =1kHz/2MHz HSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main(void) {
    enableInterrupts();
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
э   // let's go..
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_hse_auto();
}

static void switch_to_hse_auto() {
    // Enables Clock switch
    CLK_SWCR |= CLK_SWCR_SWEN;
    // Enable HSE
    CLK_SWR = CLK_SOURCE_HSE;

    while((CLK_SWCR & CLK_SWCR_SWBSY) != 0 );
    // Set CSSEN bit
    CLK_CSSR |= (CLK_CSSR_CSSEN|CLK_CSSR_CSSDIE);
}

6. Тактирование микроконтроллера от низкочастотного внутреннего генератора LSI

Еще одним интересным вариантом является возможность тактировать микроконтроллер от 128 кГц низкочастотного внутреннего генератора LSI. Генератор имеет погрешность ±12%, так что использование протоколов с четкими временным границами может быть затруднено. С помощью предделителя CPUDIV возможно снижать частоту CPU вплоть до 1 кГц.

Для того что бы задействовать LSI в качестве главного генератора тактовой частоты, необходимо будет выставить в Option Bytes флаг LSI_EN:

Пример Blink'a с полупериодом 1 сек. на генераторе LSI:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_lsi_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_64;          // =6, prescaler =64
    TIM4_ARR  = 1;                          // freq Timer IRQ =1kHz/ 128 kHz LSI
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main(void) {
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    enableInterrupts();
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2 MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_lsi_auto();
}

static void switch_to_lsi_auto() {
    // Enables Clock switch
    CLK_SWCR |= CLK_SWCR_SWEN;
    // Enable LSI
    CLK_SWR = CLK_SOURCE_LSI;

    while((CLK_SWCR & CLK_SWCR_SWBSY) != 0 );
}

7. Тактирование микроконтроллера от внешнего часового генератора DS3231

Кроме внешнего кварца, номинал которого не может быть меньше 1МГц, микроконтроллер STM8S можно тактировать от внешнего генератора тактовой частоты, который лишен этого ограничения. Для подключения используется пин OSCIN, при этом пин OSCOUT остается свободным как GPIO, и может использоваться для других нужд.

Что бы включить поддержку внешнего генератора, в Options Bytes нужно будет выставить флаг EXTCLK:

Генератора у меня под рукой не было, зато был DS3231, у которого имеется пин с меандром на выходе и частотой 32 кГц. Причем это высокоточный тактовый сигнал, в отличии от LSI.

Ну и опять снова Blink уже с тактированием от DS3231:

#include <stdint.h>
#include "stm8s103f.h"
#include "stm8s_tim4.h"
#include "stm8s_clk.h"

#define CFG_GCR_AL  ((uint8_t)0x02) /*!< Activation Level bit mask */
#define LED 5

extern void delay(void) __interrupt(23);
volatile uint16_t count;
static inline void clk_setup();
static void switch_to_lsi_auto();

void delay_ms(uint16_t ms)
{
    count = ms;
    CFG_GCR   = CFG_GCR_AL;                 // =2,  set AL bit
    TIM4_SR   = 0x0;                        // Clear Pending Bit
    TIM4_PSCR = TIM4_PRESCALER_16;          //  prescaler =16
    TIM4_ARR  = 1;                          // freq Timer IRQ =1kHz/ 32 kHz ext.HSE
    TIM4_IER  = (uint8_t)TIM4_IT_UPDATE;    // =1, enable interrupt
    TIM4_CR1  = TIM4_CR1_CEN;               // =1, enable counter
    wfi();                                  // goto sleep

    TIM4_CR1  = 0x0;                        //  disable counter
}

int main(void) {
    // Setup Clock System
    clk_setup();
    // Setup GPIO
    PB_DDR=(1<<LED);
    PB_CR1=(1<<LED);
    // let's go..
    enableInterrupts();
    // main loop
    for(;;)
    {
        PB_ODR ^= (1<<LED);
        delay_ms(1000);
    }
}

static inline void clk_setup() {
    // Set fCPU = 2 MHz
    CLK_CKDIVR=CLK_PRESCALER_HSIDIV8;
    // enable TIM4 and turn off other  peripherals
    CLK_PCKENR1=CLK_PCKENR1_TIM4;
    CLK_PCKENR2=0;
    // enable HSE
    switch_to_lsi_auto();
}

static void switch_to_lsi_auto() {
    // Enables Clock switch
    CLK_SWCR |= CLK_SWCR_SWEN;
    // Enable LSI
    CLK_SWR = CLK_SOURCE_HSE;

    while((CLK_SWCR & CLK_SWCR_SWBSY) != 0 );
}

Заключение. Остался не рассмотренным выход тактовой частоты CCO, я не смог придумать примера его использования кроме синхронизации нескольких микроконтроллеров. Еще я не затронул регулятор напряжения MVR, отключение которого позволяет повысить энергосбережение. Но S-серию я не рассматриваю как энергосберегающую. Все остальное вроде рассмотрел.

Скачать полные исходники со сборочными файлам и скомпилированными прошивками, можно по ссылке .