Pull to refresh

STM32F4: GNU AS: Настройка тактирования микроконтроллера (Часть 5)

Reading time 6 min
Views 11K
Все что нам нужно знать для написания программ на языке ассемблера для stm32f4 я уже написал, ссылки на прошлые публикации:
STM32F4: GNU AS: Программирование на ассемблере (Часть 1)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2)
STM32F4: GNU AS: Мигаем светодиодом (Версия для STM32F4 Discovery, Оптимизация) (Часть 3)
STM32F4: GNU AS: Настраиваем среду компиляции (Часть 4)

Обсуждение можно вести на платформе VK: vk.com/topic-200545792_46642258

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

Для начала, запустим микроконтроллер на его штатной частоте: 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

продолжение STM32F4: GNU AS: Прерывания, Системный таймер (SysTick) (Часть 6)
Tags:
Hubs:
+10
Comments 0
Comments Leave a comment

Articles