Notice: Memcache::get(): Server localhost (tcp 11211) failed with: Connection refused (111) in /home/a146/www/we.easyelectronics.ru/engine/lib/external/DklabCache/Zend/Cache/Backend/Memcached.php on line 134
8L-Курс, Часть 4 - Тактирование / STM8 / Сообщество EasyElectronics.ru

8L-Курс, Часть 4 - Тактирование

← Часть 3 — Прерывания Содержание Часть 5 — Таймеры, начало →

Раньше мы как-то не задумывались над тем, на какой частоте работает МК и от чего тактируется — работает и славно. Задержки выбирали на глаз, не зная о тактовой частоте. Пора положить этому конец и разобраться с организацией тактирования в STM8.

Вот вам страшная картинка для начала:


В STM8 есть 4 источника тактового сигнала (на картинке слева):

HSI (High Speed Internal) — высокочастотный внутренний RC генератор. Работает на частоте 16МГц и, что важно, весьма неплохо откалиброван еще на заводе. В отличие от AVR, где при переключении частоты RC генератора, калибровка уплывает (потому что меняются параметры самого генератора), тут с этим все в порядке. Генератор HSI всегда работает на частоте 16МГц, а уменьшение частоты, при необходимости осуществляется делителем SYSCLK уже после генератора. При старте МК тактируется от HSI с делителем 8. То-есть ядро работает на частоте 2МГц. Естественно, заводская калибровка не панацея и при изменении напряжения/температуры частота HSI все равно поплывет. На этот случай предусморена возможность дополнительной калибровки. А вообще, если нужна высокая точность и стабильность тактовой частоты, используйте…

HSE (High Speed Extrenal) — высокочастотный внешний генератор. Проще говоря, кварц. Для подключения кварцевого резонатора служат пины OSC_IN (A2) и OSC_OUT (A3).

Частота может быть от 1 до 16МГц (запускали и 20МГц — ничего, работает).

Помимо раскачивания кварцевого резонатора, HSE может работать как вход для внешнего тактового сигнала. Ножка OSC_OUT тогда не используется (можно использовать как обычный пин), а на OSC_IN подается сигнал.

А еще, на случай всяких неприятностей с кварцем есть система аварийного переключения тактирования — CSS (Clock Security System). Она следит за стабильностью тактового сигнала с HSE и если он вдруг прервется, переключает тактирование на HSI. Таким образом даже если кварцевый резонатор перестанет тикать, МК не зависнет, а спокойно продолжит работать, но уже на менее стабильном тактовом генераторе.

LSI (Low Speed Internal) — RC генератор с частотой около 38кГц (в даташите написано страшное: разброс от 26 (min) до 56 (max) кГц). Частота эта не калибруется и может довольно сильно плавать от температуры и напряжения питания.

Используется в первую очередь для тактирования сторожевого таймера, но так-же от него можно затактировать ядро и RTC (точно отсчитывать время они конечно не смогут, но для того, чтобы просто разбудить МК по таймеру — вполне подойдет). В STM8L есть очень сильное энергосберегающее колдунство «Low power run»: в этом режиме отключаются все генераторы кроме LSI (ядро тактируется от него), программа выполняется из RAM, а питание флеш памяти тоже отключено. Позволяет добиться потребления всего в несколько микроампер (при работающем ядре!).

LSE (Low Speed External) — генератор, для часов (RTC) и некоторой другой периферии (впрочем, никто не запрещает тактировать от него ядро). К выводам OSC32_IN (C5) и OSC32_OUT (C6) подключается «часовой» кварц на 32768Гц:

Тактовый сигнал от него идет через свой делитель RTC Prescaler и далее может заводится на контроллер LCD и на часы (RTC).

