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

Микроконтроллеры STM8. Система тактирования.Здравствуйте,
В прошлый раз мы начали рассматривать таймеры, а сегодня мы с вами разберемся, как устроена система тактирования в STM8S.
По сравнению с AVR, STM8 сильно выигрывает в мощности и гибкости тактирования. Единственный минус – это невозможность тактироваться от кварцев с частотой меньше 1 МГц, но это компенсируется наличием внутреннего низкочастотного генератора. Самым же главным преимуществом STM8 перед AVR является отсутствие FUSE-битов! Все параметры тактирования настраиваются непосредственно по ходу работы программы.
Итак, источниками тактовой частоты у нас могут быть:
— внешний кварцевый резонатор с частотой 1-24МГц (HSEHigh Speed External);
— внешний источник частоты 1-24МГц (HSE-extHigh Speed External);
— внутренний высокоскоростной RC-генератор 16 МГц (HSIHigh Speed Internal);
— внутренний низкоскоростной RC-генератор 128 КГц (LSILow 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_CMSRClock master status register. Регистр состояния основной частоты. В этом регистре хранится информация о том, какой именно источник тактирования является текущим на данный момент. Регистр предназначен только для чтения, запись в этот регистр не приведет ни к какому результату. Возможные значения регистра:

  • 0xE1: текущий источник тактирования – HSI (значение после сброса);
  • 0xD2: источник тактирования – LSI;
  • 0xB4: источник тактирования – HSE.

Регистр CLK_SWRClock master switch register. Регистр переключения основной частоты. В него производится запись для смены источника тактовой частоты. Константы для ёё выбора такие же, как и в регистре CLK_CMSR. Запись в этот регистр невозможна во время процесса переключения частоты (в этот момент выставлен бит SWBSY в регистре CLK_SWCR).
Регистр CLK_CKDIVRClock divider register. Регистр делителя частоты.

Биты HSIDIV[1:0] выбирают делитель для внутреннего генератора. Возможные значения:

  • 00 – делитель равен 1 (частота не делится);
  • 01 — делитель равен 2;
  • 10 — делитель равен 4;
  • 11 – делитель равен 8.

Биты CPUDIV[2:0] определяют делитель для ЦПУ.
Возможные значения:

  • 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

Регистр CLK_ICKRInternal clock register. Регистр управления внутренних источников тактирования. Этот регистр отвечает за настройки генераторов HSI и LSI. Рассмотрим его биты:

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

Рассмотрим составляющие его биты:
SWBSY – этот бит устанавливается аппаратно в момент процесса переключения источника тактовой частоты.
SWEN – установка этого бита разрешает пере переключение источника тактовой частоты.
Установка битв SWIEN разрешает прерывание по переключению источника тактовой частоты.
Флаг SWIF устанавливается после переключения источника. В процедуре обработчика прерывания его необходимо очищать.
Регистр CLK_CSSRClock 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.

На сегодня все, а в следующий раз мы продолжим разбираться с таймерами.

Источник

Оставить комментарий

Вы можете использовать следующие теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>