Микроконтроллеры STM8. Система тактирования.

Микроконтроллеры STM8. Система тактирования.
Здравствуйте,В прошлый раз мы начали рассматривать таймеры, а сегодня мы с вами разберемся, как устроена система тактирования в STM8S.
По сравнению с AVR, STM8 сильно выигрывает в мощности и гибкости тактирования. Единственный минус – это невозможность тактироваться от кварцев с частотой меньше 1 МГц, но это компенсируется наличием внутреннего низкочастотного генератора. Самым же главным преимуществом STM8 перед AVR является отсутствие FUSE-битов! Все параметры тактирования настраиваются непосредственно по ходу работы программы.
Итак, источниками тактовой частоты у нас могут быть:
— внешний кварцевый резонатор с частотой 1-24МГц (HSE – High Speed External);
— внешний источник частоты 1-24МГц (HSE-ext — High Speed External);
— внутренний высокоскоростной RC-генератор 16 МГц (HSI – High Speed Internal);
— внутренний низкоскоростной RC-генератор 128 КГц (LSI – Low Speed Internal).
Вот так выглядит блок-схема модуля тактирования:

На схеме мы видим, что выбор основной частоты происходит в модуле Master Clock Switch. После него она поступает на периферийные устройства (таймера, SPI, и.т.д.) и через делитель в ЦПУ. При включении основным источником тактовой частоты является HSI с делителем 8, что дает нам рабочую частоту после подачи питании в 16/8=2 МГц. Логично, что при включении процессор начинает работать он внутреннего генератора – ведь внешнего может и не быть. Понижение частоты сделано для того, чтобы процессор мог стартовать при плохом состоянии питания.
Низкоскоростной генератор может как тактировать ЦПУ, так и работать независимо от основных источников тактирования, управляя независимым сторожевым таймером (IWD) и модулем выхода из спящего режима.
Модуль системы тактирования может вызывать два прерывания – по переключению источника тактирования (Master clock source switch event), и по срабатыванию системы обеспечения безопасности тактирования (Clock Security System event).
Сигнал от любого из источников тактирования, независимо от того, является он активным в данный момент, или нет, может быть выведен наружу контроллера на ножку CCO.
Переключить источник тактирования можно вручную или автоматически. О способах переключения можно прочитать в Reference Manual на странице 64. Я произвожу переключение так:
1. Записываем в регистр CLK_SWR значение, соответствующее необходимому источнику тактовой частоты.
2. Ждем стабилизации источника частоты, контролируя бит SWIF в регистре CLK_SWCR.
3. Разрешаем переключение источника установкой бита SWEN в регистре CLK_SWCR.
4. Записываем в регистр CLK_SWR значение, соответствующее необходимому источнику тактовой частоты. При этом устанавливается бит SWBSY в регистре CLK_SWCR и соответствующий источник тактирования запускается. В этот момент микроконтроллер все ещё тактируется от старого источника. После того, как частота стабилизируется, значение из регистра CLK_SWR копируется в CLK_CMSR и происходит переключение источника тактирования.
5. Ждем установки флага SWIF в регистре CLK_SWCR. После его установки вызывается прерывание, если оно разрешено битом SWIEN.
Рассмотрим управляющие регистры модуля тактирования:
Регистр CLK_CMSR — Clock master status register. Регистр состояния основной частоты. В этом регистре хранится информация о том, какой именно источник тактирования является текущим на данный момент. Регистр предназначен только для чтения, запись в этот регистр не приведет ни к какому результату. Возможные значения регистра:
- 0xE1: текущий источник тактирования – HSI (значение после сброса);
- 0xD2: источник тактирования – LSI;
- 0xB4: источник тактирования – HSE.
Регистр CLK_CKDIVR — Clock divider register. Регистр делителя частоты.

Биты HSIDIV[1:0] выбирают делитель для внутреннего генератора. Возможные значения:
- 00 – делитель равен 1 (частота не делится);
- 01 — делитель равен 2;
- 10 — делитель равен 4;
- 11 – делитель равен 8.
Возможные значения:
- 000 — fCPU=fMASTER
- 001 — fCPU=fMASTER/2
- 010 — fCPU=fMASTER/4
- 011 — fCPU=fMASTER/8
- 100 — fCPU=fMASTER/16
- 101 — fCPU=fMASTER/32
- 110 — fCPU=fMASTER/64
- 111 — fCPU=fMASTER/128

