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

от автора

Все что нам нужно знать для написания программ на языке ассемблера для 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

ссылка на оригинал статьи https://habrahabr.ru/post/275653/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *