Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Программирование ARM-контроллеров STM32 на ядре Cortex-M3. Часть 6. Настройка системы тактирования

Контроллеры STM32 обладают достаточно развитой системой тактирования, включающей кучу различных делителей, умножителей и селекторов, которые позволяют «разогнать» до 72 МГц частоту ядра и системной шины, а также организовать тактирование на различных частотах всех входящих в состав контроллера модулей (USB, I/O, I2C, SPI…).

При этом стартует контроллер всегда от внутреннего генератора на 8 МГц, а дальнейший «разгон» и настройка тактирования различных модулей выполняется программно.

Все регистры, отвечающие за настройку системы тактирования сгруппированы в так называемый блок RCC (reset and clock control). Это десять 32-х битных регистров, доступных в памяти начиная с адреса 0x40021000, названия которых начинаются с «RCC_».

Настройку системы тактирования можно разделить на два этапа. На первом этапе настраивается системная тактовая частота. На втором этапе — частота системной шины AHB и подключенные к шине AHB модули. Отдельно от этих двух этапов можно настроить низкоскоростные генераторы для часов реального времени и независимого «собачьего таймера».

Тут важно запомнить и понять вот что: 1) все модули контроллера «сидят» на каких-нибудь шинах, 2) после включения контроллера тактирование большинства модулей, а также тактирование периферийных шин — выключены. Соответственно, до тех пор, пока вы не затактируете выключенные шины и модули — вам не будут доступны регистры настройки модулей. Из этих регистров будут читаться одни нули, а запись в них не будет иметь никакого эффекта.

Структурной схему, показывающую какие в контроллере есть шины и модули, какие модули к какой шине подключены и какие регистры используются для настройки всей этой системы, можно найти в документации. Тут самый главный документ — это Reference Manual RM0008 (CD00171190.pdf).

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

Настройка системной частоты, часов реального времени и независимого собачьего таймера

Серыми прямоугольниками в левом ряду обозначены 4 первичных источника тактовых сигналов: HSI — высокоскоростной встроенный RC-генератор, HSE — усилитель внешнего высокоскоростного кварцевого резонатора, LSE OSC — усилитель часового кваца (внешний низкоскоростной резонатор), LSI RC — низкоскоростной RC-генератор.

Настройка приведённого на рисунке выше «дерева» происходит слева-направо, начиная с выбора первичных источников тактового сигнала. При этом общая методика такая: настраиваем модуль, включаем модуль, ждём сигнала готовности. После получения сигнала готовности переходим к настройке компонента, находящегося правее.

Сигнал готовности означает, что выходная частота соответствующего компонента стабилизировалась и её можно использовать дальше. В качестве сигнала готовности используются специальные биты в специальных регистрах (индивидуальные для каждого модуля). На схеме эти регистры и биты не отражены, но их можно найти в документации. Например, о готовности PLL сигнализируется установкой бита PLLRDY в регистре RCC_CR, о готовности усилителя внешнего кварца — установкой бита HSERDY в регистре RCC_CR и так далее.

Интересной фишкой является наличие в процессоре Cortex-M3 модуля CSS (clock security system). Этот модуль включается программно и после включения начинает анализировать работу внешнего кварца (HSE). При обнаружении ошибки, он автоматически отключает внешний кварц и переключает селектор выбора системной частоты на внутренний высокоскоростной генератор (HSI). Если HSE использовался не напрямую, а через PLL, то PLL тоже выключается. Кроме того, при срабатывании модуля CSS генерируется прерывание NMI И посылаются сигналы ошибки таймерам TIM1, TIM8.

После того, как первый этап закончен — можно приступать ко второму. Настраиваемые на этом этапе компоненты и последовательность их настройки также изображены в виде древовидной структуры (рисунок ниже).

Настройка частоты системной шины AHB, периферийных шин APB1, APB2, включение тактирования различных модулей

Если мы работаем с flash-памятью, а не перегружаем программу в оперативку, то для оптимальной работы нашего камня должны быть настроены ещё кое-какие опции.

Во-первых, нужно задать параметр, называемый Latency. Это количество циклов задержки для операций чтения из flash. Оно задаётся исходя из следующих правил:

  1. =0, если 0 ≤ SYSCLK ≤ 24 MHz;
  2. =1, если 24 MHz ≤ SYSCLK ≤ 48 MHz;
  3. =2, если 48 MHz ≤ SYSCLK ≤ 72 MHz.

Во-вторых, для ускорения работы процессора, должен быть включен буфер предварительной выборки (prefetch buffer), который позволяет вычитывать инструкции из flash двумя 64-х битными блоками. Включить или выключить этот буфер можно только когда SYSCLK < 24 MHz и делитель шины AHB=1. После сброса он автоматически включается, поэтому нам ничего делать не нужно. Но обычно если его хотят выключить, то делают это в самом начале программы, при инициализации, когда контроллер ещё работает от внутреннего генератора 8 MHz.

Обе перечисленные выше настройки flash-памяти доступны в регистре FLASH_ACR.

Для контроля полученных в итоге частот можно использовать специальную ногу (PA8), одна из функций которой — работа в качестве MCO (microcontroller clock output). На эту ногу можно настроить вывод одной из следующих частот: SYSCLK, HSI, HSE, PLL/2. Выбор осуществляется программированием битов MCO[2:0] в регистре RCC_CFGR. Кроме программирования этих битов нужно затактировать шину APB2 (на которой находится GPIO) и настроить ногу PA8 на функционирование в качестве MCO. Кроме того, при использовании PA8 в качестве MCO необходимо учитывать, что частота GPIO не должна превышать 50 МГц (а SYSCLK как мы помним можно разогнать до 72-х).

Ну вот пожалуй и всё. А в заключении давайте напишем небольшой пример — разгоним наш камень до заветных 72-х МГц и выведем частоту PLL/2 через MCO:

Программа под катом

;---------------------------------------------------------
; blocks addresses
SAR         EQU 0x42000000 ; Start Alias Region
FLASH       EQU 0x40022000
RCC         EQU 0x40021000
PORTA       EQU 0x40010800
; registers addresses
FLASH_ACR   EQU FLASH+0x0
RCC_CR      EQU RCC+0x0
RCC_CFGR    EQU RCC+0x4
RCC_APB2ENR EQU RCC+0x18
GPIOA_CRH   EQU PORTA+0x4
; bits numbers
LAT1        EQU 1
HSEON       EQU 16
HSERDY      EQU 17
PLLSRC      EQU 16
PLLMUL      EQU 18
PPRE2       EQU 11
PLLON       EQU 24
PLLRDY      EQU 25
SW1         EQU 1
AFIOEN      EQU 0
IOPAEN      EQU 2
MCO         EQU 24
MODE8       EQU 0
CNF8        EQU 2
 
	AREA STACK, NOINIT, READWRITE
	SPACE 0x400
Stack_top
 
	AREA RESET, DATA, READONLY
	dcd Stack_top
	dcd Program_start
 
	AREA PROGRAM, CODE, READONLY
	ENTRY
Program_start
InitClock
	mov r9, #1
   ; устанавливаем Latency = 2
	ldr r0, =SAR+(FLASH_ACR&0x00FFFFFF)*0x20+LAT1*4
	str r9,[r0]
   ; включаем HSE
	ldr r0, =SAR+(RCC_CR&0x00FFFFFF)*0x20+HSEON*4
	str r9,[r0]
   ; ждём появления флага HSERDY
	ldr r0, =RCC_CR      ; загружаем в r0 адрес регистра RCC_CR
