Общее представление о системе тактирования
В прошлой статье мы научились создавать стабильные задержки с помощью простого таймера SysTick, а так же немного окунулись в механизм работы прерываний. Тогда мы принимали как постулат то, что тактируя таймер от источника HCLK – мы получаем 8 Мгц. Теперь настало время разобраться, откуда эти цифры.
Открыв документация и сделав переход к «Сигналы тактовой частоты MDR_RST_CLK» мы можем увидеть такую таблицу.
Встроенный RC генератор HSI
Генератор HSI вырабатывает тактовую частоту 8 МГц. Генератор автоматически запускается при появлении питания UСС.
Таким образом, после включения, контроллер тактируется от HSI. На блок схеме синим я выделил последовательность, которая демонстрирует изменения частоты на каждом блоке. По умолчанию модуль выбора источника частоты (MUX) настроен на прием с HSI. С частотой ничего не происходит и она, через HCLK (линия тактирования, совмещенная в том числе и с SysTick таймером) попадает в CPU_CLK без изменений. Но внутренний RS генератор нельзя назвать точным устройством. Его частота очень нестабильна. Для этих целей на плате установлен внешний кварцевый резонатор на 8 Мгц. Но как он называется?
Внешний генератор HSE
Генератор HSE предназначен для выработки тактовой частоты 2..16 МГц с помощью внешнего резонатора. Генератор запускается при появлении питания UCC…
Теперь нам нужно понять, как переключить контроллер с тактирования от HSI на HSE. Еще раз взглянем на блок схему. Красным цветом я обозначил логическое представление того, как будет проходить сигнал от внешнего кварцевого резонатора до линии тактирования ядра и HCLK.
Настройка регистров тактирования.
Теперь, когда мы имеем образное представление того, как «движется» тактирование, пора разобраться с регистрами тактирования. На схеме рядом с каждым блоком указаны биты определенных регистров, меняя которые можно менять «движение» и частоту тактирования. Данные биты находятся в регистре MDR_RST_CLK->CPU_CLOCK. Красным я обозначил те биты, которые нужно будет изменить. Синие же можно оставить так, как есть. Они уже установлены в правильном положении.
Далее нам нужно написать define-ы тех бит, значения которых мы будем менять.
Так как все остальные биты равны нулю, то мы можем писать в регистр напрямую, не боясь стереть старые данные. В итоге выходит следующее.
RST_CLK->CPU_CLOCK = CPU_C1_SEL(HSE)|HCLK_SEL(CPU_C3);
Казалось бы все. Но если мы зашьем это, то получим вот это.
С последующей невозможностью отладки. Я сразу же полез в документацию с описаниями ошибок. Но такой там не было. Как оказалось, я упустил одну важную специфическую деталь. В описании HSE было сказано:
Генератор HSE предназначен для выработки тактовой частоты 2..16 МГц с помощью внешнего резонатора. Генератор запускается при появлении питания UCC и сигнала разрешения HSEON в регистре HS_CONTROL.
Заглянем в этот регистр.
Пропишем еще 1 define и включим бит разрешения.
#define HS_CONTROL(HSE_ON) (1<<0)
RST_CLK->HS_CONTROL = HS_CONTROL(HSE_ON);
После всех дополнений функция имела следующий вид.
Первый «костыль».
В тот момент, когда я искал ошибку запуска HSE в перечне ошибок, я наткнулся на следующий глюк.
Я не мог не запомнить его, так как планировал сразу же после включение тактирования от внешнего кварцевого резонатора выключить внутренний генератор. Данная ошибка присутствует во всех ревизиях микроконтроллера. Так что для нее придется лепить костыль. Скажу сразу. В моем случае мне так и не удалось отключить HSI. Несмотря на то, что я делал все так, как сказано в рекомендациях.
Изучим проблему и пути «решения» поподробнее.
Вот тот упомянутые регистры.
Пропишем define для нужных бит, а так же для тактирования RTC.
А теперь сделаем попытку осуществить это на практике.
Отслеживаем выполнения программы. Это состояние регистров до попытки сбросить бит.
А это после.
По началу я подумал, что ошибся со смещением и просо не включил бит ALRF, но потом нажал на него в меню справа (прямое оправление регистрами) и ничего не получил. Подумал, что не правильно выбрал тактирование, но нажав на несколько других ячеек – получил отклик. Видимо, данный метод не решает эту проблему. При попытке отключить генератор путем последовательного нажатия ALRF и HSI_ON — мы окончательно разочаровываемся в самой идеи отключения генератора. Что ж. Это не сильно мешает на начальном этапе. Но в будущем это станет проблематично. Особенно если делать карманное устройство.
Получаем 16 Мгц.
Нам удалось заставить наш контроллер тактироваться от внешнего кварцевого резонатора, что дало нам возможность получить более точные временные задержки. Теперь настало время научиться пользоваться умножителем частоты. В описании нашего контроллера сказано, что он может тактироваться с частотой вплоть до 80 Мгц. Попробуем написать функцию, которая позволит нам увеличить частоту тактирования в 2 раза. Снова взглянем на блок схему.
Теперь, перед тем, как по линии CPU_C1 частота «попадает» в CPU_C2, она проходит через CPU PLL. Там она «умножается» на какое-то значение. Рассмотрим его регистры поподробнее.
PLL не требует включения бита тактирования. Так что можно сразу приступать к настройке регистра. Опять же пропишем нужные нам define-ы.
#define PLL_CONTROL_PLL_CPU_ON (1<<2) //PLL включена.
Ну саму функцию включения.
Теперь мы можем добавить функцию в проект.
Хочу заметить, что я не просто так оставил 1 цикл мелькания светодиода. Это так называемая «программа спасения». Если в ходе экспериментов что-то пойдет не так, то после нажатия RESET будет целая секунда, чтобы прошить МК исправленной прошивкой.
После прошивки светодиод 2 раза мелькнет от внутреннего кварца, а потом в 2 раза быстрее от внешнего.
Теперь оптимизируем наш код. Использовать функцию ради 3-х строк – глупо. Тем более 2 из них повторяются и во второй функции. Поэтому предлагаю сделать их define-ами. Первая функция предстает перед нами в таком виде.
А из второй предлагаю сделать универсальную функцию переключения частоты. Для этого из предыдущей функции выкидываем настройку HSE и добавляем в регистр PLL_CONTROL еще бит перезапуска PLL. Чтобы при получении нового значения сразу начать тактироваться на нем. Функция начинает иметь следующий вид.
Внесем ее в нашу основную программу. Которую тоже приводим в порядок.
В качестве заключения, напишем программу, которая после каждого цикла будет менять коэффициент умножения увеличивая свою скорость вплоть до 10, получая 80 Мгц.
Видео работы программы.
Восстановление платы.
После первого неудачного опыта с настройкой тактовой частоты, плата перестала отвечать. Так как я не предусмотрел защитную программу (посчитав ее не нужной), я начал искать пути реализации. В комментариях к одной из предыдущих статей vertu77 предложил 3 способа восстановления платы в случае неправильной настройки портов.
Если все-таки JTAG порт умер — что делать:
1. Тогда может пригодится другой JTAG порт. Если он не разведен на плате, можно припаяться только к ножкам SW на другом порту (меньше возни)
2. Залить прошивку через UART (штатный загрузчик)
3. Использовать особенности зашитой программы — успеть прошить новую заливку после подачи питания до первого мигания. В варианте автора это практически невозможно. В реальных программах часто программа запускается от внутреннего генератора, затем инициализируется внешний кварц. Если при этом используется бесконечный цикл ожидания — можно отпаять кварц и прошиться до первого мигания.
Но в моем случае оба JTAG-а не реагировали (завсали при попытке программирования МК), защитной программы не было, а USART загрузчик был очень далеко. Да и не хотелось тратить слишком много времени на восстановление. Так был придуман четвертый способ восстановления. Нужно переключить BOOT переключатели в режим EXT_ROM/JTAG_B, подключиться к JTAG_B и зашить код с прошивкой, в которой предусмотрена защитная программа. В моем случае я просто добавил цикл ожидания в одну секунду перед настройкой кварцевого резонатора. Так что после каждого неудачного опыта достаточно было нажать на RESET и успеть войти в режим отладки заново.
Автор: Vadimatorikda