Бит HSIEN отвечает за включение высокочастотного генератора. По умолчанию он равен единице и генератор включен.
Бит HSIRDY определяет состояние генератора HSI. Если он установлен, то генератор готов к работе.
Установка бита FHWU разрешает быстрый выход из спящих режимов.
Функции битов LSIEN и LSIRDY соответствуют аналогичным для HSI и отвечают за работу низкоскоростного генератора.
Регистр CLK_ECKR — External clock register. Регистр управления внешним источником тактирования. Этот регистр отвечает за настройки генератора HSE. В нем всего два конфигурационных бита:
HSEEN – установка этого бита включает внешний источник тактирования (кварц или генератор);
HSERDY – этот бит устанавливается аппаратно, и показывает готовность внешнего источника к работе.
Регистр CLK_SWCR — Switch control register. Регистр управления переключением тактовой частоты.

Рассмотрим составляющие его биты:
SWBSY – этот бит устанавливается аппаратно в момент процесса переключения источника тактовой частоты.
SWEN – установка этого бита разрешает пере переключение источника тактовой частоты.
Установка битв SWIEN разрешает прерывание по переключению источника тактовой частоты.
Флаг SWIF устанавливается после переключения источника. В процедуре обработчика прерывания его необходимо очищать.
Регистр CLK_CSSR — Clock security system register. Регистр управления системой обеспечения безопасности тактирования.