В некоторых моделях МК есть своя CSS для часового генератора (нам не повезло, у нас нету). Она работает по тому-же принципу, что и CSS для HSE: если пропадает сигнал с часового кварца, RTC начинают тактироваться от LSI. Это, между прочим, дико полезная штука для устройств, которые большую часть своей жизни проводят в спящем режиме, пробуждаясь по «будильнику» от RTC. Если не эта система, то при сбое тактирования наш девайс умрет так и не проснувшись. А так можно будет проснуться и предпринять какие-то действия.

Иногда (для калибровки например) бывает полезно вывести сигнал с тактового генератора на ножку МК. Для этого в STM8 есть приблуда по имени CCO (Configurable Clock Output). Она позволяет вывести на пин CCO (C4) сигнал с любого тактового генератора, да еще и с настраиваемым предделителем (до /64).

Системный тактовый сигнал SYSCLK (см. страшную картинку в начале) идет на ядро и на всю остальную периферию (таймеры, интерфейсы, АЦП). Для снижения потребления, в STM8L периферия при старте выключена и тактирование на нее не подается. Пока нет тактирования, записать что-то в ее регистры не получится. Поэтому перед тем, как начинать работу с каким-либо периферийным устройством, надо включить тактовый сигнал. А в STM8S тактирование всей периферии по-умолчанию включено.

Так, с организацией тактирования кажется все ясно, но как этим пользоваться?

Начнем с делителя системного тактового сигнала (SYSCLK). Для управления им служит регистр CLK_CKDIVR

В нем активны только три младших бита, а делитель может принимать значиния от 1 (то есть без делителя вообще) до 128 (что при входной частоте в 16МГц, даст всего 125кГц для тактирования ядра). Поменять делитель можно в любое время и безо всяких дополнительных задержек.

Для того, чтобы подать (или отключить) тактирование на нужную периферию, есть регистры CLK_PCKENRx. В STM8L15x их три штуки. Для каждого периферийного устройства там отводится по одному биту

Установленый в единичку бит разрешает тактирование, а сброшеный в ноль — отключает.
Сейчас мы никакие периферийные устройства использовать не будем, а вот в следующей части начнем играться с таймерами и там нам этот регистр как-раз понадобится. Поэтому считайте что эта информация была так, для общего развития :)

В AVR источник тактового сигнала задавался через fuse биты при прошивке и поменять его во время работы программы было нельзя. Это моветон и сейчас так никто не делает :) STM8 умеет менять источники тактирования «на лету», причем для этого есть целых два способа:

Автоматическое переключение источника тактирования
Для начала надо установить бит SWEN (привет любителям акустики! А на самом деле это SWitch ENable) в регистре CLK_SWCR. После этого в регистр CLK_SWR заносится магическое число:
0x01 — Переключаемся на HSI
0x02 — Переключаемся на LSI
0x04 — Переключаемся на HSE
0x08 — Переключаемся на LSE
(любые другие магические числа будут беспощадно игнорироваться)

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

Пока переключение еще не произошло, в регистре CLK_SWCR будет поднят флаг SWBSY. По нему можно определять момент, когда мы перешли на новый источник тактирования.

А еще, установив бит SWIEN (в том-же CLK_SWCR), мы можем поймать прерывание сразу после завершения процесса переключения. Иногда довольно удобно. Вот вам шаблон обработчика:
ISR(CLK_interrupt, CLK_SWITCH_vector)
{

 if (CLK_SWCR_bit.SWIF == 1)
 {
 
  CLK_SWCR_bit.SWIF = 0; 
 };
};

Хитрости с if связаны с тем, что на этот же вектор может прилететь прерывание от CSS (если оно конечно разрешено) и надо перед обработкой разобраться — что вообще случилось.

Переключение источника тактирования «вручную»
У автоматического переключения есть один недостаток — мы не решаем, когда именно произойдет переход на новый источник тактирования. Конечно, в 95% случаев это не имеет принципиального значения и можно просто подождать в цикле, пока упадет флаг SWBSY, но иногда надо переключиться в строго определенный момент. Для этого нужно сделать всё наоборот:

