STM32F100: включение и выход в рабочий режим

Своё знакомство с кортексами я начинал с нового и модного тогда ядра M3, под архитектурой STM32F103. Было интересно писать короткие фрагменты кода и следить за реакцией процессора в отладчике. Кейл по своей природе обычно недоделан и сырой, но поначалу глаза округлялись, видя всё это! Естественно хотелось скорости. Поэтому моей первой серьёзной работой стала инициализация ядра c выходом на штатные 72 MHz.

В момент подачи питания на микроконтроллер или после сигнала сброса, независимо от других обстоятельств, ядро тактируется от внутреннего RC-генератора частотой 8 мГц. При этом PLL (умножитель частоты) отключен а время задержки чтения из FLASH (latency time) равно нулю.

Такая схема включения гарантирует, что микроконтроллер запустится и начнёт работать всегда, даже если внешний кварц неисправен. Запуск кварца осуществляется программным способом. Процесс инициализации можно контролировать, а в случае неисправности кварца, обработать ошибку и оставаться затактированным от внутреннего RC.

Микроконтроллеры STM32 имеют в своём составе ещё одну схему защиты, когда при сбое внешнего кварца, схема тактирования может автоматически переключиться на внутренний RC и без остановок продолжить работу камня, сигнализируя об ошибке. По умолчанию эта возможность отключена.

Что бы вывести микроконтроллер на проектную мощность - одноцикловый режим работы на частоте 72 мГц, нужно выполнить три простых шага. Включить усилитель кварца и дождаться его стабилизации. Включить PLL и дождаться стабилизации. Затем переключить тактирование с внутреннего RC на PLL. В случае, если вы используете кварц на 8 мГц, PLL должен быть настроен на 9-кратное умножение частоты.

    ; Для BitBand под рукой всегда должны быть нолик и единичка
    MOV R10, 0
    MOV R11, 1


    ; Шаг первый. Затактировать POWER control
    ; Используем метод BitBand
    MOV32 R0, (RCC_APB1ENR & 0x00FFFFFF) * 0x20 \
            + 0x42000000 \
            + 28 * 4
            ; ^^ здесь 28 - номер бита, который нужно включить
    STR R11, [R0]


    ; Установить LATENCY time = 2 машинных цикла
    ; + задействовать буферизацию FLASH (по умолчанию всегда включена)
    MOV32 R0, FLASH_ACR
    MOV R1, FLASH_ACR_LATENCY_2 + FLASH_ACR_PRFTBE
    STR R1, [R0]


    ; Включить усилитель кварца
    MOV32 R0, (RCC_CR & 0x00FFFFFF) * 0x20 \
            + 0x42000000 \
            + 16 * 4
    STR R11, [R0]

Питание на усилитель кварца подано. И по идее, теперь нужно дождаться стабилизации частоты, пока он раскочегарится, так сказать. И мы будем ждать, смотрите код ниже. Хотя можно было заняться в это время чем-нибудь полезным. Например, настроить порты, запустить экран. И не транжирить ценные миллисекунды.


    ; Ждём готовности кварца, контролируя бит RCC_CR_HSERDY
    MOV32 R0, RCC_CR
wait_hserdy
    LDR R1, [R0]
    TST R1, RCC_CR_HSERDY
    BEQ wait_hserdy
    ; Если кварц окажется неисправен
    ; Этот цикл рискует стать бесконечным


    ; Установим коэфф умножения * 9 для PLL
    ; И деления на 2 для APB1
    MOV32 R0, RCC_CFGR
    MOV32 R1, ((9-2) << 18) + RCC_CFGR_PLLSRC + RCC_CFGR_PPRE1_DIV2
            ;  ^^^ здесь 9 = коэфф. умножения PLLMUL. 8*9 = 72 mHz
    STR R1, [R0]


    ; Включаем PLL
    ; На его стабилизацию то же потребуется время.
    MOV32 R0, (RCC_CR & 0x00FFFFFF) * 0x20 \
            + 0x42000000 \
            + 24 * 4
    STR R11, [R0]


    ; Ждём готовности PLL
    MOV32 R0, RCC_CR
wait_pllrdy
    LDR R1, [R0]
    TST R1, RCC_CR_PLLRDY
    BEQ wait_pllrdy


    ; Выбираем PLL как источник тактового сигнала для микроконтроллера
    MOV32 R0, (RCC_CFGR & 0x00FFFFFF) * 0x20 \
            + 0x42000000 \
            + 1 * 4
    STR R11, [R0]


    ; Ждём готовности
    MOV32 R0, RCC_CFGR
wait_swspll
    LDR R1, [R0]
    TST R1, RCC_CFGR_SWS_PLL
    BEQ wait_swspll


Вот и всё, и совсем даже не больно! Теперь наш камень работает на full speed, full power, крейсерские 72 мГц.
Теперь ассемблерный код только подавай! Вперёд и с песней!