Как затактировать систему
То решение, какое предлагает фирменный фреймфорк (или как это китайцы называют у себя на фирме) мне, честно говоря, вообще не нравится. Я уже писал об этом. Там, в их коде полно ошибок, а, кроме того, компилятор генерирует много ненужного кода.
Я вообще не понимаю, зачем нужно использовать структуру GPIO_InitStructure разработчику, который, знает состав портов ввода-вывода и как они работают.
Вот, пример, кода который, я взял из фреймворка:
void GPIO_Toggle_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
Возможно кому-то это и нравится, но я считаю, что это какой-то ужас.
Вместо того, чтобы одной командой включить тактирование порта, а другой сконфигурировать все известные на данный момент биты, — создаётся структура. Затем инициализируются её поля. Потом вызывается функция GPIO_Init().
Вы заглядывали во внутрь, что делает функция GPIO_Init()? — Нет?!!
Не поленитесь, загляните! Получите массу удовольствия. Или инфаркт.
И как изощрённое издевательство — не поленитесь просмотреть код настройки тактирования, который находится в файлах system_ch32v00x.c и system_ch32v00x.h. Я думаю, после этого вы поймёте моё негодование.
Понятно, что весь этот «сервис» создаёт огромную кучу бессмысленного и беспощадного кода.
Вы спросите — а для чего это всё так сделано? Точного ответа я не знаю, но предполагаю, что в основе лежит вполне благородная цель — типа чтобы программист не изучал регистры, а пользовался нашим замечательным фреймворком. Скорее всего предполагается, что такое решение сэкономит время разработки программы, а значит -- не сильно увеличит цену изделия. Ну, не знаю... сэкономит ли.
А с другой стороны. Ну, хорошо. Теперь программисту не надо изучать регистры и назначение битов. А как насчёт того, что вместо этого программисту придётся изучать сам фреймворк? Нет ли в этом подходе того, что вместо изучения одной (базовой) сущности, нужно изучать другую (производную) сущность? Ладно, если производная сущность окажется проще. А если нет? А если, тем паче, — что-то пойдёт не так? Тогда программисту придётся изучать "обе две" сущности — и фреймворк, и регистры. Вот, счастье-то!
Короче, хотите быть ардуинщиком — будьте им! Никто вам не мешает! А кто уважительно относится к технике, того прошу за мной!
Итак, ниже я провожу код инициализации тактирования от встроенного генератора. В этом коде я не использую PLL, поэтому ядро микроконтроллера будет работать на частоте 24 МГц. Поскольку частота не высокая, то флеш-памяти не потребуется дополнительный такт для работы.
void system_init(void)
{
RCC->CTLR |= RCC_HSION; // Включаю HSI (24 МГц)
RCC->CFGR0 &= 0x00000000; // Отключаю MCO, пределители в исходное сотояние, HSI
RCC->CTLR &= ~(RCC_PLLON | RCC_CSSON | RCC_HSEON); // Выключаю PLL и HSE
RCC->CTLR &= ~RCC_HSEBYP; // Выключаю байпас
RCC->INTR = (RCC_CSSC | RCC_PLLRDYC | RCC_HSERDYC | RCC_HSIRDYC | RCC_LSIRDYC); // Сбрасываю флаги
FLASH->ACTLR &= ~FLASH_ACTLR_LATENCY;
FLASH->ACTLR |= FLASH_ACTLR_LATENCY_0; // Цикл обращения к флеш-памяти
RCC->CFGR0 |= RCC_HPRE_DIV1; // HCLK = SYSCLK = 24 MHz
}
Вот такой рабочий код размером в полтора десятка строк.
Если мне понадобится, к примеру, затактировать систему от внешнего кварцевого резонатора или увеличить частоту в два раза, я тупо сделаю копию этого кода и изменю нужные мне параметры, а исходный код (чтобы не потерялся) просто закомментирую. И не надо говнокодить никаких заумныйх решений на все случаи жизни. Ни к чему это.
Потом, спустя какое-то время, я могу снова вернуться к этим исходным кодам и выбрать то, что мне будет нужно в следующем проекте. Разумеется, рядом с кодом нужно разместить комментарии.
Во первых, это красиво. (с)
А во вторых, код таким образом получается наглядный, понятный, осязаемый. В отличие от говнокода из фреймворка. Ведь при таком подходе будет сразу понятно, что этот код делает.
Мне остаётся только добавить, что функция system_init() вызывается из ассемблерного файла startup_ch32v00x.S.
Вот, несколько последних строк этого файла:
...
la t0, _start
ori t0, t0, 3
csrw mtvec, t0
jal system_init
la t0, main
csrw mepc, t0
mret
Да, это ассемблер RISC-V. Это не важно, что вы сейчас ничего не понимаете, что тут происходит. Я как-нибудь потом вернусь к этому файлу.
Сейчас важно чтобы вы увидели команду вызова функции system_init() и команду вызова функции main(). Заметьте, что сначала вызывается функция system_init(). А функция main() начинает работать, когда оборудование уже настроено.
1 Комментарий
Рекомендуемые комментарии
Присоединяйтесь к обсуждению
Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.