Сначала заносим в регистр CLK_SWR номер нужного источника (номера такие-же, как и в прошлый раз). Выбранный генератор запускается и выходит в рабочий режим. О том, что он готов к работе нам сообщит бит SWIF в регистре CLK_SWCR. Или прерывание, если оно разрешено. А вот на бит SWBSY тут ориентироваться нельзя — он будет в 1 до тех пор, пока не завершится процесс перехода на новый генератор.

После того, как генератор запустился мы можем в нужный момент установить бит SWEN и система тут-же перейдет на новый источник тактирования.

Старый источник, если он используется для чего-то еще (например, HSI для SWIM) не отключается автоматически после переключения. Если нужно его выключить, то можно сбросить соотвествующий бит в регистре CLK_ICKCR.

Бит LSION отвечает за низкочастотный внутренний генератор, а HSION за высокочастотный.

Управление внешними генераторами осуществляется через регистр CLK_ECKCR:

Тут нас интересуют биты LSEON и HSEON.

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

Хорошо, допустим, мы переключили тактирование на кварцевый резонатор. А как теперь обезопасить себя от неприятностей? У нас вроде есть CSS?

Она управляется всего одним регистром CLK_CSSR,

а для включения достаточно просто поставить один-единственный бит CSSEN. Естественно, при этом мы должны работать от HSE, иначе в чем смысл-то? Кстати, включить ее можно, а выключить уже нет. CSSEN сбрасывается только при перезагрузке МК.

После включения CSS начинает пристально следить за тактовым сигналом от HSE. Если он вдруг прервался, выполняются следующие спасательные мероприятия:

— Запускается HSI (если он выключен)и тактирование переключается на него
— HSE вырубается нафиг
— В CLK_CSSR поднимается флаг AUX, оповещая нас о том, что работает запасной генератор.
— Там-же поднимается флаг CSSD и, если установлен бит CSSDIE, происходит прерывание.
— Все регистры из группы CLK, кроме CLK_CKDIVR, блокируются для записи. Изменить настройки тактирования (кроме делителя) больше нельзя до следующей перезагрузки.

Проще говоря, действия CSS при сбое тактирования выглядят так: ПЕРЕБРОСИТЬ ТАКТ НА HSI, ПОДНЯТЬ ФЛАГИ, ЗАБЛОКИРОВАТЬ ВСЕ РЕГИСТРЫ, ПРЕРЫВАНИЕ АААА ПАНИКА!!!111 На мой взгляд, блокировка регистров — лишнее. Мы не можем переключиться обратно на кварц (может быть сбой был временным, и через пару секунд кварц запустится нормально), а значит устройство хоть и продолжает работать, но уже не сможет вернуть себе полную функциональность, пока не перезагрузится.

Ну ладно, если в случае сбоя нам предстоит работать от HSI, то его неплохо-бы откалибровать (или хотя-бы посмотреть на реальную частоту). Для вывода тактового сигнала на ножку МК есть модуль CCO. Он тоже управляется всего одним регистром CLK_CCOR:

Биты CCOSEL отвечают за выбор генератора, сигнал с которого будет заведен на ножку (C4, напомню). Значение 0000 соответствует отключенной CCO, когда на ножку ничего не выводится. А остальные варианты, это те-же магические числа, которые мы использовали при переключении тактирования:
0x01 (0001) - Выводим тактовый сигнал от HSI
0x02 (0010) - От LSI
0x04 (0100) - HSE
0x08 (1000) - LSE

Биты CCODIV отвечают за предделитель, через который проходит сигнал перед тем, как попасть на пин:

Самый младший бит CCOSWBSY обозначает, что выбранный генератор находится в процессе запуска/стабилизации. Пока он поднят писать в остальные биты нельзя.

Настроив CCO, можно брать частотомер и смотреть насколько частота интересующего нас генератора уходит от номинала. Только не забывайте, что перед тем как ловить что-то на ножке, ее надо настроить на выход.