wait_hserdy
	ldr r10,[r0]         ; читаем регистр RCC_CR
   ; проверяем, равен ли бит HSERDY единице (&0x20000)
	tst r10,#(1<<HSERDY)
	beq wait_hserdy
   ; выбираем HSE источником для PLL, устанавливаем множитель=9,
   ; предделитель для APB2 (/2), выбираем источник для MCO
	ldr r0,=RCC_CFGR     ; загружаем адрес регистра RCC_CFGR
	ldr r10,=(1<<PLLSRC)+(7<<PLLMUL)+(4<<PPRE2)+(7<<MCO)
	str r10,[r0]
   ; включаем PLL
	ldr r0,=SAR+(RCC_CR&0x00FFFFFF)*0x20+PLLON*4
	str r9,[r0]
   ; ждём появления флага PLLRDY
	ldr r0, =RCC_CR      ; загружаем адрес регистра RCC_CR
wait_pllrdy
	ldr r10,[r0]         ; читаем RCC_CR
   ; проверяем, равен ли бит PLLRDY единице (&0x2000000)
	tst r10, #(1<<PLLRDY)
	beq wait_pllrdy
   ; выбираем PLL в качестве SYSCLK
	ldr r0,=SAR+(RCC_CFGR&0x00FFFFFF)*0x20+SW1*4
	str r9,[r0]
   ; включаем тактирование PORTA
	ldr r0,=SAR+(RCC_APB2ENR&0x00FFFFFF)*0x20+IOPAEN*4
	str r9,[r0]
   ; конфигурим PORTA для MCO
	ldr r0,=GPIOA_CRH
	ldr r10,[r0]         ; читаем регистр GPIOA_CRH
   ; устанавливаем для PA8 MODE=11 (output max speed 50MHz),
   ; CNF=10  (alternate function output push-pull)
	ldr r11,=(3<<MODE8)+(2<<CNF8)
	bfi r10, r11, #0, #4 ; копируем 4 младших бита r11 в r10
	str r10,[r0]         ; записываем r10 в GPIOA_CRH
;------------------
   ; ничего не делаем
Work
	b Work
	END
;---------------------------------------------------------

[свернуть]

Естественно, в реальности никто в заголовке имена и адреса регистров и битов не описывает, вместо этого берут готовый файл, в котором все эти имена описаны и добавляют его в свой код директивой GET. В примере я их специально описал, чтобы было понятно, откуда что берётся (сопоставьте их с картой памяти, которую мы рассматривали в третьей части).

Ну да ладно, пусть этот код и не самый оптимальный, зато, надеюсь, предельно понятный. Для установки отдельных битов в примере используется метод bit-banding, если требуется изменить сразу кучу битов — классические «чтение-модификация-запись».

Если есть вопросы, как обычно, задаём на форуме.

  1. Часть 1. Установка MDK, создание проекта, основы Keil uVision
  2. Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32
  3. Часть 3. Карта памяти контроллеров STM32, методы работы с памятью
  4. Часть 4. Регистры, старт и режимы работы контроллеров STM32
  5. Часть 5. Как залить прошивку в контроллер
  6. Часть 6. Настройка системы тактирования
  7. Часть 7. Работа с портами ввода-вывода
  8. Часть 8. Процедуры на ассемблере для STM32
  9. Часть 9. Система прерываний
  10. Часть 10. CMSIS, использование стандартных библиотек и функций
  11. Часть 11. Подключение и использование драйверов из пакета StdPeriph
  12. Часть 12. Работа с модулями USART и UART.
  13. Часть 13. Работа с модулями ADC
  14. Часть 14. Использование DMA
  15. Часть 15. Таймеры. Глава 1 — Введение. Простейшие таймеры
  16. Часть 15. Таймеры. Глава 2 — Таймеры общего назначения TIM9 — TIM14
  17. Часть 15. Таймеры. Глава 3 — Таймеры общего назначения TIM2 — TIM5
  18. Часть 15. Таймеры. Глава 4 — Продвинутые таймеры TIM1, TIM8
  19. Часть 16. Создание устройства USB HID в Keil uVision при помощи библиотечного компонента USB
  20. Приложение 1. Набор инструкций THUMB-2 и особенности их использования
  21. Приложение 2. Таблица векторов прерываний для семейств STM32F101, STM32F102, STM32F103
  22. Приложение 3. Драйвера и функции библиотеки StdPeriph

Добавить комментарий