Бит CSSEN включает модуль CSS.
Бит CSSDIE разрешает вызов прерывания по срабатыванию системы обеспечения безопасности тактирования.
Флаг CSSD выставляется при срабатыванию модуля CSS. В процедуре обработчика прерывания его необходимо очищать.
Более подробное описание этого модуля будет ниже.
Самое время потренироваться на кошках. На плате STM8S – Discovery установлен кварц на 16 МГц, его и будем использовать. Изменим программу из предыдущей статьи про таймеры: добавим в нее переключение тактирования на кварц. При этом увеличим предделитель Таймера1, чтобы видеть переключение светодиода, так как частота тактирования таймера увеличится с 2 МГц (значение по умолчанию) до 16 МГц, и мигание светодиода будет незаметно глазу.
include "iostm8s105s6.h" // подключение заголовочного файла с объявлениями регистров, масок и битов
__interrupt void TIM1_OVR_UIF_handler(void);
void init(void)
{
PD_DDR_bit.DDR0 = 1; // Ножка PD0 конфигурируется на вывод
PD_CR1_bit.C10 = 1; // Выход типа Push-pull
PD_CR2_bit.C20 = 1; // Скорость переключения - до 10 МГц.
//Настройка Таймера1
// Синхронизация как ведущий с периферией отключена
TIM1_CR2 = 0;
// Синхронизация как ведомый с периферией отключена
TIM1_SMCR = 0;
// Внешнее тактирование отключено
TIM1_ETR = 0;
// Прерывание по обновлению счетного регистра разрешено
TIM1_IER = MASK_TIM1_IER_UIE;
// ВАЖНО!!!
// Порядок установки предделителя - старший регистр, потом младший
TIM1_PSCRH = 0;
TIM1_PSCRL = 10;
// Режим непрерывного счета по возрастанию
// Прерывание по переполнению разрешено и таймер запущен
TIM1_CR1 = (MASK_TIM1_CR1_URS+MASK_TIM1_CR1_CEN);
// Настройка тактового генератора
CLK_ECKR_bit.HSEEN = 1; // Вкючаем HSE
CLK_SWCR_bit.SWEN=1; // Разрешаем переключение источника тактовой частоты
while(CLK_ECKR_bit.HSERDY != 1) {} //Ждем готовности источника тактирования
CLK_CKDIVR = 0; // Предделитель равен нулю
CLK_SWR = 0xB4; // Выбираем HSE источником тактовой частоты
while (CLK_SWCR_bit.SWIF != 1){} // Ждем готовности переключения
}
int main( void ) // Основная программа
{
init();
asm("rim");
while(1) // Бесконечный цикл
{
asm("nop");
}
}
// Вектор прерывания по обновлению или переполнению Таймера1
#pragma vector = TIM1_OVR_UIF_vector
__interrupt void TIM1_OVR_UIF_handler(void)
{
// Проверка, что же вызвало прерывание
if (TIM1_SR1_UIF==1)
{
TIM1_SR1_UIF = 0; // Очистка флага прерывания по обновлению
PD_ODR ^= MASK_PD_ODR_ODR0; // Переключение уровня напряжения на ножке на противоположное
// при помощи операции Исключающее ИЛИ (XOR)
}
}
У STM8 есть такой замечательный модуль, как CSS (Clock Security System). Он следит за работой кварцевого генератора, и может, в случае выхода его из строя, переключить тактирование на внутренний генератор. При этом делитель входной частоты устанавливается равным 8, что соответствует 2 МГц. Также, в момент переключения устанавливается флаг CSSD (Clock security system detection) в регистре CLK_CSSR и вызывается прерывание по срабатыванию системы обеспечения безопасности тактирования (Clock security system detection interrupt), если оно разрешено флагом CSSDIE (Clock security system detection interrupt enable).
Включается этот модуль установкой бита CSSEN (Clock security system enable) в регистре CSSR. Отредактируем нашу программу так, чтобы она, в случае выхода кварца из строя, автоматически переключалась на HSI, вызывала прерывание и выполняла некие действия.
И здесь я столкнулся с ошибкой в среде программирования. Напомню, я использую IAR Embedded Workbench for STMicroelectronics STM8, версию 1.20. В заголовочном файле iostm8s105s6.h, который я использую, есть такое описание регистра CLK_CSSR:
/* Clock security system register */
#ifdef __IAR_SYSTEMS_ICC__
typedef struct
{
unsigned char CSSEN : 1;
unsigned char AUX : 1;
unsigned char CSSD : 1;
unsigned char CSSDIE : 1;
} __BITS_CLK_CSSR;
#endif
__IO_REG8_BIT(CLK_CSSR, 0x50C8, __READ_WRITE, __BITS_CLK_CSSR);
Смотрим в Reference manual на странице 77 описание этого регистра и удивляемся: биты CSSD и CSSDIE перепутаны местами!
Исправим содержимое этого файла.
/* Clock security system register */
#ifdef __IAR_SYSTEMS_ICC__
typedef struct
{
unsigned char CSSEN : 1;
unsigned char AUX : 1;
unsigned char CSSDIE : 1;
unsigned char CSSD : 1;
} __BITS_CLK_CSSR;
#endif
__IO_REG8_BIT(CLK_CSSR, 0x50C8, __READ_WRITE, __BITS_CLK_CSSR);
И исправим маски чуть ниже.
#define MASK_CLK_CSSR_CSSDIE 0x04
#define MASK_CLK_CSSR_CSSD 0x08
В общем для всего семейства хидере iostm8.h эта ошибка присутствует тоже. В хидерах для остальных процессоров я не смотрел, но подозреваю, что там присутствует такая же ошибка.
Кроме того, в iostm8s105s6.h отсутствуют номера векторов прерывания для системы тактирования. Возьмем их из iostm8.h и вставим в конец нашего хидера. Выглядят они так:
#define AWU_vector 0x03
#define CLK_CSS_vector 0x04
#define CLK_SWITCH_vector 0x04
И с уже поправленным заголовочным файлом напишем нашу программу.
#include "iostm8s105s6.h" // подключение заголовочного файла с объявлениями регистров, масок и битов
__interrupt void TIM1_OVR_UIF_handler(void);
__interrupt void CLK_CSS_handler(void);
void init(void)
{
PD_DDR_bit.DDR0 = 1; // Ножка PD0 конфигурируется на вывод
PD_CR1_bit.C10 = 1; // Выход типа Push-pull
PD_CR2_bit.C20 = 1; // Скорость переключения - до 10 МГц.
//Настройка Таймера1
// Синхронизация как ведущий с периферией отключена
TIM1_CR2 = 0;
// Синхронизация как ведомый с периферией отключена
TIM1_SMCR = 0;
// Внешнее тактирование отключено
TIM1_ETR = 0;
// Прерывание по обновлению счетного регистра разрешено
TIM1_IER = MASK_TIM1_IER_UIE;
// Предделитель - 0
// ВАЖНО!!!
// Порядок установки предделителя - старший регистр, потом младший
TIM1_PSCRH = 0;
TIM1_PSCRL = 10;
// Режим непрерывного счета по возрастанию
// Прерывание по переполнению разрешено и таймер запущен
TIM1_CR1 = (MASK_TIM1_CR1_URS+MASK_TIM1_CR1_CEN);
// Настройка тактового генератора
CLK_ECKR_bit.HSEEN = 1; // Вкючаем HSE
CLK_SWCR_bit.SWEN=1; // Разрешаем переключение источника тактовой частоты
while(CLK_ECKR_bit.HSERDY != 1) {} //Ждем готовности источника тактирования
CLK_CKDIVR = 0; // Предделитель равен нулю
CLK_SWR = 0xB4; // Выбираем HSE источником тактовой частоты
while (CLK_SWCR_bit.SWIF != 1){} // Ждем готовности переключения
// Включение автоматического переключения тактирования и
// разрешение прерывания по сбою HSE
CLK_CSSR = (MASK_CLK_CSSR_CSSEN + MASK_CLK_CSSR_CSSDIE);
}
int main( void ) // Основная программа
{
init();
asm("rim");
while(1) // Бесконечный цикл
{
asm("nop");
}
}
// Вектор прерывания по обновлению или переполнению Таймера1
#pragma vector = TIM1_OVR_UIF_vector
__interrupt void TIM1_OVR_UIF_handler(void)
{
// Проверка, что же вызвало прерывание
if (TIM1_SR1_UIF==1)
{
TIM1_SR1_UIF = 0; // Очистка флага прерывания по обновлению
PD_ODR ^= MASK_PD_ODR_ODR0; // Переключение уровня напряжения на ножке на противоположное
// при помощи операции Исключающее ИЛИ (XOR)
}
}
// Вектор прерывания
#pragma vector = CLK_CSS_vector
__interrupt void CLK_CSS_handler(void)
{
// Проверка, что же вызвало прерывание
if (CLK_CSSR_CSSD == 1)
{
CLK_CSSR_bit.CSSD = 0; // Очистка флага прерывания по сбою HSE
asm("nop");
// Сюда можно вставить код обработки сбоя HSE
}
}
И небольшое видео с демонстрацией работы программы. Для того, чтобы заставить сбоить кварц, я касаюсь обеих его ножек пальцем. Можно также коротить ножки кварца резистором Ом на сто. При сбое кварца видно, что частота мигания диода уменьшается, так как частота тактирования таймера падает с 16 МГц при работе от HSE до 2 МГц при переключении на HSI/8.
На сегодня все, а в следующий раз мы продолжим разбираться с таймерами.

