Программирование STM32. Часть 4: Настройка RCC
Добавлено: 30 авг 2023, 11:09
Регистры CR и CFGR
Открываем Reference manual на микроконтроллер STM32F103x8 и переходим к разделу 7: Low-, medium-, high- and XL-density reset and clock control (RCC). RCC имеет довольно много регистров, целых 10 штук. Однако, для настройки источника тактирования и делителей шин нам понадобится только 2 из них. Описание основных битов регистра:
PLLRDY — флаг готовности PLL. Устанавливается аппаратно и сигнализирует о том, что PLL заблокирован.
PLLON — Включить PLL. Устанавливается и сбрасывается программно. При переходе в режим Stop или Standby сбрасывается аппаратно. Этот бит не может быть сброшен, если PLL используется как источник системного тактирования.
CSSON — включить систему CSS
HSEBYP — Если вместо кварцевого резонатора HSE мы хотим использовать внешний прямоугольный тактовый сигнал, то этот бит нужно установить в 1
HSERDY — Флаг готовности генератора HSE. Аппаратно устанавливается в 1 после успешного запуска и стабилизации частоты HSE-генератора
HSEON — Запустить HSE генератор. Устанавливается и сбрасывается программно. При переходе в режим Stop или Standby сбрасывается аппаратно и HSE генератор останавливается. Этот бит не может быть сброшен, если HSE используется как источник системного тактирования.
HSIRDY — то же самое, что и HSERDY, только для встроенного RC-генератора HSI
HSION — то же самое, что и HSEON, только для встроенного RC-генератора HSI Описание основных битов регистра:
MCO — подача тактового сигнала на MCO-пин микроконтроллера.
0xx: Функция отключена
100: Выбран System clock (SYSCLK)
101: Выбран сигнал с HSI
110: Выбран сигнал с HSE
111: Выбран сигнал с PLL, который поделен на 2
PLLMUL — коэффициент умножения PLL. Эти биты могут быть записаны программно только при отключенном PLL
0000: Входную частота PLL умножить на 2
0001: —//— на 3
0010: —//— на 4
0011: —//— на 5
0100: —//— на 6
0101: —//— на 7
0110: —//— на 8
0111: —//— на 9
1000: —//— на 10
1001: —//— на 11
1010: —//— на 12
1011: —//— на 13
1100: —//— на 14
1101: —//— на 15
1110: —//— на 16
1111: —//— на 16
Два последних значения соответствуют одинаковому коэффициенту умножения.
PLLXTPRE — Делитель частоты с HSE генератора перед подачей на PLL. Этот бит не может быть изменен, если PLL запущен. При установке в 1 частота HSE будет поделена на 2, если 0, то делитель отключен.
PLLSRC — Источник входной частоты PLL. Не может быть изменен, если PLL запущен.
0: частота HSI генератора поделенная на 2
1: частота HSE генератора. Делитель может быть выбран PLLXTPRE битом.
PPRE2 — Делитель шины APB2 prescaler
0xx: HCLK без деления
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
PPRE1 — Делитель шины APB1 prescaler. Частота шины APB1 не должна превышать 36 МГц.
0xx: HCLK без деления
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
HPRE — AHB prescaler
0xxx: SYSCLK без деления
1000: SYSCLK / 2
1001: SYSCLK / 4
1010: SYSCLK / 8
1011: SYSCLK / 16
1100: SYSCLK / 64
1101: SYSCLK / 128
1110: SYSCLK / 256
1111: SYSCLK / 512
SWS — Состояние переключателя тактирования системы. Устанавливается аппаратно и указывает на текущий источник тактирования.
00: HSI генератор используется как источник тактирования системы
01: HSE генератор используется как источник тактирования системы
10: PLL используется как источник тактирования системы
SW — Переключатель источника тактирования системы. Изменяется программно для выбора источника SYSCLK. Устанавливается аппаратно для принудительного переключения на HSI генератор переходе в режим Stop или Standby или в случае срыва генерации HSE, который используется в качестве источника SYSCLK (только если активна система CSS)
00: HSI выбран в качестве источника системного тактирования
01: HSE выбран в качестве источника системного тактирования
10: PLL выбран в качестве источника системного тактирования
Коэффициенты
Ранее мы рассмотрели вариант тактирования системы тактирования от HSE генератора через PLL, для удобства скопирую это сюда:
Кварц HSE на 8 МГц
PLLXTPRE: без деления
PLLSRC: HSE генератор
PLLMUL = 9
SW = PLLCLK
AHB Prescaler = 1
APB1 Prescaler = 2
APB2 Prescaler = 1
И картинку тоже: Регистры в CMSIS
Во одной из тем мы учились подключать библиотеку CMSIS к IAR-у, сейчас нам понадобится этот проект, так как мы переходим к практике. Но перед этим немного поговорим о том, как устроено обращение к регистрам периферии в CMSIS.
Каждый экземпляр периферии является структурой, в которой находятся все регистры, относящиеся к данному устройству. Почти во всех случаях имя структуры совпадает с именем периферийного модуля. Для микроконтроллера STM32F103C8 все структуры периферийных модулей объявлены в файле stm32f103xb.h:
Рассмотрим, как выполняется обращение к регистрам из программы на Си. Например, нам надо в регистре RCC_CR установить бит HSEON. Это можно сделать одним из следующих способов:
или так:
Думаю, те, кто раньше программировал для микроконтроллеров AVR увидят в этих записях что-то знакомое. Рассмотрим первый случай:
Сначала идет имя периферийного модуля, в нашем случае «RCC». Затем символ «->», после чего имя регистра «CR». RCC_CR_HSEON_Msk представляет собой вот такой #define:
где 16 — номер бита HSEON в регистре CR (см. рис. 1). RCC_CR_HSEON_Msk есть ни что иное, как битовая маска, имя которой состоит из названия периферийного модуля, имени регистра и бита, а так же постфикса _Msk. В CMSIS есть еще один #define, который является синонимом RCC_CR_HSEON_Msk:
По факту все то же самое, только без _Msk.
Второй случай выглядит аналогичным образом: где
То есть, RCC_CR_HSEON_Pos является позицией бита в регистре, о чем говорит постфикс _Pos.
А как быть с параметрами, которые имеют несколько битов? К примеру в регистре CFGR мы хотим установить значение множителя PLL равное девяти, имеющее код 0111 (см. рис. 2 биты PLLMUL). Тут вот такое решение:
Первой строчкой мы устанавливаем биты 0, 1 и 2 PLLMUL в единицы, второй строчкой сбрасываем бит 3 в ноль, в итоге получаем 0111. Однако, если мы уверены, что все биты PLLMUL изначально установлены в нули, то вторую строчку мы можем пропустить:
А есть и еще один вариант: значение 0111 в десятичном виде является числом 7. Тогда можно сделать вот так:
Но и тут надо помнить, что такая запись справедлива только в случае, если изначальное значение всех битов PLLMUL равны нулям.
Настройка
Для правильной настройки системы тактирования в микроконтроллерах STM32 необходимо соблюдать определенную последовательность запуска блоков. Если мы хотим переключить систему с внутреннего RC-генератора на HSE через PLL, то необходимо провести следующие операции вот в таком порядке:
Запустить генератор HSE
Настроить PLL
Запустить PLL
Настроить количество циклов ожидания FLASH
Настроить делители шин
Переключиться на работу от PLL
Так, вроде все понятно, хотя… А что это за 4-й пункт? Что за циклы ожидания FLASH? Вот здесь кроется один неочевидный момент. Дело в том, что FLASH-память микроконтроллера, в которой хранится управляющая программа, может работать на максимальной частоте 24 МГц. Обмен данными с FLASH осуществляется через шину AHB (см. рис. 3). А если частота шины AHB выше 24 МГц, но необходимо ввести циклы ожидания обращений к этой памяти, примем, чем выше частота, тем больше этих циклов надо:
ноль циклов ожидания, если 0 < SYSCLK ≤ 24 MHz
один цикл ожидания, если 24 MHz < SYSCLK ≤ 48 MHz
два цикла ожидания, если 48 MHz < SYSCLK ≤ 72 MHz
Надеюсь, с этим все ясно.
Хочу отметить, что порядок настройки может быть и таким:
Настроить количество циклов ожидания FLASH
Настроить делители шин
Запустить генератор HSE
Настроить PLL
Запустить PLL
Переключиться на работу от PLL
Просто надо помнить, что мы сидим на ветке, которую пилим, и важно все отпилить и подставить в нужной последовательности, чтоб не упасть. В случае с STM32, «упасть» не получится, так как в этих микроконтроллерах реализована аппаратная защита от неправильной последовательности действий: мы не сможем остановить генератор, от которого сейчас работаем или переключиться на источник тактового сигнала, если он еще не запущен. Так что в худшем случае у нас просто ни чего не получится, что то же является не очень приятным.
Итак, поехали! Возьмем за основу проект, который мы создали ранее, когда подключали CMSIS: https://github.com/DiMoonElec/stm32f103c8_empty_project.
Создадим функцию, в которую будем добавлять код инициализации:
Первым делом запускаем генератор HSE:
После этого нам надо дождаться установки флага HSERDY, который указывает на успешный запуск генератора. Можно это сделать по-простому с помощью цикла while() {}, но тогда мы рискуем зависнуть в нем навсегда, если что-то случится с кварцевым резонатором. Хотелось бы иметь возможность каким-то образом сигнализировать о невозможности запуска. Вот моя реализация:
Тут реализован тайм-аут на запуск HSE. Если генератор успел запуститься до возникновения условия if(StartUpCounter > 0x1000), то мы выходим из цикла for() с помощью инструкции break.
Так, HSE запустили. Переходим к настройке PLL:
Тут просто настраиваем коэффициент умножения и выбираем источник тактирования PLL. Далее, запускаем PLL:
После этого ждем успешного запуска. Тут ожидание реализовано точно так же, как и для HSE:
После этого настраиваем FLASH и делители:
Ну и торжественный момент переключение на работу от PLL:
Ждем завершения переключения:
Поздравляю! Мы запустились от PLL! После этого можно отключить RC-генератор HSI, так как он нам больше не нужен:
Приведу весь код функции переключения на работу от PLL:
Открываем Reference manual на микроконтроллер STM32F103x8 и переходим к разделу 7: Low-, medium-, high- and XL-density reset and clock control (RCC). RCC имеет довольно много регистров, целых 10 штук. Однако, для настройки источника тактирования и делителей шин нам понадобится только 2 из них. Описание основных битов регистра:
PLLRDY — флаг готовности PLL. Устанавливается аппаратно и сигнализирует о том, что PLL заблокирован.
PLLON — Включить PLL. Устанавливается и сбрасывается программно. При переходе в режим Stop или Standby сбрасывается аппаратно. Этот бит не может быть сброшен, если PLL используется как источник системного тактирования.
CSSON — включить систему CSS
HSEBYP — Если вместо кварцевого резонатора HSE мы хотим использовать внешний прямоугольный тактовый сигнал, то этот бит нужно установить в 1
HSERDY — Флаг готовности генератора HSE. Аппаратно устанавливается в 1 после успешного запуска и стабилизации частоты HSE-генератора
HSEON — Запустить HSE генератор. Устанавливается и сбрасывается программно. При переходе в режим Stop или Standby сбрасывается аппаратно и HSE генератор останавливается. Этот бит не может быть сброшен, если HSE используется как источник системного тактирования.
HSIRDY — то же самое, что и HSERDY, только для встроенного RC-генератора HSI
HSION — то же самое, что и HSEON, только для встроенного RC-генератора HSI Описание основных битов регистра:
MCO — подача тактового сигнала на MCO-пин микроконтроллера.
0xx: Функция отключена
100: Выбран System clock (SYSCLK)
101: Выбран сигнал с HSI
110: Выбран сигнал с HSE
111: Выбран сигнал с PLL, который поделен на 2
PLLMUL — коэффициент умножения PLL. Эти биты могут быть записаны программно только при отключенном PLL
0000: Входную частота PLL умножить на 2
0001: —//— на 3
0010: —//— на 4
0011: —//— на 5
0100: —//— на 6
0101: —//— на 7
0110: —//— на 8
0111: —//— на 9
1000: —//— на 10
1001: —//— на 11
1010: —//— на 12
1011: —//— на 13
1100: —//— на 14
1101: —//— на 15
1110: —//— на 16
1111: —//— на 16
Два последних значения соответствуют одинаковому коэффициенту умножения.
PLLXTPRE — Делитель частоты с HSE генератора перед подачей на PLL. Этот бит не может быть изменен, если PLL запущен. При установке в 1 частота HSE будет поделена на 2, если 0, то делитель отключен.
PLLSRC — Источник входной частоты PLL. Не может быть изменен, если PLL запущен.
0: частота HSI генератора поделенная на 2
1: частота HSE генератора. Делитель может быть выбран PLLXTPRE битом.
PPRE2 — Делитель шины APB2 prescaler
0xx: HCLK без деления
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
PPRE1 — Делитель шины APB1 prescaler. Частота шины APB1 не должна превышать 36 МГц.
0xx: HCLK без деления
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
HPRE — AHB prescaler
0xxx: SYSCLK без деления
1000: SYSCLK / 2
1001: SYSCLK / 4
1010: SYSCLK / 8
1011: SYSCLK / 16
1100: SYSCLK / 64
1101: SYSCLK / 128
1110: SYSCLK / 256
1111: SYSCLK / 512
SWS — Состояние переключателя тактирования системы. Устанавливается аппаратно и указывает на текущий источник тактирования.
00: HSI генератор используется как источник тактирования системы
01: HSE генератор используется как источник тактирования системы
10: PLL используется как источник тактирования системы
SW — Переключатель источника тактирования системы. Изменяется программно для выбора источника SYSCLK. Устанавливается аппаратно для принудительного переключения на HSI генератор переходе в режим Stop или Standby или в случае срыва генерации HSE, который используется в качестве источника SYSCLK (только если активна система CSS)
00: HSI выбран в качестве источника системного тактирования
01: HSE выбран в качестве источника системного тактирования
10: PLL выбран в качестве источника системного тактирования
Коэффициенты
Ранее мы рассмотрели вариант тактирования системы тактирования от HSE генератора через PLL, для удобства скопирую это сюда:
Кварц HSE на 8 МГц
PLLXTPRE: без деления
PLLSRC: HSE генератор
PLLMUL = 9
SW = PLLCLK
AHB Prescaler = 1
APB1 Prescaler = 2
APB2 Prescaler = 1
И картинку тоже: Регистры в CMSIS
Во одной из тем мы учились подключать библиотеку CMSIS к IAR-у, сейчас нам понадобится этот проект, так как мы переходим к практике. Но перед этим немного поговорим о том, как устроено обращение к регистрам периферии в CMSIS.
Каждый экземпляр периферии является структурой, в которой находятся все регистры, относящиеся к данному устройству. Почти во всех случаях имя структуры совпадает с именем периферийного модуля. Для микроконтроллера STM32F103C8 все структуры периферийных модулей объявлены в файле stm32f103xb.h:
Код: Выделить всё
#define TIM2 ((TIM_TypeDef *)TIM2_BASE)
#define TIM3 ((TIM_TypeDef *)TIM3_BASE)
#define TIM4 ((TIM_TypeDef *)TIM4_BASE)
#define RTC ((RTC_TypeDef *)RTC_BASE)
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
#define IWDG ((IWDG_TypeDef *)IWDG_BASE)
#define SPI2 ((SPI_TypeDef *)SPI2_BASE)
#define USART2 ((USART_TypeDef *)USART2_BASE)
#define USART3 ((USART_TypeDef *)USART3_BASE)
#define I2C1 ((I2C_TypeDef *)I2C1_BASE)
#define I2C2 ((I2C_TypeDef *)I2C2_BASE)
#define USB ((USB_TypeDef *)USB_BASE)
#define CAN1 ((CAN_TypeDef *)CAN1_BASE)
#define BKP ((BKP_TypeDef *)BKP_BASE)
#define PWR ((PWR_TypeDef *)PWR_BASE)
#define AFIO ((AFIO_TypeDef *)AFIO_BASE)
#define EXTI ((EXTI_TypeDef *)EXTI_BASE)
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define ADC2 ((ADC_TypeDef *)ADC2_BASE)
#define ADC12_COMMON ((ADC_Common_TypeDef *)ADC1_BASE)
#define TIM1 ((TIM_TypeDef *)TIM1_BASE)
#define SPI1 ((SPI_TypeDef *)SPI1_BASE)
#define USART1 ((USART_TypeDef *)USART1_BASE)
#define SDIO ((SDIO_TypeDef *)SDIO_BASE)
#define DMA1 ((DMA_TypeDef *)DMA1_BASE)
#define DMA1_Channel1 ((DMA_Channel_TypeDef *)DMA1_Channel1_BASE)
#define DMA1_Channel2 ((DMA_Channel_TypeDef *)DMA1_Channel2_BASE)
#define DMA1_Channel3 ((DMA_Channel_TypeDef *)DMA1_Channel3_BASE)
#define DMA1_Channel4 ((DMA_Channel_TypeDef *)DMA1_Channel4_BASE)
#define DMA1_Channel5 ((DMA_Channel_TypeDef *)DMA1_Channel5_BASE)
#define DMA1_Channel6 ((DMA_Channel_TypeDef *)DMA1_Channel6_BASE)
#define DMA1_Channel7 ((DMA_Channel_TypeDef *)DMA1_Channel7_BASE)
#define RCC ((RCC_TypeDef *)RCC_BASE)
#define CRC ((CRC_TypeDef *)CRC_BASE)
#define FLASH ((FLASH_TypeDef *)FLASH_R_BASE)
#define OB ((OB_TypeDef *)OB_BASE)
#define DBGMCU ((DBGMCU_TypeDef *)DBGMCU_BASE)
Код: Выделить всё
RCC->CR |= RCC_CR_HSEON_Msk;
Код: Выделить всё
RCC->CR |= (1 << RCC_CR_HSEON_Pos);
Код: Выделить всё
#define RCC_CR_HSEON_Msk (1<<16)
Код: Выделить всё
#define RCC_CR_HSEON RCC_CR_HSEON_Msk
Второй случай выглядит аналогичным образом: где
Код: Выделить всё
#define RCC_CR_HSEON_Pos 16
А как быть с параметрами, которые имеют несколько битов? К примеру в регистре CFGR мы хотим установить значение множителя PLL равное девяти, имеющее код 0111 (см. рис. 2 биты PLLMUL). Тут вот такое решение:
Код: Выделить всё
RCC->CFGR |= RCC_CFGR_PLLMULL_0 | RCC_CFGR_PLLMULL_1 | RCC_CFGR_PLLMULL_2;
RCC->CFGR &= ~(RCC_CFGR_PLLMULL_3);
Код: Выделить всё
RCC->CFGR |= RCC_CFGR_PLLMULL_0 | RCC_CFGR_PLLMULL_1 | RCC_CFGR_PLLMULL_2;
Код: Выделить всё
RCC->CFGR |= (7 << RCC_CFGR_PLLMULL_Pos);
Настройка
Для правильной настройки системы тактирования в микроконтроллерах STM32 необходимо соблюдать определенную последовательность запуска блоков. Если мы хотим переключить систему с внутреннего RC-генератора на HSE через PLL, то необходимо провести следующие операции вот в таком порядке:
Запустить генератор HSE
Настроить PLL
Запустить PLL
Настроить количество циклов ожидания FLASH
Настроить делители шин
Переключиться на работу от PLL
Так, вроде все понятно, хотя… А что это за 4-й пункт? Что за циклы ожидания FLASH? Вот здесь кроется один неочевидный момент. Дело в том, что FLASH-память микроконтроллера, в которой хранится управляющая программа, может работать на максимальной частоте 24 МГц. Обмен данными с FLASH осуществляется через шину AHB (см. рис. 3). А если частота шины AHB выше 24 МГц, но необходимо ввести циклы ожидания обращений к этой памяти, примем, чем выше частота, тем больше этих циклов надо:
ноль циклов ожидания, если 0 < SYSCLK ≤ 24 MHz
один цикл ожидания, если 24 MHz < SYSCLK ≤ 48 MHz
два цикла ожидания, если 48 MHz < SYSCLK ≤ 72 MHz
Надеюсь, с этим все ясно.
Хочу отметить, что порядок настройки может быть и таким:
Настроить количество циклов ожидания FLASH
Настроить делители шин
Запустить генератор HSE
Настроить PLL
Запустить PLL
Переключиться на работу от PLL
Просто надо помнить, что мы сидим на ветке, которую пилим, и важно все отпилить и подставить в нужной последовательности, чтоб не упасть. В случае с STM32, «упасть» не получится, так как в этих микроконтроллерах реализована аппаратная защита от неправильной последовательности действий: мы не сможем остановить генератор, от которого сейчас работаем или переключиться на источник тактового сигнала, если он еще не запущен. Так что в худшем случае у нас просто ни чего не получится, что то же является не очень приятным.
Итак, поехали! Возьмем за основу проект, который мы создали ранее, когда подключали CMSIS: https://github.com/DiMoonElec/stm32f103c8_empty_project.
Создадим функцию, в которую будем добавлять код инициализации:
Код: Выделить всё
int ClockInit(void)
{
}
Код: Выделить всё
RCC->CR |= (1<<RCC_CR_HSEON_Pos); //Запускаем генератор HSE
Код: Выделить всё
__IO int StartUpCounter;
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_HSERDY_Pos))
break;
//Если не запустилось, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
return 1;
}
}
Так, HSE запустили. Переходим к настройке PLL:
Код: Выделить всё
//Настраиваем PLL
RCC->CFGR |= (0x07<<RCC_CFGR_PLLMULL_Pos) //PLL множитель равен 9
| (0x01<<RCC_CFGR_PLLSRC_Pos); //Тактирование PLL от HSE
Код: Выделить всё
RCC->CR |= (1<<RCC_CR_PLLON_Pos); //Запускаем PLL
Код: Выделить всё
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_PLLRDY_Pos))
break;
//Если по каким-то причинам не запустился PLL, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
RCC->CR &= ~(1<<RCC_CR_PLLON_Pos); //Останавливаем PLL
return 2;
}
}
Код: Выделить всё
//Устанавливаем 2 цикла ожидания для Flash
//так как частота ядра у нас будет 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= (0x02<<FLASH_ACR_LATENCY_Pos);
//Делители
RCC->CFGR |= (0x00<<RCC_CFGR_PPRE2_Pos) //Делитель шины APB2 отключен (оставляем 0 по умолчанию)
| (0x04<<RCC_CFGR_PPRE1_Pos) //Делитель нишы APB1 равен 2
| (0x00<<RCC_CFGR_HPRE_Pos); //Делитель AHB отключен (оставляем 0 по умолчанию)
Код: Выделить всё
RCC->CFGR |= (0x02<<RCC_CFGR_SW_Pos); //Переключаемся на работу от PLL
Код: Выделить всё
//Ждем, пока переключимся
while((RCC->CFGR & RCC_CFGR_SWS_Msk) != (0x02<<RCC_CFGR_SWS_Pos))
{
}
Код: Выделить всё
//После того, как переключились на
//внешний источник такирования
//отключаем внутренний RC-генератор
//для экономии энергии
RCC->CR &= ~(1<<RCC_CR_HSION_Pos);
Код: Выделить всё
//Настраиваем тактирование системы от внешнего кварца
//через PLL на саксимально возможных частотах.
//Внешний кварц должен быть на 8МГц
//Возвращает:
// 0 - завершено успешно
// 1 - не запустился кварцевый генератор
// 2 - не запустился PLL
int ClockInit(void)
{
__IO int StartUpCounter;
////////////////////////////////////////////////////////////
//Запускаем кварцевый генератор
////////////////////////////////////////////////////////////
RCC->CR |= (1<<RCC_CR_HSEON_Pos); //Запускаем генератор HSE
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_HSERDY_Pos))
break;
//Если не запустилось, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
return 1;
}
}
////////////////////////////////////////////////////////////
//Настраиваем и запускаем PLL
////////////////////////////////////////////////////////////
//Настраиваем PLL
RCC->CFGR |= (0x07<<RCC_CFGR_PLLMULL_Pos) //PLL множитель равен 9
| (0x01<<RCC_CFGR_PLLSRC_Pos); //Тактирование PLL от HSE
RCC->CR |= (1<<RCC_CR_PLLON_Pos); //Запускаем PLL
//Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter=0; ; StartUpCounter++)
{
//Если успешно запустилось, то
//выходим из цикла
if(RCC->CR & (1<<RCC_CR_PLLRDY_Pos))
break;
//Если по каким-то причинам не запустился PLL, то
//отключаем все, что включили
//и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Останавливаем HSE
RCC->CR &= ~(1<<RCC_CR_PLLON_Pos); //Останавливаем PLL
return 2;
}
}
////////////////////////////////////////////////////////////
//Настраиваем FLASH и делители
////////////////////////////////////////////////////////////
//Устанавливаем 2 цикла ожидания для Flash
//так как частота ядра у нас будет 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= (0x02<<FLASH_ACR_LATENCY_Pos);
//Делители
RCC->CFGR |= (0x00<<RCC_CFGR_PPRE2_Pos) //Делитель шины APB2 отключен
| (0x04<<RCC_CFGR_PPRE1_Pos) //Делитель нишы APB1 равен 2
| (0x00<<RCC_CFGR_HPRE_Pos); //Делитель AHB отключен
RCC->CFGR |= (0x02<<RCC_CFGR_SW_Pos); //Переключаемся на работу от PLL
//Ждем, пока переключимся
while((RCC->CFGR & RCC_CFGR_SWS_Msk) != (0x02<<RCC_CFGR_SWS_Pos))
{
}
//После того, как переключились на
//внешний источник такирования
//отключаем внутренний RC-генератор
//для экономии энергии
RCC->CR &= ~(1<<RCC_CR_HSION_Pos);
//Настройка и переклбючение сисемы
//на внешний кварцевый генератор
//и PLL запершилось успехом.
//Выходим
return 0;
}