В предыдущей части урока мы изучиkb устройство RCC, а также познакомились с его основными регистрами.
Сначала напомню то, что схема наша со времён прошлого урока не изменилась
Проект мы также сделаем из проекта прошлого урока с именем BLINK01_CMSIS и назовём его CMSIS_RCC.
Напомню, как мы делаем проект из другого для Keil без Cube MX.
Создадим папку с новым названием
Скопируем в неё все папки из архива с проектом-донором, а файлы только главные два с настройками проекта
Затем переименуем эти два файла в новое имя
Файл с зелёной иконкой (с расширением uvprojx) откроем в блокноте и исправим там старое имя на новое вот здесь
Также нужно будет изменить имя старое на новое в строке создания файла bin. Можно сделать это и в блокноте, а можно и потом в настройках проекта уже в Keil. Сделаем это сейчас здесь, в файле
<UserProg1Name>fromelf —bin —output .ObjectsCMSIS_RCC.bin .ObjectsCMSIS_RCC.axf</UserProg1Name>
Когда я снимал видео прошлого урока, у меня не формировался файл bin из-за того, что вместо двух тире (минусов) WordPress на сайте подменил их в одно длинное, которое скопировалось в строку в кейле, как одно короткое, так что не забывайте, что перед bin и output не длинное тире, а именно два коротких.
Вот и всё по преобразованию проекта в новый с новым именем.
Откроем наш новоиспечённый проект, затем откроем файл main.c и удалим пока вот эту ненужную глобальную переменную
uint8_t time2_count = 0;
Прежде чем начинать инициализацию RCC, советуют сбросить его настройки, то есть проделать обратную операцию — деинициализацию.
Поэтому добавим вот такую функцию, пока пустотелую
1 2 3 4 5 |
//---------------------------------------------------------- void RCC_DeInit(void) { } //---------------------------------------------------------- |
Включим для начала HSI (внутренний генератор 8 МГц)
1 2 3 |
void RCC_DeInit(void) { SET_BIT(RCC->CR, RCC_CR_HSION); |
Дождёмся его стабилизации
1 2 |
SET_BIT(RCC->CR, RCC_CR_HSION); while(READ_BIT(RCC->CR, RCC_CR_HSIRDY == RESET)) {} |
Сбросим калибровку
1 2 |
while(READ_BIT(RCC->CR, RCC_CR_HSIRDY == RESET)) {} MODIFY_REG(RCC->CR, RCC_CR_HSITRIM, 0x80U); |
Полностью очистим конфигурационный регистр
1 2 |
MODIFY_REG(RCC->CR, RCC_CR_HSITRIM, 0x80U); CLEAR_REG(RCC->CFGR); |
Дождёмся очистку бита SWS
1 2 |
CLEAR_REG(RCC->CFGR); while (READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RESET) {} |
Аналогичным образом отключим PLL
1 2 3 |
while (READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RESET) {} CLEAR_BIT(RCC->CR, RCC_CR_PLLON); while (READ_BIT(RCC->CR, RCC_CR_PLLRDY) != RESET) {} |
Выключим HSE и его детектор тактового сигнала, дождавшись затем отключения HSE
1 2 3 |
while (READ_BIT(RCC->CR, RCC_CR_PLLRDY) != RESET) {} CLEAR_BIT(RCC->CR, RCC_CR_HSEON | RCC_CR_CSSON); while (READ_BIT(RCC->CR, RCC_CR_HSERDY) != RESET) {} |
Сбросим бит, разрешающий использование внешнего генератора
1 2 |
while (READ_BIT(RCC->CR, RCC_CR_HSERDY) != RESET) {} CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); |
Сбросим флаги всех прерываний от RCC
1 2 3 |
CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); //Reset all CSR flags SET_BIT(RCC->CSR, RCC_CSR_RMVF); |
Также запретим все прерывания от RCC
1 2 3 |
SET_BIT(RCC->CSR, RCC_CSR_RMVF); //Disable all interrupts CLEAR_REG(RCC->CIR); |
Вызовем нашу функцию в функции main()
1 2 3 |
int main(void) { RCC_DeInit(); |
Соберём проект прошьём контроллер и мы увидим, что светодиод по планке будем бежать очень медленно.
Отсюда сделаем вывод, что без настройки RCC у нас контроллер работал вообще на какой-то непонятной частоте. Хотя можно всё это просчитать, внимательно просмотрев в документации настройки битов по умолчанию и выявить, что включено. Но мы этого делать не будем.
Для лучшего эффекта и для оценки преимущества генератора HSE немного исправим величину задержки в бесконечном цикле
LED10_OFF(); LED1_ON(); delay(650000);
LED1_OFF(); LED2_ON(); delay(650000);
LED2_OFF(); LED3_ON(); delay(650000);
LED3_OFF(); LED4_ON(); delay(650000);
LED4_OFF(); LED5_ON(); delay(650000);
LED5_OFF(); LED6_ON(); delay(650000);
LED6_OFF(); LED7_ON(); delay(650000);
LED7_OFF(); LED8_ON(); delay(650000);
LED8_OFF(); LED9_ON(); delay(650000);
LED9_OFF(); LED10_ON(); delay(650000);
После этого светодиоды будут мигать примерно раз в секунду.
Теперь добавим функцию инициализации RCC в режиме работы от HSE с настройкой системной частоты 72 МГц, которая является максимальной для нашего контроллера
1 2 3 4 5 |
//---------------------------------------------------------- void SetSysClockTo72(void) { } //---------------------------------------------------------- |
Включим наш HSE, дождавшись его стабилизации
1 2 3 4 |
void SetSysClockTo72(void) { SET_BIT(RCC->CR, RCC_CR_HSEON); while(READ_BIT(RCC->CR, RCC_CR_HSERDY == RESET)) {} |
Теперь настройка работы с памятью FLASH. Включим буфер предварительной выборки, сначала отключив его, затем включим максимальную задержку, так как мы настраиваем максимальную частоту
1 2 3 4 5 |
while(READ_BIT(RCC->CR, RCC_CR_HSERDY == RESET)) {} //Enable the Prefetch Buffer CLEAR_BIT(FLASH->ACR, FLASH_ACR_PRFTBE); SET_BIT(FLASH->ACR, FLASH_ACR_PRFTBE); MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, FLASH_ACR_LATENCY_2); |
Настроим значения всех делителей
1 2 3 4 |
MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, FLASH_ACR_LATENCY_2); MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_CFGR_HPRE_DIV1); MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, RCC_CFGR_PPRE2_DIV1); MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_CFGR_PPRE1_DIV2); |
Настроим PLL на коэффициент 9 и настроим его вход для работы от HSE
1 2 3 |
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_CFGR_PPRE1_DIV2); MODIFY_REG(RCC->CFGR, RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL, RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9); |
Разрешим работу PLL, дождавшись затем его разблокировку
1 2 3 |
RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9); SET_BIT(RCC->CR, RCC_CR_PLLON); while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != (RCC_CR_PLLRDY)) {} |
Выберем PLL в качестве источника системного тактирования, дождавшись затем применения данного действия
1 2 3 |
while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != (RCC_CR_PLLRDY)) {} MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL); while(READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {} |
Вызовем нашу функцию в функции main()
1 2 |
SET_BIT(AFIO->MAPR, AFIO_MAPR_SWJ_CFG_JTAGDISABLE); SetSysClockTo72(); |
Соберём код, прошьём контроллер и увидим, что наши светодиоды теперь побегут с такой скоростью, что за секунду будут пробегать почти весь цикл (см. видеоверсию), что свидетельствует о том, что наш контроллер теперь действительно тактируется частотой 72 Мгц
Итак, на данном уроке мы научились настраивать модуль RCC, тем самым заставили работать наш контроллер от внешнего генератора, исполненного на кварцевом резонаторе, что позволило добиться более стабильного тактирования на максимальной частоте.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Смотреть ВИДЕОУРОК (нажмите на картинку)
А не проще отредактировать файл RTE_Device.h в Keil?
Не проще чего? Предложение не закончено. «Не проще …, чем…»
Проектов сотни, поконкретнее, пожалуйста.
Не проще чем расставлять биты, следить за флагами. Я не гуру в вопросах программирования STM, пока только учусь. И учусь по в том числе и по Вашим занятиям, за которые отдельное большое СПАСИБО.
Но мне кажется проще отредактировать файл RTE_Device.h, который создаётся Keil, при установке галочки «Startup» (Вы упоминали этот пунктик на своих занятиях). И на вкладке «Configuration Wizard», которая становится доступной при редактировании RTE_Device.h прописать все частоты работы STM
«Разрешим работу PLL, дождавшись затем его разблокировку
SET_BIT(RCC->CR, RCC_CR_PLLON);
while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != (RCC_CR_PLLRDY)) {}»
Поправьте меня пожалуйста если я не прав.
Но в этой строчке мы разрешаем работу PLL и дожидаемся его БЛОКИРОВКУ.
т.к.
PLLRDY (PLL clock ready flag): флаг готовности PLL. Устанавливается аппаратно для блокирования RCC
0 — PLL разблокирован
1 — PLL заблокирован.
и в цикле while мы именно и ждём когда бит RCC_CR_PLLRDY станет единицей что бы провести ложную операцию неравенства и выйти из цикла.
Если упростить.
while( 1 != 1 ) {}
В цикле мы спрашиваем (логическое побитовое И над содержимым регистра RCC_CR и битовой маской RCC_CR_PLLRDY == 0?). И пока мы на этот вопрос отвечаем ДА — остаемся в цикле while.
Подскажите почему мы именно это число 0х80U == 0b10000000 записываем в битовое поле, нельзя ли просто записать нули?
У вас ошибка где MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL); вы там противоположное задуманному сделали вы включаете RCC_CFGR_SW и отключаете RCC_CFGR_SW_PLL
то есть вы отключаете PLL там цикл while повиснет