- +6
- 26 марта 2011, 13:32
- kalvenolt
Блин, не понимаю:
>>Он следит за работой кварцевого генератора, и может, в случае выхода его из строя, переключить тактирование на внутренний генератор. При этом делитель входной частоты устанавливается равным 8, что соответствует 2 МГц.
Как сделать так, что бы он и внутренний после обнаружения неисправности внешнего выставлял в 16Мгц? Ведь вся переферия же тогда, при падении частоты в 8 раз, падает. Я ж конфигурировал её на 16Мгц.
Пробовал дописывать в while(1) {} сброс предделителя HSI в 1 (CLK_CKDIVR_bit.HSIDIV = 0;), но светодиод всё равно начинает моргать реже… В чём моя ошибка?
>>Он следит за работой кварцевого генератора, и может, в случае выхода его из строя, переключить тактирование на внутренний генератор. При этом делитель входной частоты устанавливается равным 8, что соответствует 2 МГц.
Как сделать так, что бы он и внутренний после обнаружения неисправности внешнего выставлял в 16Мгц? Ведь вся переферия же тогда, при падении частоты в 8 раз, падает. Я ж конфигурировал её на 16Мгц.
Пробовал дописывать в while(1) {} сброс предделителя HSI в 1 (CLK_CKDIVR_bit.HSIDIV = 0;), но светодиод всё равно начинает моргать реже… В чём моя ошибка?
Помучился с выставлением внешнего кварца 24МГц. В итоге нашел что нужно выставить в Options Bytes WAITESTATE = 1 wait state
- sergeyb2009
- 28 сентября 2011, 14:47
- ↓
кто ответит на такой вопрос
выполняю настройку на внутренний высокоскоростной RC-генератор 16 МГц (HSI)
mov CLK_DIVR,#%00000000 ; установка предделителя
; Запрещаем переключение источника тактовой частоты
BRES CLK_SWCR,#1 ; clear bit SWEN
;; установка источника тактирования
; внутренний высокоскоростной RC-генератор 16 МГц (HSI)
mov CLK_SWR,#$E1; кстати почему значение E1
если на стр 108 RM0031 Reference manual
указано 0x01: HSI selected as system clock source (reset value)
откуда тогда E1?
идем далее
; Ждем готовности переключения
; Ждем стабилизации источника частоты,
; контролируя бит SWIF в регистре CLK_SWCR.
WAIT0: BTJF CLK_SWCR,#3,WAIT0 ; wait SWIF
BRES CLK_SWCR,#3 ; clear SWIF
и вот главный вопрос а произойдет ли переключение флага SWIF?
выполняю настройку на внутренний высокоскоростной RC-генератор 16 МГц (HSI)
mov CLK_DIVR,#%00000000 ; установка предделителя
; Запрещаем переключение источника тактовой частоты
BRES CLK_SWCR,#1 ; clear bit SWEN
;; установка источника тактирования
; внутренний высокоскоростной RC-генератор 16 МГц (HSI)
mov CLK_SWR,#$E1; кстати почему значение E1
если на стр 108 RM0031 Reference manual
указано 0x01: HSI selected as system clock source (reset value)
откуда тогда E1?
идем далее
; Ждем готовности переключения
; Ждем стабилизации источника частоты,
; контролируя бит SWIF в регистре CLK_SWCR.
WAIT0: BTJF CLK_SWCR,#3,WAIT0 ; wait SWIF
BRES CLK_SWCR,#3 ; clear SWIF
и вот главный вопрос а произойдет ли переключение флага SWIF?
Люди добрые, подскажите…
Пытаюсь написать фукцию задержки для стм8.
Что-то типа этого:
в инициализации:
CLK_CKDIVR=0;
и сама функция:
void delay(unsigned int value)
{
TIM1_CR1|=(1<<7)|(1<<3); //APRE и OPM
TIM1_PSCRH=0x3e; //16000
TIM1_PSCRL=0x7f;
TIM1_ARRH=value>>8;
TIM1_ARRL=value;
TIM1_CNTRH=0;
TIM1_CNTRL=0;
TIM1_CR1|=(1<<0); //старт
while ((TIM1_SR1&(1<<0))==0); //ждем UIF
TIM1_SR1&=~(1<<0); //очищаем
}
Так речь о том, что не сходятится по времени. Вызов функции с аргументом 10000 дает задержку секунды 3-4. Никак не пойму, в чем дело. Подскажите, плиз…
Пытаюсь написать фукцию задержки для стм8.
Что-то типа этого:
в инициализации:
CLK_CKDIVR=0;
и сама функция:
void delay(unsigned int value)
{
TIM1_CR1|=(1<<7)|(1<<3); //APRE и OPM
TIM1_PSCRH=0x3e; //16000
TIM1_PSCRL=0x7f;
TIM1_ARRH=value>>8;
TIM1_ARRL=value;
TIM1_CNTRH=0;
TIM1_CNTRL=0;
TIM1_CR1|=(1<<0); //старт
while ((TIM1_SR1&(1<<0))==0); //ждем UIF
TIM1_SR1&=~(1<<0); //очищаем
}
Так речь о том, что не сходятится по времени. Вызов функции с аргументом 10000 дает задержку секунды 3-4. Никак не пойму, в чем дело. Подскажите, плиз…
На сколько я понял вы ARPE устанавливаете, а значит включаете теневые регистры. Зачем?
Вам надо вызвать обновление вручную (UG бит), затем сбросить флаг обновление (UIF установится от ручного обновления), и затем только запускать таймер. Между UG и UIF ещё полезно NOP вбить, а то на больших скоростях запаздывает почему-то установка флага.
Больше без доков не скажу.
Вам надо вызвать обновление вручную (UG бит), затем сбросить флаг обновление (UIF установится от ручного обновления), и затем только запускать таймер. Между UG и UIF ещё полезно NOP вбить, а то на больших скоростях запаздывает почему-то установка флага.
Больше без доков не скажу.
седня решил поцацкаться с системой тактирования, но почему то не получилось переключить МК на внешний источник. Проц stm8s003k3t6 (что на дискавери), код:
при отладке, прога виснет на while(CLK_ECKR_bit.HSERDY != 1), тоесть, почему то не хочет запускаться HSE.
Какая может быть причина?
#include "iostm8.h"
int main( void ) // Основная программа
{
// Настройка тактового генератора
CLK_ECKR_bit.HSEEN = 1; // Вкючаем HSE
CLK_SWCR_bit.SWEN=1; // Разрешаем переключение источника тактовой частоты
while(CLK_ECKR_bit.HSERDY != 1) {} //Ждем готовности источника тактирования
CLK_CKDIVR = 0; // Предделитель равен нулю
CLK_SWR = 0xB4; // Выбираем HSE источником тактовой частоты
while (CLK_SWCR_bit.SWIF != 1){} // Ждем готовности переключения
while(1) // Бесконечный цикл
{
}
}
при отладке, прога виснет на while(CLK_ECKR_bit.HSERDY != 1), тоесть, почему то не хочет запускаться HSE.
Какая может быть причина?
Кварц подключен? :)
Если вы производите отладку не в железе, а в эмуляторе (а IAR, в частности, по умолчанию всегда устанавливает эмулятор), то бита готовности вы не получите. Посмотрите на плату, идет ли обмен данными, когда вы производите отладку.
P.S.: установка бита SWEN не должна ли быть после цикла ожидания HSERDY? Или там пофигу?
Если вы производите отладку не в железе, а в эмуляторе (а IAR, в частности, по умолчанию всегда устанавливает эмулятор), то бита готовности вы не получите. Посмотрите на плату, идет ли обмен данными, когда вы производите отладку.
P.S.: установка бита SWEN не должна ли быть после цикла ожидания HSERDY? Или там пофигу?
У меня процессор stm8s003f.
Ну никак не загнать его работать от LSI!!!
Пробовал и так
CLK->ICKR |= 0x08;
CLK->SWCR |= 0x02;
while((CLK->ICKR & 16 ) != 16);
CLK->CKDIVR = 0;
CLK->SWR = 0xD2;
while ((CLK->SWCR & 0x08) != 0x08);
И с помощью библиотеки
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_LSI, ENABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
Все равно молотит от HSI!
в отладчике виснет на цикле в ожидании флага.
while ((CLK->SWCR & 0x08) != 0x08);
И еще в описании попалась странная фраза
The clock signal is not switched until the new clock source
is ready.
Как это понимать?
Ну никак не загнать его работать от LSI!!!
Пробовал и так
CLK->ICKR |= 0x08;
CLK->SWCR |= 0x02;
while((CLK->ICKR & 16 ) != 16);
CLK->CKDIVR = 0;
CLK->SWR = 0xD2;
while ((CLK->SWCR & 0x08) != 0x08);
И с помощью библиотеки
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_LSI, ENABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
Все равно молотит от HSI!
в отладчике виснет на цикле в ожидании флага.
while ((CLK->SWCR & 0x08) != 0x08);
И еще в описании попалась странная фраза
The clock signal is not switched until the new clock source
is ready.
Как это понимать?
- dpochechuev
- 18 августа 2014, 23:14
- ↓
Комментарии (15)
RSS свернуть / развернуть