Микроконтроллер как и процессор компьютера можно разогнать. И как в случае с процессором компа есть как минимум два способа разгона - увеличением частоты внешнего тактового генератора (в компе эквивалент этому поднятие частоты шины FSB) или увеличение множителя. Применительно к МК первый вариант не очень удобный, т. к. требует подбора кварцевого резонатора, а второй удобнее, т. к. не меняя резонатор можно получить нужную частоту.
Посмотрим на фрагмент системы тактирования STM32F030F4P6. Фирма ST заявляет что у этого МК максимальная частота ядра 48 МГц.
На картинке показан путь тактового сигнала и его частота в ключевых точках, которые устанавливаются по умолчанию при инициализации МК. Видно что тактовая частота с генератора HSE (высокочастотный внешний генератор) через переключатель "PLL Source Mux" поступает в модуль PLL, который умножает частоту на заданное значение (в пределах 1 - 16), в данном случае на 6. На его выходе частота 48 МГц, которая поступает на еще один переключатель "System Clock Mux", после которого идет на ядро процессора и периферийные шины.
Думаю уже догадались что для разгона микроконтроллера нужно увеличить умножение частоты в модуле PLL.
Это можно сделать как при инициализации МК так и в процессе его работы. Последнее интересно тем, что можно динамически управлять частотой ядра и периферии, повышая частоту когда нужна высокая производительность или понижая когда важна экономия электроэнергии. Периферия также тактируется от ядра, но у нее есть делители частоты и если в результате разгона она начнет сбоить, то можно ей снизить частоту, сохранив высокую частоту ядра.
Итак, приступим к делу - разгоним STM32F030F4P6 и посмотрим до какой частоты он будет стабильно работать.
Схема тестового устройства.
По миганию светодиода будем определять что МК все еще нормально работает.
Тестовый код.
int main(void) { Overclocking(); // Разгон микроконтроллера. SysTim_Init(100); // Инициализация системного таймера. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); // Включаем тактирование порта PB. { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; // Настройка вывода PB1. GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // Вывод работает как выход. GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // Двухтактный выход (т. е. не открытый сток). GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // Подтягивающие резисторы отключены. GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); // Инициализация порта. } while(1) { GPIO_SetBits(GPIOB, GPIO_Pin_1); // Лог. 1 на выходе PB1. SysTim_DelayMS(500); // Ждем 500 миллисекунд. GPIO_ResetBits(GPIOB, GPIO_Pin_1); // Лог. 0 на выходе PB1. SysTim_DelayMS(500); // Ждем 500 миллисекунд. } }
Код довольно простой. Сначала вызывается функция разгона МК (будет рассмотрена позже), потом настраивается системный таймер на 100 прерываний в секунду, используемый для формирования задержек и отсчета времени. Затем включается тактирование порта GPIOB и настраивается вывод PB1 на выход. После в цикле каждые 500 миллисекунду включается и выключается светодиод.
Прежде чем рассматривать код функции Overclocking() посмотрим на рисунок тактирования МК. Для разгона нужно увеличить множитель PLL. Чем он будет больше тем выше частота ядра. Допустимые значения от 1 до 16, что дает теоретическую возможность разогнать МК до 128 МГц при 8 МГц-овом кварце. Но есть один нюанс - на момент начала выполнения функции Overclocking() умножитель частоты уже активен и через него тактируется ядро МК с периферией, а значит просто так поменять множитель не получится. Сперва необходимо отключить умножитель, а затем менять множитель. Если при той цепочке тактирования что есть сейчас попытаться его отключить то ничего из этого не выйдет. Сперва нужно изменить цепочку тактирования исключив из нее умножитель. Взглянув на картинку тактирования становится очевидно что для этого потребуется изменить состояние переключателя "System Clock Mux", выбрав тактовым сигналом HSI или HSE. После уже можно выключить умножитель и изменить множитель. Включать нужно в обратном порядке - сначала включаем умножитель, а затем переключаем источник тактового сигнала в "System Clock Mux" на PLL.
Собственно функция Overclocking() именно это и делает.
void Overclocking(void) // Разгон микроконтроллера. { RCC_HSICmd(ENABLE); // Включаем внутренний RC генератор. RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI); // Выбираем источником такторования внутренний RC генератор. RCC_PLLCmd(DISABLE); // Выключаем умножитель частоты. RCC_PLLConfig(RCC_PLLSource_HSE, RCC_CFGR_PLLMULL12); // На сколько будем умножать частоту. RCC_PLLCmd(ENABLE); // Включаем умножитель частоты. while ((RCC->CR & RCC_CR_PLLRDY) == 0); // Ждем запуска умножителя частоты. RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // Выбираем источником такторования умножитель частоты. SystemCoreClockUpdate(); // Вычисление тактовой частоты ядра. }
Мои эксперименты с разгоном показали следующее: МК стабильно работает до частоты 96 МГц, т. е. до умножения на 12 при исходной частоте 8 МГц. Дальнейшее повышение частоты сперва приводило к нестабильной работе с последующим сваливанием в прерывание HardFault (прерывание происходит при серьезной "железной" ошибке) и чем больше частота без быстрее происходил сбой. При частоте 112 МГц еще кое-как работала отладка, а при дальнейшем увеличении частоты, отладка уже не функционировала. Из этого можно сделать вывод что в случае необходимости МК можно разогнать до почти 100 МГц, что неплохо для микроконтроллера стоимостью 40 центов или 28 рублей по текущему курсу.