Все что нам нужно знать для написания программ на языке ассемблера для stm32f4 я уже написал, ссылки на прошлые публикации:
STM32F4: GNU AS: Программирование на ассемблере (Часть 1)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2)
STM32F4: GNU AS: Мигаем светодиодом (Версия для STM32F4 Discovery, Оптимизация) (Часть 3)
STM32F4: GNU AS: Настраиваем среду компиляции (Часть 4)
Если вы читали публикации не как увлекательное чтиво, а разбираясь в прочитанном (и находя ошибки! спасибо тем кто мне о них написал!), то к вам уже пришло понимание того, что программирование на ассемблере это не какой-то архисложный и запутанный, а простой, понятный и эффективный процесс. Теперь нужно наработать библиотеку модулей, которые бы помогали использовать ту или иную функциональность периферии микроконтроллера, и плюс ко всему упростить настройку этой периферии.
Для начала, запустим микроконтроллер на его штатной частоте: 168 мгц, от внешнего кварцевого генератора, с использованием PLL.
В этом модуле нужно будет заложить возможность настройки системы тактирования без вмешательства в сам текст модуля. Во первых так намного проще использовать модуль — нам не придется смотреть по коду «где еще какая настройка находится», а во вторых — не придется спустя полгода — год разбираться в зависимостях внутри программы (пусть даже хорошо откомментированной). Для этого я вынес все настройки в начало файла:
@ ***************************************************************************
@ * НАСТРОЙКИ МОДУЛЯ *
@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
@ * значения делителей и множителей *
@ * *
@ * Формула расчета частоты PLL *
@ * *
@ * PLL_VCO = ([HSE_VALUE or HSI_VALUE] / PLL_M) * PLL_N *
@ * *
@ * Упрощенно: делитель PLL_M должен делить частоту кварца таким образом *
@ * чтобы на выходе получить 2 МГц. Для кварца 8 МГц: PLL_M = 4, для кварца *
@ * 16 МГц: PLL_M=8, и так далее. (стр.227 RM0090 Reference manual) *
@
.equ PLL_M , 4
.equ PLL_N , 168
@ * Формула расчета частоты тактирования процессора (стандарт: 168 мгц): *
@ * *
@ * SYSCLK = PLL_VCO / PLL_P *
@
.equ PLL_P , 2
@ * Формула расчета тактовой частоты для USB (стандарт: 48 мгц): *
@ * *
@ * USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ *
@
.equ PLL_Q , 7
@ * - - - - - - - - - - - - - - - - - - - - - - - - *
@ * значение таймаута при операциях ожидания готовности (степень двойки) *
@ * Этот параметр менять не обязательно, в случае критической ошибки при *
@ * запуске тактирования - ошибка будет сгенерирована и возвращена в R0 *
@ * по умолчанию стоит значение: 12 *
.equ timeout, 12
@ ***************************************************************************
далее, для упрощения использования модуля в проектах — все константные значения которые в нем использованы я внес в сам модуль — теперь, для того чтобы его использовать нужно только положить .asm файл в папку проекта, никаких дополнительных включений файла определений stm32f40_def.inc делать не нужно (таким образом вы можете использовать модуль даже со своим файлом определений который вы форматируете и формируете под себя, без всяких оговорок, о том что есть значения которые должны называться «так как принято, иначе сторонние модули работать не будут»:
.syntax unified @ синтаксис исходного кода
.thumb @ тип используемых инструкций Thumb
.cpu cortex-m4 @ процессор
.fpu fpv4-sp-d16 @ сопроцессор
@ константы используемые модулем
.equ PERIPH_BASE ,0x40000000
.equ APB1PERIPH_BASE ,0x00000000
.equ AHB1PERIPH_BASE ,0x00020000
.equ RCC_BASE ,(AHB1PERIPH_BASE + 0x3800)
.equ RCC_CR ,0x00000000
.equ RCC_CR_HSEON ,0x00010000
.equ RCC_CR_HSERDY ,0x00020000
.equ RCC_CR_PLLON ,0x01000000
.equ RCC_CR_PLLRDY ,0x02000000
.equ RCC_APB1ENR ,0x40
.equ RCC_APB1ENR_PWREN ,0x10000000
.equ PWR_BASE ,(APB1PERIPH_BASE + 0x7000)
.equ PWR_CR ,0x00000000
.equ PWR_CR_VOS ,0x4000
.equ RCC_CFGR ,0x08
.equ RCC_CFGR_HPRE_DIV1 ,0x00000000
.equ RCC_CFGR_PPRE2_DIV2 ,0x00008000
.equ RCC_CFGR_PPRE1_DIV4 ,0x00001400
.equ RCC_CFGR_SW ,0x00000003
.equ RCC_CFGR_SW_PLL ,0x00000002
.equ RCC_CFGR_SWS_PLL ,0x00000008
.equ RCC_PLLCFGR_PLLSRC_HSE,0x00400000
.equ RCC_PLLCFGR ,0x04
.equ FLASH_R_BASE ,(AHB1PERIPH_BASE + 0x3C00)
.equ FLASH_ACR ,0x00000000
.equ FLASH_ACR_ICEN ,0x00000200
.equ FLASH_ACR_DCEN ,0x00000400
.equ FLASH_ACR_LATENCY_5WS ,0x00000005
.equ FLASH_ACR_PRFTEN ,0x00000100
Текст программы модуля, по нашим договоренностям из четвертой части публикации размещаем в секции .asmcode, имя процедуры делаем глобальным для возможности вызова из других файлов проекта:
.section .asmcode
.global SYSCLK168_START
SYSCLK168_START:
PUSH { LR }
LDR R7, =(PERIPH_BASE + RCC_BASE)
@ Включаем HSE
LDR R1, [R7, RCC_CR]
ORR R1, R1, RCC_CR_HSEON
STR R1, [R7, RCC_CR]
@ Ожидаем стабилизации частоты кварца
MOV R0, 1 @ код ошибки при выходе по timeout
ADD R6, R7, RCC_CR @ регистр для проверки
LDR R2, =RCC_CR_HSERDY @ бит для проверки
BL TST_BIT
@ Включаем POWER control
LDR R1, [R7, RCC_APB1ENR]
ORR R1, R1, RCC_APB1ENR_PWREN
STR R1, [R7, RCC_APB1ENR]
@ Вн. регулятор в режим "нагрузкa" (выходим из энергосбережения)
LDR R1, =(PERIPH_BASE + PWR_BASE + PWR_CR)
LDR R2, [R1]
ORR R2, R2, PWR_CR_VOS
STR R2, [R1]
@ Установим делители шин
LDR R1, [R7, RCC_CFGR] @ делитель шины AHB
ORR R1, R1, RCC_CFGR_HPRE_DIV1 @ HCLK=SYSCLK
STR R1, [R7, RCC_CFGR]
LDR R1, [R7, RCC_CFGR] @ делитель шины APB2
ORR R1, R1, RCC_CFGR_PPRE2_DIV2 @ PCLK2=HCLK / 2
STR R1, [R7, RCC_CFGR]
LDR R1, [R7, RCC_CFGR] @ делитель шины APB1
ORR R1, R1, RCC_CFGR_PPRE1_DIV4 @ PCLK1=HCLK / 4
STR R1, [R7, RCC_CFGR]
@ Настройка PLL коэффициентами PLL_M, PLL_N, PLL_Q, PLL_P
LDR R1, =RCC_PLLCFGR_val @ расчитанное значение
STR R1, [R7, RCC_PLLCFGR]
@ Включаем питание PLL
LDR R1, [R7, RCC_CR]
ORR R1, R1, RCC_CR_PLLON
STR R1, [R7, RCC_CR]
@ Ожидаем готовности PLL
ADD R0, R0, 1
LDR R2, =RCC_CR_PLLRDY
BL TST_BIT
@ Настройка Flash prefetch, instruction cache, data cache и wait state
LDR R2, =(PERIPH_BASE + FLASH_R_BASE + FLASH_ACR)
LDR R1, [R2]
LDR R1, =(FLASH_ACR_ICEN + FLASH_ACR_DCEN + FLASH_ACR_LATENCY_5WS + FLASH_ACR_PRFTEN)
STR R1, [R2]
@ Выбираем PLL источником такта
LDR R1, [R7, RCC_CFGR]
BIC R1, R1, RCC_CFGR_SW
ORR R1, R1, RCC_CFGR_SW_PLL
STR R1, [R7, RCC_CFGR]
@ Ожидаем переключения на PLL
ADD R0, R0, 1
ADD R6, R7, RCC_CFGR
LDR R2, =RCC_CFGR_SWS_PLL
BL TST_BIT
MOV R0, 0 @ признак успешности выполнения
B exit
@ Подпрограмма проверки готовности: ------------------------------------------
@ R0 - статус на выход
@ R1 - адрес для чтения
@ R2 - бит-карта для сравнения
@ R3 портиться !
@ R4 портиться !
TST_BIT:
ADD R3, R0, R0, lsl timeout @ значение timeout
TST_ready:
@ проверка на таймаут
SUBS R3, R3, 1
BEQ exit @ timeout истек, выходим !
@ проверка готовности HSE
LDR R4, [R6, 0]
TST R4, R2
BEQ TST_ready
BX LR
@ выход из процедуры
exit:
POP { PC }
Ну и после проверки работы модуля, сделаем ему небольшое описание которое поместим в начало файла:
@GNU AS
@ ***************************************************************************
@ * МОДУЛЬ НАСТРОЙКИ ТАКТИРОВАНИЯ STM32F4 *
@ ***************************************************************************
@ * Модуль настраивает систему тактирования микроконтроллера на внешний *
@ * кварцевый генератор, с использованием PLL и установкой необходимых де- *
@ * лителей для шин и интерфейсов, ошибки при исполнении фиксируются *
@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
@ * Модуль вызывать без параметров, регистры не сохраняются *
@ * команда вызова: *
@ * BL SYSCLK168_START *
@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
@ * Используемые регистры: R0, R1, R2, R3, R4, R6, R7 (не сохраняются) *
@ * На входе: нет *
@ * На выходе: R0 - статус результата настройки тактирования *
@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
@ * Статус результата: *
@ * 0: Частота установлена *
@ * 1: Не удалось запустить HSE *
@ * 2: Не удалось запустить PLL *
@ * 3: Не удалось переключиться на PLL *
У вас не должно возникнуть вопросов по использованию модуля, но если вдруг.., то наша программа мигалка (в секции .asmcode) должна начинаться с вызова модуля тактирования:
Предполагается что этот модуль будет использоваться в проектах с раздельной компиляцией исходных файлов (смотрите четвертую часть публикации).
@ основная программа
Start:
.extern SYSCLK168_START
BL SYSCLK168_START @ настройка тактирования
MOV R0, 0 @ Значение 0, будет использоваться для bitband
MOV R1, 1 @ значение 1, будет использоваться для bitband
@ включим тактирование GPIO
LDR R2, =(PERIPH_BB_BASE + (RCC_BASE + RCC_AHB1ENR) * 32 + RCC_GPIO_EN * 4)
STR R1, [R2] @ запись R1 ("1") по адресу бита указанному в R2
. . .
поскольку частота тактирования микроконтроллера изменилась, возможно, потребуется увеличение значения счетчика в подпрограмме DELAY.
Файл модуля можно скачать по ссылке sysclk.asm
Автор: VitGo