Допустим, мы измерили частоту HSI и увидели что она немного не соответсвует номиналу. А нам надо получить точно 16МГц. Для таких случаев предусмотрена возможность калибровки HSI.

Вообще, как я уже говорил, HSI в каждом МК калибруется еще на заводе. Калибровочное значение хранится в регистре CLK_HSICALR. Не спрашивайте меня, в каких попугаях оно измеряется.

Пользовательское значение записывается в регистр CLK_HSITRIMR. RM0031 настоятельно советует, чтобы оно находилось в пределах от CLK_HSICALR-12 до CLK_HSICALR+8.
Но просто так записать туда что-то нельзя: регистр защищен от записи. Для снятия защиты используется регистр CLK_HSIUNLCKR.

В общем, процедура изменения калибровочного значения выглядит так:
Записать 0xAC в CLK_HSIUNLCKR
Записать 0x35 туда-же. Такой хитрый способ отключения защиты от записи.
Записать нужное значение в CLK_HSITRIMR.

Все эти действия надо выполнять друг за другом, не отвлекаясь на другие операции — иначе фокус не получится. После записи, CLK_HSITRIMR снова блокируется.

Ну что? Сочиним еще один бесполезный примерчик?

Использовать будем все тот-же набор: индикатор, светодиод, кнопка. Но к нему добавим еще кварц — надо же устроить сбой тактирования для демонстрации CSS :)

Работать теперь будем не на 2МГц, а на 16, поэтому значение нашей задержки придется пересчитать под новые реалии:
void SomeDelay()
{
  for (unsigned long delay_count=0; delay_count<300000; delay_count++);
};


Для того, чтобы на пин C4 выдавался тактовый сигнал, надо настроить его на выход:
PC_DDR_bit.DDR4 = 1;
PC_CR1_bit.C14 = 1;

Делитель сигнала SYSCLK отключим нафиг, чтобы работать на максимальной частоте
CLK_CKDIVR = 0;

Помните, что он стоит после всех генераторов и значит, если его не отключить, то тактовый сигнал с кварца тоже пройдет через делитель. Оно нам надо?

После отключения делителя помигаем светодиодом, чтобы показать длительность задержки при частоте 16МГц:
for (char i=0; i<6; i++)
  {
   PD_ODR_bit.ODR4 ^= 1;
   SomeDelay();
  };

А затем ждем нажатия кнопки и после него начинаем переключение на HSE. В автоматическом режиме
CLK_SWCR_bit.SWEN = 1; 
CLK_SWR = 0x04;

После того, как переключение запущено, ждем в цикле, пока упадет флаг SWBSY
while (CLK_SWCR_bit.SWBSY==1);  

Вообще-то так делать не правильно хотя-бы потому, что если HSE не запустится, то мы повиснем тут навечно. По-хорошему надо отсчитывать время ожидания и начинать беспокоиться, если оно затянулось. Кстати, а сколько длится раскачка HSE при старте? Вообще это зависит от многих факторов, но у меня с кварцем 4МГц, нагрузочными конденсаторами 18пФ, и напряжением питания 3V получилось вот так:

Желтый — сигнал с одного из выводов кварца (относительно земли), а красный импульс длительностью как-раз в наш цикл ожидания. Вышло около 1.52мс.
Сразу после того, как переключение закончено, включаем паническую CSS и разрешаем прерывание от нее:
CLK_CSSR_bit.CSSEN = 1;
CLK_CSSR_bit.CSSDIE = 1; 

А в обработчике прерывания от CSS будем выводить на индикатор букву E — мол, ERROR.
ISR(CLK_interrupt, CLK_CSS_vector)
{
  PB_ODR = ~((1<<segment_G)|(1<<segment_F)|(1<<segment_E)|(1<<segment_D)|(1<<segment_A));
  CLK_CSSR_bit.CSSDIE = 0;
};

