Первоначально плата STM32F103 работает от внутреннего генератора на частоте 48 мГц.
Сделал настройку по ссылке но ничего не поменялось на двух разных платах. Почему может не работать?
https://dimoon.ru/obuchalka/stm32f1/uroki-stm32f103-chast-4-nastroyka-rcc.html?ysclid=m77p7n5frs371276003
Код.
//#include <stm32f1xx.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd (PA9, PA8, PB15, PB14, PB13, PB12); //(RS,E,D4,D5,D6,D7)
uint32_t Tay_ind = 1000000;
uint32_t time_new_ind,time_old_ind,time_del_ind;
//-------------------------------------------------------------------------//
int ClockInit(void)
{
__IO int StartUpCounter;
////////////////////////////////////////////////////////////
// Запускаем кварцевый генератор
////////////////////////////////////////////////////////////
RCC->CR |= RCC_CR_HSEON; // Запускаем генератор HSE
// Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter = 0; ; StartUpCounter++)
{
// Если успешно запустилось, то выходим из цикла
if(RCC->CR & RCC_CR_HSERDY)
break;
// Если не запустилось, то отключаем все, что включили и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~RCC_CR_HSEON; // Останавливаем HSE
return 1;
}
}
////////////////////////////////////////////////////////////
// Настраиваем и запускаем PLL
////////////////////////////////////////////////////////////
// Настраиваем PLL
RCC->CFGR |= RCC_CFGR_PLLMULL2 // PLL множитель равен 9
| RCC_CFGR_PLLSRC; // Тактирование PLL от HSE
RCC->CR |= RCC_CR_PLLON; // Запускаем PLL
// Ждем успешного запуска или окончания тайм-аута
for(StartUpCounter = 0; ; StartUpCounter++)
{
// Если успешно запустилось, то выходим из цикла
if(RCC->CR & RCC_CR_PLLRDY)
break;
// Если по каким-то причинам не запустился PLL, то отключаем все, что включили и возвращаем ошибку
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~RCC_CR_HSEON; // Останавливаем HSE
RCC->CR &= ~RCC_CR_PLLON; // Останавливаем PLL
return 2;
}
}
////////////////////////////////////////////////////////////
// Настраиваем FLASH и делители
////////////////////////////////////////////////////////////
// Устанавливаем 2 цикла ожидания для Flash
// так как частота ядра у нас будет 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= FLASH_ACR_LATENCY_2;
// Делители
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1 // Делитель шины APB2 отключен
| RCC_CFGR_PPRE1_DIV2 // Делитель шины APB1 равен 2
| RCC_CFGR_HPRE_DIV1; // Делитель AHB отключен
RCC->CFGR |= RCC_CFGR_SW_PLL; // Переключаемся на работу от PLL
// Ждем, пока переключимся
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
{
}
// После того, как переключились на внешний источник тактирования
// отключаем внутренний RC-генератор для экономии энергии
RCC->CR &= ~RCC_CR_HSION;
// Настройка и переключение системы на внешний кварцевый генератор и PLL завершилось успехом.
// Выходим
return 0;
}
//-------------------------------------------------------------------------//
void setup()
{
//-------------------------------------------------------------------------//
// Инициализация тактирования
if(ClockInit() != 0) {
// Обработка ошибки инициализации тактирования
while(1);
}
//-------------------------------------------------------------------------//
lcd.begin(20, 4);
//-------------------------------------------------------------------------//
// Включаем тактирование TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// Настройка TIM2 для счета миллисекунд
TIM2->CR1 = 0; // Остановка таймера
TIM2->PSC = 47999; // Предделитель 48000 (48 МГц / 48000 = 1 кГц)
TIM2->ARR = 0xFFFF; // Автоперезагрузка на максимум (16 бит)
TIM2->CCER &= ~TIM_CCER_CC1P; // Полярность: считаем по фронту
TIM2->EGR = 1; // Обновление регистров
TIM2->CR1 |= (1 << 0); // Запуск TIM2
//-------------------------------------------------------------------------//
}
void loop()
{
time_new_ind= micros();
time_del_ind=time_new_ind-time_old_ind;
if (time_del_ind>=Tay_ind)
{
//-----------------------------------------------------------------------//
uint32_t milliseconds = TIM2->CNT;
uint32_t sysclk_freq = HAL_RCC_GetSysClockFreq();
//-----------------------------------------------------------------------//
lcd.clear();
lcd.setCursor(0, 0);lcd.print("T= ");lcd.print(millis());
lcd.setCursor(0, 1);lcd.print("N= ");lcd.print(milliseconds);
lcd.setCursor(0, 2);lcd.print("F= ");lcd.print(sysclk_freq);
time_old_ind=time_new_ind;
}
}
На какой частоте с таким кодом работают платы ?
В 31 строке надо смотреть что в итоге за множитель получается …RCC_CFGR_PLLMULL9 нужен скорее всего для кварца 8 МГц. Вы как то странно читали описание по ссылке и привели свой вариант кода !!!
Если первая строка = комментарий - откуда берутся все константы ??? Вы уверены что это вообще компилируется и прошивается ???
Плата такая и что самое интересное прошивается и не дает ошибок. На меня это не похоже.
Плата работает на частоте 48 мГц. В 86 строке настраиваю предделитель. Время с таймера и время миллисекунд отображаются синхронно.
https://aliexpress.ru/item/1005004918334754.html?sku_id=12000034303875973&spm=a2g2w.stores.seller_list.3.2ee647dcIl7d7x
Кварц какой на плате ?
И тут в процессе возник еще вопрос - на какой частоте вы хотите запустить микроконтроллер ?
если это действительно плата от Weact, то они только 8мГц.
Ну для начала хотел бы запустить на 72 мгц. Но пока получилось только запустить на 8 мГц от внутреннего генератора. Строки с 6 по 17.
//работает на частоте 8 мгц от внутреннего генератора
#include <LiquidCrystal.h>
LiquidCrystal lcd (PA9, PA8, PB15, PB14, PB13, PB12); //(RS,E,D4,D5,D6,D7)
uint32_t Tay_ind = 1000000;
uint32_t time_new_ind,time_old_ind,time_del_ind;
//-------------------------------------------------------------------------//
void SystemClock_Config(void) {
// Включение HSI (внутренний генератор 8 МГц)
RCC->CR |= RCC_CR_HSION; // Включение HSI
while (!(RCC->CR & RCC_CR_HSIRDY)); // Ожидание готовности HSI
// Переключение на HSI как источник тактирования
RCC->CFGR &= ~RCC_CFGR_SW; // Сброс источника тактирования
RCC->CFGR |= RCC_CFGR_SW_HSI; // Использование HSI как источника тактирования
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); // Ожидание переключения на HSI
}
//-------------------------------------------------------------------------//
void setup()
{
//-------------------------------------------------------------------------//
SystemClock_Config(); // Настройка тактирования
//-------------------------------------------------------------------------//
lcd.begin(20, 4);
//-------------------------------------------------------------------------//
// Включаем тактирование TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// Настройка TIM2 для счета миллисекунд
TIM2->CR1 = 0; // Остановка таймера
TIM2->PSC = 7999; // Предделитель 48000 (8 МГц / 8000 = 1 кГц)
TIM2->ARR = 0xFFFF; // Автоперезагрузка на максимум (16 бит)
TIM2->CCER &= ~TIM_CCER_CC1P; // Полярность: считаем по фронту
TIM2->EGR = 1; // Обновление регистров
TIM2->CR1 |= (1 << 0); // Запуск TIM2
//-------------------------------------------------------------------------//
}
void loop()
{
time_new_ind= micros();
time_del_ind=time_new_ind-time_old_ind;
if (time_del_ind>=Tay_ind)
{
//-----------------------------------------------------------------------//
uint32_t milliseconds = TIM2->CNT;
uint32_t sysclk_freq = HAL_RCC_GetSysClockFreq();
//-----------------------------------------------------------------------//
lcd.clear();
lcd.setCursor(0, 0);lcd.print("T= ");lcd.print(millis());
lcd.setCursor(0, 1);lcd.print("N= ");lcd.print(milliseconds);
lcd.setCursor(0, 2);lcd.print("F= ");lcd.print(sysclk_freq);
time_old_ind=time_new_ind;
}
}
Запустил в работу от внешнего генератора на 8 мгц.
//-------------------------------------------------------------------------//
void SystemClock_Config(void) {
// Включение HSE (внешний кварцевый генератор 8 МГц)
RCC->CR |= RCC_CR_HSEON; // Включение HSE
while (!(RCC->CR & RCC_CR_HSERDY)); // Ожидание готовности HSE
// Настройка FLASH (для работы на частоте HSE)
FLASH->ACR |= FLASH_ACR_PRFTBE; // Включение предварительной выборки
FLASH->ACR &= ~FLASH_ACR_LATENCY; // Сброс задержки
FLASH->ACR |= FLASH_ACR_LATENCY_0; // Установка задержки в 0 тактов (для частот до 24 МГц)
// Переключение на HSE как источник тактирования
RCC->CFGR &= ~RCC_CFGR_SW; // Сброс источника тактирования
RCC->CFGR |= RCC_CFGR_SW_HSE; // Использование HSE как источника тактирования
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE); // Ожидание переключения на HSE
}
//-------------------------------------------------------------------------//
Добавил настройку шин.
void SystemClock_Config(void) {
// Включение HSE (внешний кварцевый генератор 8 МГц)
RCC->CR |= RCC_CR_HSEON; // Включение HSE
while (!(RCC->CR & RCC_CR_HSERDY)); // Ожидание готовности HSE
// Настройка FLASH (для работы на частоте HSE)
FLASH->ACR |= FLASH_ACR_PRFTBE; // Включение предварительной выборки
FLASH->ACR &= ~FLASH_ACR_LATENCY; // Сброс задержки
FLASH->ACR |= FLASH_ACR_LATENCY_0; // Установка задержки в 0 тактов (для частот до 24 МГц)
// Настройка делителей шин
RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2); // Сброс делителей
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB без деления (частота = HSE = 8 МГц)
RCC->CFGR |= RCC_CFGR_PPRE1_DIV1; // APB1 без деления (частота = HSE = 8 МГц)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2 без деления (частота = HSE = 8 МГц)
// Переключение на HSE как источник тактирования
RCC->CFGR &= ~RCC_CFGR_SW; // Сброс источника тактирования
RCC->CFGR |= RCC_CFGR_SW_HSE; // Использование HSE как источника тактирования
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE); // Ожидание переключения на HSE
}
пример функции инициализации RCC для STM32F103C8T6 с использованием внешнего кварца 8 МГц для достижения тактовой частоты 72 МГц:
#include "stm32f10x.h"
void SystemClock_Config(void) {
// 1. Включение HSE и ожидание готовности
RCC->CR |= RCC_CR_HSEON; // Включить HSE
while(!(RCC->CR & RCC_CR_HSERDY)); // Ждем стабилизации генератора
// 2. Настройка задержки Flash-памяти
FLASH->ACR |= FLASH_ACR_LATENCY_2; // Два цикла ожидания (для 48-72 МГц)
FLASH->ACR |= FLASH_ACR_PRFTBE; // Включить предварительную выборку
// 3. Настройка делителей шин
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB = 72 MHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 = 36 MHz
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2 = 72 MHz
// 4. Конфигурация PLL (8MHz * 9 = 72MHz)
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE; // Источник PLL - HSE
RCC->CFGR |= RCC_CFGR_PLLMULL9; // Множитель PLL x9
// 5. Включение PLL и ожидание готовности
RCC->CR |= RCC_CR_PLLON; // Запуск PLL
while(!(RCC->CR & RCC_CR_PLLRDY)); // Ждем готовности PLL
// 6. Переключение на PLL как источник системного такта
RCC->CFGR |= RCC_CFGR_SW_PLL; // Выбираем PLL как системный источник
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Ждем переключения
}
Решение вопроса оказалось сложным. Но вопрос решился. Плохо что никто не подсказал способ решения проблемы. В программе написан код для внешнего кварцевого резонатора. А у меня на плате стоит внешний кварцевый генератор. Поэтому код уже другой. Осталось только выяснить почему в регистры пишется одна информация, а считывается другая.
// Версия 11.2 (STM32F103 + TTL 8 МГц, 7 тестов + отладка)
#include <LiquidCrystal.h>
LiquidCrystal lcd(PA9, PA8, PB15, PB14, PB13, PB12); // RS, E, D4-D7
// Глобальные переменные
uint32_t time_old_ind = 0; // Для отслеживания времени (секунды)
uint32_t stepStartTime = 0; // Время начала текущего этапа
uint32_t regDisplayTime = 0; // Время последнего обновления регистра
uint8_t setupStep = 0; // Текущий этап настройки
uint8_t debugStep = 0; // Текущий отображаемый регистр
bool setupCompleted = false; // Флаг завершения настройки
bool messageShown = false; // Флаг отображения сообщения
// Структура для хранения информации о регистрах
struct RegisterInfo {
const char* name; // Имя регистра
uint32_t expected; // Ожидаемое значение
volatile uint32_t* reg; // Указатель на регистр
};
// Инициализация регистров для отладки
RegisterInfo regs[] = {
{"RCC_CR", RCC_CR_HSEON | RCC_CR_HSERDY | RCC_CR_PLLON | RCC_CR_PLLRDY, &RCC->CR},
{"RCC_CFGR", RCC_CFGR_PLLXTPRE_HSE_DIV2 | RCC_CFGR_PLLMULL2 | RCC_CFGR_PLLSRC | RCC_CFGR_SW_PLL, &RCC->CFGR}
};
// Функция расчета частоты процессора
uint32_t getSysClockFreq() {
uint32_t hse = 8000000; // Частота внешнего генератора (8 МГц)
uint32_t pllDiv = 2; // Делитель HSE (по умолчанию /2)
uint32_t pllMul = 2; // Множитель PLL (по умолчанию x2)
// Чтение делителя из регистра RCC_CFGR
if (RCC->CFGR & RCC_CFGR_PLLXTPRE_HSE_DIV2) pllDiv = 2;
// Чтение множителя из регистра RCC_CFGR
switch (RCC->CFGR & RCC_CFGR_PLLMULL) {
case RCC_CFGR_PLLMULL2: pllMul = 2; break;
// Добавьте другие множители при необходимости
}
return (hse / pllDiv) * pllMul; // Расчет итоговой частоты
}
// Настройка LCD и тактирования
void setup() {
lcd.begin(20, 4); // Инициализация LCD 20x4
RCC->CR |= RCC_CR_HSEBYP; // Активация bypass режима для TTL
pinMode(PC14, INPUT); // Настройка OSC_IN как входа
}
// Основной цикл программы
void loop() {
if (!setupCompleted) {
switch (setupStep) {
// Шаг 0: Включение HSE
case 0:
RCC->CR |= RCC_CR_HSEON; // Активация HSE
lcd.clear();
lcd.print("Test 1: HSE...");
setupStep++;
stepStartTime = millis();
break;
// Шаг 1: Ожидание HSE
case 1:
if (RCC->CR & RCC_CR_HSERDY) {
if (!messageShown && millis() - stepStartTime >= 1000) {
lcd.clear();
lcd.print("Test 2: HSE OK");
messageShown = true;
stepStartTime = millis();
}
if (messageShown && millis() - stepStartTime >= 1000) {
setupStep++;
messageShown = false;
}
} else if (millis() - stepStartTime > 2000) {
lcd.print("HSE FAIL!");
while(1);
}
break;
// Шаг 2: Настройка делителя HSE/2
case 2:
RCC->CFGR |= RCC_CFGR_PLLXTPRE_HSE_DIV2;
if (!messageShown) {
lcd.clear();
lcd.print("Test 3: DIV OK");
messageShown = true;
stepStartTime = millis();
}
if (millis() - stepStartTime >= 1000) {
setupStep++;
messageShown = false;
}
break;
// Шаг 3: Настройка множителя PLL x2
case 3:
RCC->CFGR |= RCC_CFGR_PLLMULL2;
if (!messageShown) {
lcd.clear();
lcd.print("Test 4: PLL MUL OK");
messageShown = true;
stepStartTime = millis();
}
if (millis() - stepStartTime >= 1000) {
setupStep++;
messageShown = false;
}
break;
// Шаг 4: Выбор источника PLL (HSE)
case 4:
RCC->CFGR |= RCC_CFGR_PLLSRC;
if (!messageShown) {
lcd.clear();
lcd.print("Test 5: PLL SRC OK");
messageShown = true;
stepStartTime = millis();
}
if (millis() - stepStartTime >= 1000) {
setupStep++;
messageShown = false;
}
break;
// Шаг 5: Включение PLL
case 5:
RCC->CR |= RCC_CR_PLLON;
if (RCC->CR & RCC_CR_PLLRDY) {
if (!messageShown) {
lcd.clear();
lcd.print("Test 6: PLL RDY");
messageShown = true;
stepStartTime = millis();
}
if (millis() - stepStartTime >= 1000) {
setupStep++;
messageShown = false;
}
}
break;
// Шаг 6: Переключение на PLL
case 6:
RCC->CFGR |= RCC_CFGR_SW_PLL;
if ((RCC->CFGR & RCC_CFGR_SWS) == RCC_CFGR_SWS_PLL) {
if (!messageShown) {
lcd.clear();
lcd.print("Test 7: SYSCLK OK");
messageShown = true;
stepStartTime = millis();
}
if (millis() - stepStartTime >= 1000) {
setupCompleted = true;
}
}
break;
}
} else {
// Режим отладки после настройки
if (millis() - regDisplayTime >= 6000) {
debugStep = (debugStep + 1) % 2;
regDisplayTime = millis();
}
if (millis() - time_old_ind >= 1000) {
// Строка 0: Ожидаемое значение регистра (EX: ...)
lcd.setCursor(0, 0);
lcd.print(regs[debugStep].name);
lcd.print(" EX:0x"); // Сокращено с "EXP:0x"
lcd.print(regs[debugStep].expected, HEX);
lcd.print(" ");
// Строка 1: Фактическое значение регистра (AC: ...)
lcd.setCursor(0, 1);
lcd.print(regs[debugStep].name);
lcd.print(" AC:0x"); // Сокращено с "ACT:0x"
lcd.print(*regs[debugStep].reg, HEX);
lcd.print(" ");
// Строка 2: Частота процессора (обновляется каждую секунду)
lcd.setCursor(0, 2);
lcd.print("Freq:");
lcd.print(getSysClockFreq() / 1000000);
lcd.print("MHz ");
// Строка 3: Время работы (обновляется каждую секунду)
lcd.setCursor(0, 3);
lcd.print("T=");
lcd.print(millis() / 1000);
lcd.print("s ");
time_old_ind = millis();
}
}
}