Доброго времени суток!
В данной статье речь пойдет о системе самодиагностики микроконтроллера STM32, в частности — STM32F100RB, который входит в отладочный комплект STM32-Discovery. Но так как микроконтроллеры STM32 во многом схожи, и отличаются в основном своей периферией — написанное будет верно и для других контроллеров (возможно с небольшими изменениями). Статья расчитана на людей, уже немного знакомых с STM32, но постараюсь рассказывать по возможности подробнее.
Clock security system (CSS)
Итак, начнем.
А начнем мы с рассмотрения источников системной частоты, лезем в даташит:
Разберемся — что и куда — на периферию системная частота попадает с высокопроизводительной шины AHB. На саму шину, с делителя, позволяющего разделить системную частоту на 1..512. А собственно сама системная частота определяется мультиплексором, обозначенным на схеме как SW. На мультиплексор подаются частоты с трёх источников: HSI, PLL,HSE, кратенько пробежимся по каждому из них:
HSI(High Speed Internal oscillator)
Внутренняя 8MHz RC цепочка, которая может быть использована как источник системной частоты или, делённая на 2, как входной сигнал для PLL(ФАПЧ). Может использоваться в дешевых устройствах, где нет особых требований к точности и стабильности системной частоты, в дешевых — потому что можно обойтись без внешнего кварца. Частота генерации зависит от температуры, напряжения, погоды в Тайване и магнитных бурь на Солнце, в общем — плавает очень сильно, хотя производитель и калибрует их с точностью 1% при температуре 25C — при изменении температуры — частота поплывет, что недопустимо, если от нее зависит точность измерений или временные задержки. Кроме того может использоваться как резервный источник системной частоты в CSS
HSE(High Speed External oscillator)
Внешний генератор тактовой частоты — может быть, как резонатор, так и внешний тактирующий сигнал. В случае с кварцем — отличается высокой температурной стабильностью, точностью частоты и долговечностью. Отсюда тактовые импульсы — либо сразу идут на мультиплексор, либо через делитель 1..16 на вход PLL
PLL(High Speed External oscillator)
Или ФАПЧ позволяет умножить входной сигнал в 2..16 раз, при этом погрешность тоже умножается, поэтому, если входной сигнал плавал +-1MHz — умноженный в 16 раз он будет плавать +-16MHz, кроме того выходная частота не должна превышать максимально допустимую частоту AHB
Так — с источниками частоты вроде разобрались — теперь перейдем к теме статьи. Чаще всего в системах высокой надежности в качестве источника системной частоты используется именно кварцевый резонатор, который может по каким то причинам отказать или сбойнуть. Для того, чтобы минимизировать плохие последствия такого сбоя в STM32 и существует CSS. Суть ее в следующем: при запуске HSE включается детектор частоты, который при ее сбое ( даже если HSE не является источником системной частоты) сразу же выключает HSE, включает HSI, устанавливает его источником системной частоты, посылает сигнал ошибки системной частоты расширенным таймерам и генерирует прерывание, извещая программу о сбое HSE
Пример кода в CoIDE:
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x.h"
#define LED_PORT GPIOC
void LED_GPIO_Configuration(void);
void Delay(__IO uint32_t nCount);
void NMI_Handler();
int delay_time = 300;
int main()
{
static unsigned long ticks;
unsigned char Clock1s;
//==================System Clock Init==================
//Сбрасываем настройки
RCC_DeInit();
//Включаем HSE
RCC_HSEConfig(RCC_HSE_ON);
//Ждем пока запустится кварц
while(RCC_WaitForHSEStartUp() != SUCCESS);
//Включаем Clock Security System
RCC->CR |= RCC_CR_CSSON;
//Переключаем системную частоту на HSE
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
//Выключаем HSI для экономии энергии
RCC_HSICmd(DISABLE);
//======================GPIO Init======================
LED_GPIO_Configuration();
//Простенькая функция мигания светодиодиком
while(1)
{
if (ticks++ >= 9999)
{
ticks = 0;
Clock1s = 1;
}
if (Clock1s)
{
Clock1s = 0;
Delay(delay_time);
GPIO_WriteBit(LED_PORT, GPIO_Pin_9, Bit_SET);
Delay(delay_time);
GPIO_WriteBit(LED_PORT, GPIO_Pin_9, Bit_RESET);
}
}
}
//Инициализируем порт GPIOC
void LED_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//Разрешаем тактирование
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
//Настраиваем
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void NMI_Handler()
{
//Очищаем флаг прерывания CSS
RCC->CIR |= RCC_CIR_CSSC;
//Ждем некоторое время после сбоя, если он кратковременный
//Возможно удастся перезапустить
Delay(100);
//Пытаемся запустить HSE
RCC_HSEConfig(RCC_HSE_ON);
//Задержка на запуск кварца
Delay(1);
if (RCC_WaitForHSEStartUp() == SUCCESS)
{
//Если запустился - устанавливаем HSE источником системной частоты
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
//Выключаем HSI
RCC_HSICmd(DISABLE);
//Меняем период мигания зеленым светодиодом
delay_time = 100;
}
else GPIO_SetBits(GPIOC,GPIO_Pin_8);
//Если не удалось запустить кварц - остаемся на HSI
//Зажигаем синий светодиод
}
//Нубская функция задержки
void Delay(__IO uint32_t nCount)
{
uint32_t i = 0;
for (; nCount != 0; i++)
{
if (i == 1000)
{
i = 0;
nCount--;
}
}
}
Видео работы:
На этом пока все — при подготовке статьи пользовался даташитом, примерами кода из CoIDE, а также статьями уважаемого DI Halta, обработчик прерывания дан лишь для примера и не претендует на супер надежное решение — просто демонстрация возможностей
Автор: SlavikMIPT