Все, кто пользовался отладкой на уровне регистров под STM32CubeIDE, замечал, что список регистров идёт вразнобой, без какой-либо логики. Например, список для типичного STM32F407xx выглядит таким образом, здесь у нас порты ввода/вывода:
Это у нас UART/USART:
Это таймеры:
Как видим, все устройства идут не по порядку, а вразнобой. И это ещё цветочки. Есть МК, где всё это перемешано ещё хуже, и приходится искать, где и что у нас находится. Кроме этого, если присмотреться, есть периферия, которой на данном кристалле вообще нет. Такой подход очень неудобен и мешает отладке.
С первого взгляда может показаться, что нет никакой логики, но посмотрев даташит, раздел распределения памяти, можно увидеть, что таблица периферии повторяет порядок следования регистров в отладчике. Я задавал вопрос фирме ST, какого ... так сделано. Мне ответили, что у них работы много, а людей мало, вам надо, вы и занимайтесь этим. Ну я и занялся. Для этого нужно всего ничего, найти файл вот по этому пути в каталоге с установленной STM32CubeIDE:
/plugin/com.st.stm32cube.ide.mcu.productdb.debug/resources/cmsis/STMicroelectronics_CMSIS_SVD/STM32F407.svd
Затем я скопировал его в каталог, где у меня находятся все драйвера данного кристалла, назвал STM32F407_main.svd и, первым делом, отсортировал всю периферию. Получил вот такой порядок:
Т. е. я разбил по группам и сделал сортировку в группе, группы получились такие:
- аналоговая периферия
- порты ввода/вывода
- таймеры
- внешние интерфейсы
- остальное
В каждой группе периферия идёт примерно в том порядке, в каком она идёт в STM32CubeMX.
Дальше я скопировал получившийся файл в тот же каталог с именем, схожим с маркировкой кристалла. В моём случае - STM32F407VET.svd - и грохнул там упоминание о периферии, которой нет на этом кристалле. Грохнул не всё, в разделе "Остальное" остались описания регистров, с которыми было лень возиться. Теперь список периферии выглядит так:
Ушла часть портов GPIO и так, по мелочи. А теперь о том, что это за файл и как устроен. Подключается файл здесь:
Выбираем слева "CMSIS-SVD Settings" и кнопкой "Browse" выбираем нужный нам файл. Структура файла такова - первым идёт заголовок, он нам неинтересен, и менять его не стоит. А интересует нас всё, что заключено между тегами <peripherals> ... <peripherals>
.
Здесь и находится описание периферии, регистров и битов регистров:
<peripherals> <peripheral> <!--Ветка описания одного устройства--> <name>RCC</name> <!--Имя устройства--> <description>Reset and clock control</description> <!--Описание, (для чего оно)--> <groupName>RCC</groupName> <!--Группа к которой относится устройство--> <baseAddress>0x40023800</baseAddress> <!--Стартовый адрес в памяти--> <addressBlock> <!--Занимаемое адресное пространство--> <offset>0x0</offset> <!--Смещение от базового адреса--> <size>0x400</size> <!--Размер занимаемого блока--> <usage>registers</usage> <!--Используется регистрами--> </addressBlock> <interrupt> <!--Блок прерывания--> <name>RCC</name> <!--Имя прерывания--> <description>RCC global interrupt</description> <!--Описание--> <value>5</value> <!--Номер вектора прерывания--> </interrupt> <registers> <!--Регистры--> <register> <name>CR</name> <!--Имя регистра--> <displayName>CR</displayName> <!--Имя показываемое в списке--> <description>clock control register</description> <addressOffset>0x0</addressOffset> <!--Смещение от базового адреса--> <size>0x20</size> <!--Количество занимаемых бит--> <resetValue>0x00000083</resetValue> <!--Значение при сбросе--> <fields> <!--Блок полей--> <field> <!--Поле--> <name>PLLI2SRDY</name> <!--Имя поля--> <description>PLLI2S clock ready flag</description> <!--Описание--> <bitOffset>27</bitOffset> <!--Смещение поля относительно начала регистра--> <bitWidth>1</bitWidth> <!--Количество бит в поле--> <access>read-only</access> <!--Способ доступа--> </field> <field> ... </field> </fields> </register> <register> ... </register> <registers> </peripheral> <peripheral> ... </peripheral> </pepherals>
Получается такая картина: файл разделён на блоки ограниченные тегами <peripheral> ... </peripheral>
. В этих тегах описываются конкретные периферийные устройства. Далее идёт заголовок периферийного устройства, где указывается его базовый адрес в адресном пространстве МК, прерывания, если они есть, их именование в системе и номер вектора.
Далее тегами <register> ... </register>
описываются все регистры, которые принадлежат данной периферии. Сколько регистров, столько и блоков. Каждый блок имеет также заголовок, в котором описывается имя регистра, смещение относительно базового адреса самого периферийного устройства, количество активных битов, значение при сбросе.
Теперь идут теги <field> ... </field>
- в них идёт описание конкретных битов. Опять же имя, описание, смещение относительно адреса данного регистра, количество битов занимаемых полем и способ доступа: чтение, запись, чтение/запись и т. д.
Все данные структуры повторяются столько раз, сколько периферийных устройств присутствует в данном микроконтроллере.
Теперь о нюансах... Если несколько периферийных устройств имеют одинаковую структуру, каждое из них не описывается отдельно. Просто описывается одно, а другие описываются со ссылкой на предыдущее.
<peripheral derivedFrom="GPIOC"> <!--GPIOD--> <name>GPIOD</name> <baseAddress>0X40020C00</baseAddress> </peripheral>
Например GPIOC и GPIOD имеют одинаковую структуру, но разный базовый адрес. Описываем GPIOC, а у GPIOD ссылаемся на структуру GPIOC - <peripheral derivedFrom="GPIOC">
, и указываем только новый базовый адрес - <baseAddress>0X40020C00</baseAddress>
. Главное, чтобы периферия, на которую ссылаемся, была описана раньше.
Вот в принципе и всё, выкладываю набор проектов и файлов: STM32Lib. Здесь у нас каталог для предыдущих статей, но немного видоизменённый, добавились SVD-файлы для контроллеров, на которые мне нужно было срочно их сделать. Все каталоги CMSIS из проектов я перенёс сюда для уменьшения размера проектов. Позже я расскажу подробнее, что именно изменилось.