Затем (мы снова в main(), следите за мыслью) еще раз мигаем светодиодом (через ту-же функцию задержки), чтобы было видно как изменилась частота после переключения на HSE.

И включаем CCO, настроив на выдачу сигнала с HSE:
CLK_CCOR_bit.CCOSEL = 0x04;

Теперь можно вооружаться частотомером и тыкать в пин C4, наблюдая там меандр с частотой нашего кварца.

После того, как все настроено, в цикле ловим нажатие кнопки и меняем делитель на CCO:
while (1)
  {
   CLK_CCOR_bit.CCODIV = value;
   PB_ODR = numbers[value]; 

   while (PD_IDR_bit.IDR7 == 1);
   SomeShortDelay();

   value++;
   value &= 7;
  };
};


Исходники

Я не рассказал про некоторые хитрости с тактированием. Они больно специфичные и мы их рассмотрим как-нибудь в другой раз. Но кому очень интересно — RM0031, страница 87 ждет вас!

Теперь, когда мы умеем работать с системой тактирования и знаем как пользоваться прерываниями, можно изучить, например, таймеры. В следующей части наконец-то избавимся от этой идиотской задержки на цикле!

← Часть 3 — Прерывания Содержание Часть 5 — Таймеры, начало →
  • +13
  • 21 февраля 2013, 17:03
  • dcoder
  • 1
Файлы в топике: 4_CLK.zip

Комментарии (13)

RSS свернуть / развернуть
спасибо, как всегда интересно!
0
Спасибо
0
Мы не можем переключиться обратно на кварц (может быть сбой был временным, и через пару секунд кварц запустится нормально), а значит устройство хоть и продолжает работать, но уже не сможет вернуть себе полную функциональность, пока не перезагрузится.
Мы можем програмно сбросить МК, и заново настроить генератор.
При получении сбоя не известна степень «тяжести». Может сбой произошел во время обмена по UART или другому интерфейсу, более критичному к таймингам, а следовательно данные оказались битыми. Инициализация с нуля в этом смысле надежнее, т.к. предполагается, что в начале у нас состояние не определенное.
P.S.: А это, при срабатывании защиты делитель же сбрасывается в 8, или я ошибаюсь?
0
Не, вроде не сбрасывается. В rm0031 ничего не сказано.

Лучше бы дали возможность запустить генератор и не переключаясь на него, посмотреть — работает или нет. А там уже (к примеру если он за 10 секунд не дал сбой) — можно и переключиться обратно. Программный сброс это костыль и не всегда удобно.
0
А почему битик CSSDGON не освещен?
0
  • avatar
  • Vga
  • 21 февраля 2013, 20:04
Спасибо)Про CSS очень интересно было прочитать.

А скоро будет про DMA?
0
Про DMA будет не очень скоро :) Я пока точно не решил, куда его всунуть, но это точно будет после АЦП. А АЦП после таймеров. А про них как минимум две статьи.
+1
Я не рассказал про некоторые хитрости с тактированием. Они больно специфичные и мы их рассмотрим как-нибудь в другой раз. Но кому очень интересно — RM0031, страница 87 ждет вас!
Лучше укажи номер и название раздела. Рефманы разных версий бывают. У меня там начинается раздел 9. Clock. Или ты его весь и предлагаешь почитать?)
0
  • avatar
  • Vga
  • 22 февраля 2013, 00:29
Именно весь :) Потому что раздела «то о чем не писал d.» увы, нету :(
0
Просто есть некоторые вещи в этом разделе, которые больше относятся ко всякой периферии (RTC, LCD, ненужный BEEP). Поэтому о них расскажу при случае)
0
PC_DDR_bit.DDR4 = 1;
PC_CR1_bit.C14 = 1;
Тут нет ошибки? Бит С14? Или С4 все же?
0
Все, нашла =)
0
Меня тоже это часто в ступор вгоняет. Могли бы сделать что-то вроде C1_4
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.