- повышенная частота ядра до 400 МГц
- увеличенный объем ОЗУ, до 1 МБ
- 16 разрядный АЦП
- pin-to-pin совместимость с серий F7
Отлично подумал я, запаял на плату кристалл STM32H743IIT6 вместо STM32F746IGT6 и начал новый проект в SW4STM32.
Для расчета коэффициентов делителей и множителей системы тактирования микроконтроллера удобно пользоваться вкладкой Clock Configuration программы STM32CubeMX.
Настройки тактирования:
- внешний кварц — 8 МГц
- источник частоты для PLL1 — внешний кварц (HSE)
- делитель для PLL1 — 4 (DIVM1)
- множитель PLL1 — 400 (DIVN1)
- выходные делители — 2 (DIVP1, DIVQ1, DIVR1)
Соответственно, частота ядра (SYSCLK)- 400 МГц.
Кроме STM32CubeMX есть еще набор «STM32CubeH7 firmware package», который содержит большое количество примеров для работы с периферией для STM32H7. Именно из него была взята последовательность инициализации системы тактирования микроконтроллера.
Информация и комментарии взяты из следующих источников:
- SystemClock_Config из STM32CubeH7 firmware package
- Reference manual STM32H743/753 and STM32H750 advanced ARM-based 32-bit MCUs
- — Datasheet STM32H743xI
Итак, начнем.
1. Включение внешнего кварца и ожидание готовности.
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait till HSE is ready
while((RCC->CR & RCC_CR_HSERDY) == 0);
2. Указание источника частоты для PLL1 — внешний кварц.
//RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC -> PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSE;
3. Значение делителя устанавливается равным 4.
//PLLM = 4
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_5; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_4; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_3; //0
RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2; //1
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_1; //0
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_0; //0
4. Множитель N и делители P, Q, R
//PLL1DIVR bits
//DIVN1[8:0] 0 - 8 PLLN = 400
//DIVP1[6:0] 9 - 15 PLLP = 2
//DIVQ1[6:0] 16 - 22 PLLQ = 2
//DIVR1[6:0] 24 - 30 PLLR = 2
RCC -> PLL1DIVR |= 0x0101038F;
5. Дробный делитель частоты PLL (если нужен)
// /* Configure PLL PLL1FRACN */
//__HAL_RCC_PLLFRACN_CONFIG(RCC_OscInitStruct->PLL.PLLFRACN);
RCC -> PLL1FRACR = 0;
6. Указание диапазона входной частоты PLL1
/* Select PLL1 input reference frequency range: VCI */
//__HAL_RCC_PLL_VCIRANGE(RCC_OscInitStruct->PLL.PLLRGE) ;
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_3;
7. Указание диапазона выходной частоты PLL1
/* Select PLL1 output frequency range : VCO */
//__HAL_RCC_PLL_VCORANGE(RCC_OscInitStruct->PLL.PLLVCOSEL) ;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLL1VCOSEL;
8. Включение выходных делителей PLL1: P, Q, R
/* Enable PLL System Clock output. */
// __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVP);
//Bit 16 DIVP1EN: PLL1 DIVP divider output enable
RCC->PLLCFGR |= RCC_PLLCFGR_DIVP1EN;
/* Enable PLL1Q Clock output. */
//__HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ);
RCC->PLLCFGR |= RCC_PLLCFGR_DIVQ1EN;
/* Enable PLL1R Clock output. */
// __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVR);
RCC->PLLCFGR |= RCC_PLLCFGR_DIVR1EN;
9. Включение дробного делителя.
/* Enable PLL1FRACN . */
//__HAL_RCC_PLLFRACN_ENABLE();
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1FRACEN;
10. Пуск PLL1 и ожидание готовности
/* Enable the main PLL. */
//__HAL_RCC_PLL_ENABLE();
RCC->CR |= RCC_CR_PLLON;
while((RCC->CR & RCC_CR_PLL1RDY) == 0);
PLL1 настроен и запущен. Теперь выбор источника частоты SYSCLK и настройка делителей шин.
11. Делитель на 2 HPRE
//RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
// MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
//HPRE[3:0]: D1 domain AHB prescaler
//1000: rcc_hclk3 = sys_d1cpre_ck / 2
RCC -> D1CFGR |= RCC_D1CFGR_HPRE_3; //1
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_2; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_1; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_HPRE_0; //0
12. Без деления D1CPRE
//RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
//MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1CPRE, RCC_ClkInitStruct->SYSCLKDivider);
//D1CPRE[3:0]: D1 domain Core prescaler
//0xxx: sys_ck not divided (default after reset)
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_3; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_2; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_1; //0
RCC -> D1CFGR &= ~RCC_D1CFGR_D1CPRE_0; //0
13. Задаем PLL1 как источник SYSCLK и ожидаем готовности
//RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
//MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_ClkInitStruct->SYSCLKSource);
//SW[2:0]: System clock switch
//011: PLL1 selected as system clock (pll1_p_ck)
RCC->CFGR &= ~RCC_CFGR_SW_2; //0
RCC->CFGR |= RCC_CFGR_SW_1; //1
RCC->CFGR |= RCC_CFGR_SW_0; //1
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1);
14. Делитель на 2 D1PPRE
//D1PCLK1 Configuration
//RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
//MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1PPRE, RCC_ClkInitStruct->APB3CLKDivider);
//Bits 6:4 D1PPRE[2:0]: D1 domain APB3 prescaler
//100: rcc_pclk3 = rcc_hclk3 / 2
RCC -> D1CFGR |= RCC_D1CFGR_D1PPRE_2;
RCC -> D1CFGR &= ~RCC_D1CFGR_D1PPRE_1;
RCC -> D1CFGR &= ~RCC_D1CFGR_D1PPRE_0;
15. Делитель на 2 D2PPRE1
//PCLK1 Configuration
//RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
//MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE1, (RCC_ClkInitStruct->APB1CLKDivider));
//Bits 6:4 D2PPRE1[2:0]: D2 domain APB1 prescaler
//100: rcc_pclk1 = rcc_hclk1 / 2
RCC -> D2CFGR |= RCC_D2CFGR_D2PPRE1_2;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE1_1;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE1_0;
16. Делитель на 2 D2PPRE2
//PCLK2 Configuration
//RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
//MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE2, (RCC_ClkInitStruct->APB2CLKDivider));
//Bits 10:8 D2PPRE2[2:0]: D2 domain APB2 prescaler
//100: rcc_pclk2 = rcc_hclk1 / 2
RCC -> D2CFGR |= RCC_D2CFGR_D2PPRE2_2;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE2_1;
RCC -> D2CFGR &= ~RCC_D2CFGR_D2PPRE2_0;
17. Делитель на 2 D3PPRE
//D3PCLK1 Configuration
//RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
//MODIFY_REG(RCC->D3CFGR, RCC_D3CFGR_D3PPRE, (RCC_ClkInitStruct->APB4CLKDivider) );
//Bits 6:4 D3PPRE[2:0]: D3 domain APB4 prescaler
//100: rcc_pclk4 = rcc_hclk4 / 2
RCC -> D3CFGR |= RCC_D3CFGR_D3PPRE_2;
RCC -> D3CFGR &= ~RCC_D3CFGR_D3PPRE_1;
RCC -> D3CFGR &= ~RCC_D3CFGR_D3PPRE_0;
Для того чтобы убедиться, что конфигурирование и запуск прошли успешно используем выход микроконтроллера MCO2. На этом выходе должна быть частота 26.666 МГц при делителе выхода 15.
Отлично. Частота присутствует, значит все сделано правильно.
Комментарии (47)
j_wayne
25.10.2018 16:12А как у нее с энергопотреблением относительно серии F?
sanders1967 Автор
25.10.2018 16:14Не исследовал. В моих проектах вопросы экономии потребления, как правило, не стоят. :)
j_wayne
25.10.2018 16:19Пошел искать информацию по H7 и наткнулся на вашу же статью, тут же, на хабре (STM32 без HAL и SPL), где вы «ждете H7 с нетерпением». Мои поздравления)
sanders1967 Автор
25.10.2018 16:24Ага. Дождался. Радует увеличение памяти, можно будет поднять разрешение экрана и 16- разрядный АЦП, выйти на 4 десятичные цифры в измериловке. :)
rstepanov
25.10.2018 16:28+1Отказ от использования HAL — не достижение, тот же CubeMX может вам выдать готовый код инициализации и с использованием LL, не суть. Лучше про готовый проект напишите, что он делает, зачем, как удалось много всего засунуть в так мало памяти и т.д. Хотя H7, конечно, не особо стимулирует ее экономить…
sanders1967 Автор
25.10.2018 16:36У меня много проектов :) Проектирование высоковольтных испытательных установок, соответственно: управление инверторами, измерения разнообразные тока, напряжения, сопротивления, затухания и т.п. :) Вывод информации на цветной ЖК монитор.
HAL не использую в своих проектах. Не люблю за излишнюю громоздкость кода. Быстрее, проще и компактней получается без него. :) Я привык работать с официальными источниками: рефреренс, даташит… :)rstepanov
25.10.2018 16:39Ага, ну вот например как вы шум фильтруете чтобы 16-битный АЦП не показывал мусор в младших битах? Мне было бы интересно почитать о методах борьбы, в первую очередь — аппаратно, в схемотехнике.
sanders1967 Автор
25.10.2018 16:46Большая скорость АЦП контроллера позволяет делать большое количество усреднений. Стандартно, я использую усреднение в 1000 раз и младшие 2 разряда отбрасываю, делю на 4. То есть максимальное число будет 1000. При этом скорость преобразования АЦП ставлю минимальной, так он точней работает. Этот простой метод позволяет иметь обновление данных, примерно, 10-20 раз в секунду. Младшая цифра не скачет даже при больших шумах. Если нужна большая точность, использую 24-разрядные АЦП.
По схемотехнике решения стандартные: фильтры, земли. :)rstepanov
25.10.2018 16:55Я как раз по схемотехнике спрашивал :) Вот лежит рядом с компьютером мультик в режиме измерения частоты, показывает 50 герц, никуда не подключен:
Картинкаsanders1967 Автор
25.10.2018 17:028 бит потерять, это конечно много. :( Какой референс? я обычно использую внешний, так как внутренний редко у кого бывает хороший.
Если шум есть при замкнутом входе, тогда нужно смотреть входные цепи, земли.rstepanov
25.10.2018 17:11Референс внешний (F373: VREFSD+) от того же регулятора, от которого и питание контроллера (LM1117-3.3), можно конечно сделать отдельный только для аналоговой цепи, но сначала хочется понять есть ли смысл.
Прикол в том, что наличие руки рядом с платой сразу увеличивает амплитуду шума, а готовое устройство предполагается не в клетке фарадея, а рядом с биологическими объектами и компьютером.sanders1967 Автор
26.10.2018 09:18Очень высокоомный вход? Вход дифференциальный с подавлением синфазной помехи? Если наводка 50 Гц, то может стоит отфильтроваться от нее?
Если считывать биологические потенциалы, то стоит посмотреть на схемы кардиографов. Референсные схемы, вроде, были сайте TI. У Терраэлектроники была рассылка на эту тему.rstepanov
26.10.2018 11:07Вход — от десятков до сотен килоОм, фактически это вариант макетки с подключением всякого разного, от банальных потенциометров до усилителя load cell, не дифференциальный. Частоту 50 Гц отфильтровать не очень понимаю как, у входного сигнала нет фиксированной частоты и слишком загрублять его тоже не хочется.
sanders1967 Автор
26.10.2018 13:23Сотня килоом, нормально, не фантастика. Может коэффициент усиления большой?
rstepanov
26.10.2018 14:22Если потенциометр повесить — то же самое.
hardegor
27.10.2018 10:13от того же регулятора, от которого и питание контроллера (LM1117-3.3)
Таким образом вы измеряете весь шум генерируемый контроллером по питанию — как минимум надо аналоговое питание(VDDA) от цифрового отделять дросселем с емкостью. А на опорное (REF) как минимум ставить отдельный стабилизатор, а для 16 бит и выше — специальный источник опорного.
sami777
26.10.2018 10:16В первую очередь разбирайтесь с разделением общего (аналогового от цифровой). Я в своих опытных изделиях всегда добавляю нулевой резистор между точками соединения цифрового и аналогового общего. Если блымает, то его убираю и проводником соединяя разные участки общего пытаюсь добиться минимального блыманья. При этом стараюсь все довести, чтобы младший знак не прыгал более, чем на 1-2 единицы (разрешение 12бит) без всяких цифровых фильтров. Так или иначе при правильной схемотехнике вполне достижимо, чтобы блымало не более 1-2 единиц младшего разряда для 12 бит без цифрового фильтра.
rstepanov
26.10.2018 11:1010 бит (12-2) — мало, так я мог бы остаться на 12-битном ADC и не пытаться переползти на 16-битный. С тыканием проводочком — хорошоя идея, спасибо!
Dima_Sharihin
27.10.2018 12:15Ну, с открытой измерительной цепью вы будете ловить что угодно, очевидно, что срач на 50Гц самый сильный, поэтому АЦП его и регистрирует.
novarc
26.10.2018 08:48Зря вы так. Не факт что Ваш код лучше чем HAL.
Вот например в 10. «Пуск PLL1 и ожидание готовности» нет выхода из цикла по таймауту.
А в HAL есть обработчики таких ситуаций.sanders1967 Автор
26.10.2018 09:14-3Вы правы. Сделано намеренно, поскольку если будет проблема, то проц не выйдет из цикла и плата не запустится. А запуск устройства с другими параметрами тактирования не нужен.
NordicEnergy
26.10.2018 10:12+3Не фантазируйте. То, что вы не умете писать код и не удосужились написать обработчик ошибки — это банальный быдло-код. HAL конечно мусор, но там все события обработаны. Вы и так можете не запускать устройство, но отправить информацию об ошибки, например, по UART или в отладчик или написать сценарий на случай возникновения ошибки. У RCC есть так же защищенный режим работы, на хабре о нем отдельная статья была.
golf2109
25.10.2018 21:54HAL не использую в своих проектах. Не люблю за излишнюю громоздкость кода. Быстрее, проще и компактней получается без него. :) Я привык работать с официальными источниками: рефреренс, даташит… :)
не совсем понятно, а что CubeMX и HAL это «неофициальніе источники»?sanders1967 Автор
26.10.2018 09:11Конечно официальные. :) По ним есть документация. Но вчера была SPL, сейчас HAL, есть еще LL :). После покупки Atollic, возможно, STM придумает еще более удобную среду разработки и новый уровень абстракции. :) Например, что-нибудь, типа LabView. :)
golf2109
26.10.2018 12:51Кстати, LabView вовсю используется на андронном коллайдере
sanders1967 Автор
26.10.2018 13:16Используется. :) Я имел небольшой опыт написания на LabView. Не представляю, как сопровождать и поддерживать большой код в этой системе.
Dima_Sharihin
26.10.2018 14:36+3Извините, но это
//PLLM = 4 RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_5; //0 RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_4; //0 RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_3; //0 RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2; //1 RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_1; //0 RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_0; //0
просто финиш. Вы смотрели на объявления этих регистров? Все, как один — volatile? Во что компилятор должен будет превратить всю эту простыню быдлокода? На каждую строку Сишного кода у вас должно быть:
- Загрузка значения регистра RCC -> PLLCKSELR
- Присваивание регистру значения битовой маски
- Арифметическая операция AND (и один раз OR)
- Выгрузка значения в регистр RCC -> PLLCKSELR
- Повторить пункты 1-4 для каждой строки сишного кода
В то время, как это значение у вас известно на этапе компиляции и не меняется в рантайме. Достаточно было
- Присвоить регистру compile-time константу с нужными взведенными битами
- Выгрузить значение в RCC -> PLLCKSELR
Да, часто нужно выполнять операции над регистрами в определенной последовательности, но конкретно здесь это точно не нужно. Простота кода? Но зачем вы тогда спускались до уровня регистров?
sanders1967 Автор
26.10.2018 14:42-3Значения делителей и множителей PLL необходимо устанавливать до запуска. После запуска поменять нельзя, нужно будет останавливать. Поэтому определенный порядок должен быть. Также и с выбором источника SysClk.
Dima_Sharihin
26.10.2018 15:06+2Нет, я не про это говорю, я про то, что вы в одном блоке кода 6 раз подряд модифицируете делитель частоты, зачем?
sanders1967 Автор
26.10.2018 15:12-3А в чем проблема? код нечитабелен? :) проблема с производительностью? биты определены в хедере… :)
NordicEnergy
26.10.2018 19:50+21) Читабельность? Ее тут нет
2) Вам вроде на пальцах объяснили, даже цирковые медведи бы поняли. Вы вместо разовой модификации значения регистра сделали это в 6 этапов, то есть производительность конкретного куска кода в 6 раз ниже.
P.S. идите на HAL, не в обиду, но дно должно жить на дне.
AnthonyDS
27.10.2018 14:00Сколько стоит сие чудо? Datasheet?
Dima_Sharihin
27.10.2018 14:09Обычно у хороших фирм вся информация размещается на одной странице и для ее поиска достаточно вбить партнамбер в гугл.
https://www.st.com/en/microcontrollers/stm32h743ii.html
https://www.st.com/en/microcontrollers/stm32f746ig.html
TNN100
класс
sanders1967 Автор
Спасибо :)
TNN100
Всем добра и позитива :)
CKOPOCTb
А чего минусов столько?
NordicEnergy
Регистры это хорошо, но уже миллион первая статья, где опять настраивают RCC и мигают светодиодом. Лучше бы подняли на регистрах там usb что ли или SDRAM какую.
sanders1967 Автор
SDRAM поднят :) цветной дисплей подключен как внешняя память и обновляется по ДМА :) USB тоже можно поднять… если кому-то нужно… :)
NordicEnergy
Так и пишите про SDRAM, а не статью где очевидную, хорошо описанную в RM операцию, пытаются реализовать и в итоге
через жопукоряво. Ваш вариант действительно хуже реализации на HAL. Вообще нужно сильно постараться, чтобы сделать менее стабильно, чем в родном HAL-е.sanders1967 Автор
В Reference Manual детально описана инициализация PLL, а вот переключение SysClk на тактирование от PLL нет так явно. :) Пришлось потратить некоторое время на исследование HAL и формирование нужной последовательности.
sanders1967 Автор
Настройка FMC контроллера — одна строчка :) если интересно могу выложить :)
NordicEnergy
Ох, фантазер… как минимум расшарить в линковщике адресное пространство это 2 строки. А вообще ждем-с статью, где в 1 строчку SDRAM запускается.
В RM есть наскальный рисунок системы тактирования, где для самых «талантливых» около каждого делителя написали конкретный регистр для его настройки, а также около каждого мультиплексора тоже указали регистр. Ужасно не очевидно. А еще, чтобы разработчик совсем ничего не понял у ST десятки страниц в RMе + аппноуты, где расписан каждый чих, включая порядок настройки и включения, на систему тактирования.
sanders1967 Автор
Согласен :) не одна.
void init_FMC(void)
{
RCC->AHB3ENR |= RCC_AHB3ENR_FMCEN;
FMC_Bank1->BTCR[0] = 0x000010D9; //for 168 MHz
FMC_Bank1->BTCR[1] = 0x000300F;
}
Инициализация FMC для F7.