Assert param stm32 что это

от admin

Русские Блоги

Когда мы изучали STM32, вероятность того, что функция assert_param появилась, была очень высока.Поиск в Интернете и в целом объясните механизм утверждения в Интернете, который используется на этапе разработки и отладки программы. Теперь я расскажу о своих взглядах на эти приложения и узнаю, что нужно знать и почему.
4 Функция механизма утверждения assert_param
Когда мы анализируем библиотечные функции, почти каждый прототип функции имеет эту функцию assert_param (); ниже возьмите assert_param (IS_GPIO_ALL_PERIPH (GPIOx)); в качестве примера, чтобы объяснить мое понимание, функция Параметр IS_GPIO_ALL_PERIPH (GPIOx), мы можем найти прототип

#define IS_GPIO_ALL_PERIPH(PERIPH) (((*(uint32_t*)&(PERIPH)) == GPIOA_BASE)|| \
((*(uint32_t*)&(PERIPH)) == GPIOB_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOC_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOD_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOE_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOF_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOG_BASE))
Функция этого определения макроса состоит в том, чтобы проверить параметр PERIPH и определить, является ли параметр PERIPH одним из базовых адресов GPIOX (A . G). Пока один из них истинен, его значение истинно, в противном случае — Ложь, разумеется, это базовая логическая операция в языке C. Конечно, эта библиотечная функция также очень интересна.Посмотрите: сначала получите адрес PERIPH, то есть найдите адрес, & PERIPH, а затем вставьте адрес в 32-битный указатель, то есть добавьте (uint32_t *) впереди, а затем используйте * Доступ к содержимому этого адреса (указателя). Я не буду много говорить, я могу понять это, прочитав несколько раз.
Затем давайте вернемся к функции assert_param. Где эта функция? Прототип, найденный в stm32f10x_conf.h, выглядит следующим образом:
#ifdef USE_FULL_ASSERT

#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t*)__FILE__, __LINE__))
void assert_failed(uint8_t* file, uint32_t line);

#else
#define assert_param(expr) ((void)0)
#endif
Это предварительно скомпилированный файл. Если файл USE_FULL_ASSERT определен, будут выполняться следующие файлы. Обычно в программе нет определения, то есть выполнить следующий оператор ((void) 0) , Этот оператор не должен слишком много думать. Если USE_FULL_ASSERT не определен, ничего не выполняется. Чтобы было ясно, не выполняйте никаких операций с указанным выше оператором IS_GPIO_ALL_PERIPH (GPIOx).
Если USE_FULL_ASSERT определен, когда мы вызываем эту функцию assert_param и проверяем правильность параметра IS_GPIO_ALL_PERIPH (GPIOx), мы можем судить по бинокулярному оператору на языке C, если это Возврат 1, выполнение оператора (void) 0, то же, что и выше, если он возвращает 0, выполнить следующую функцию assert_failed ((uint8_t *) __ FILE __, __ LINE__). Функция функции объясняется в библиотечной функции для указания строки ошибки Номер и файл. Примечание. __FILE__, __LINE__ — это определения макросов в стандартных библиотечных функциях! Помните
void assert_failed (uint8_t * file, uint32_t line); Сначала я не понял, почему он был добавлен сюда. Я тщательно обдумываю объявление функции в файле заголовка. Что касается функциональных сущностей? Его можно найти в main.c из шаблона официального документа. следующим образом:
void assert_failed(u8* file, u32 line)
< /* User can add his own implementation to report the file name and linenumber,
ex: printf(“Wrong parameters value: file %s on line %d\r\n”, file,line) */
/* Infinite loop */
while (1) < >
> Комментарий на английском языке также объясняет, как его использовать. Положение определяется вводом параметров. Самый простой способ — распечатать последовательный порт. Основная идея этой функции — когда есть проблема с входным параметром, но Если вы не можете его скомпилировать, это может помочь вам проверить правильность параметров.
Продолжайте объяснять следующее: Как включается assert_param? Объявления функций, которые мы определили в заголовочном файле stm32f10x_conf.h, по-прежнему являются определениями макросов. Как их можно применить к другим файлам? Многие онлайн-друзья также столкнулись с проблемой сбоя компиляции, когда только начали учиться, и, наконец, решили ее, добавив в файл USE_STDPERIPH_DRIVER:

Мы можем искать USE_STDPERIPH_DRIVER во всем проекте, и мы можем видеть из файла заголовка, что используется стандартный периферийный файл. В файле stm32f10x.h мы можем искать следующие ситуации:

#if !defined USE_STDPERIPH_DRIVER
/**
* @brief Comment the line below if you will not use the peripherals drivers.
In this case, these drivers will not be included and the application code will
be based on direct access to peripherals registers
*/
#define USE_STDPERIPH_DRIVER
#endif

#ifdef USE_STDPERIPH_DRIVER
#include “stm32f10x_conf.h”
#endif
Легко увидеть, что мы не добавляем его туда. Этот файл заголовка также устанавливает для нас переключатель. Пока первый комментарий удален, нет необходимости добавлять USE_STDPERIPH_DRIVER в конфигурацию. Во втором файле мы можем знать, как включить этот файл контрольного переключателя, ха-ха. Мы также понимаем, почему мы можем легко включить все файлы при написании программы, если мы включаем stm32f10x.h, нам нужно только настроить stm32f10x_conf.h для включения необходимых файлов библиотеки.
Из вышесказанного видно, что за счет взаимного включения файлов заголовков мы можем управлять вызовом периферийных устройств и отладочных файлов, чтобы мы могли прояснить наше мышление и лучше его понять. Конечно, в исследовании могут быть некоторые проблемы с языком C. Вы можете быстрее выяснить это, выполнив поиск в Интернете или прочитав больше книг.

В библиотеке прошивки STM32 и предусмотренных процедурах вы можете увидеть использование assert_param () повсюду. Если вы откроете файл stm32f10x_conf.h в любой подпрограмме, вы увидите, что assert_param на самом деле является определением макроса;
В библиотеке микропрограмм его роль заключается в том, чтобы определять, являются ли параметры, переданные функции, допустимыми параметрами.
Так называемые допустимые параметры относятся к параметрам, которые соответствуют указанному диапазону. Например, диапазон значений параметра может быть только положительным целым числом меньше 3. Если данный параметр больше 3,
Затем эта assert_param () может сообщать об ошибках, когда запущенная программа вызывает эту функцию, так что программист может вовремя найти ошибки, не дожидаясь ошибки результата работы программы и множества проблем.

Это распространенный программный метод, который может помочь программистам быстро устранить очевидные ошибки на этапе отладки.

Это приносит в жертву эффективность работы программы (но только на этапе отладки), но помогает повысить эффективность разработки проекта.

Когда ваш проект будет успешно разработан, используйте режим выпуска для компиляции или закомментируйте макроопределение USE_FULL_ASSERT в файле stm32f10x_conf.h, все проверки assert_param () исчезнут, что не повлияет на эффективность окончательной программы.

define assert_param(expr) ((expr) ? (void)0 : assert_failed((u8 *)FILE, LINE))

При выполнении проверки assert_param (), если она обнаруживает ошибку параметра, она вызывает функцию assert_failed (), чтобы сообщить об ошибке программисту. В main.c есть шаблон для этой функции, как показано ниже:

void assert_failed(uint8_t* file, uint32_t line)
<

Вы можете добавить соответствующие операторы для вывода подсказок с информацией об ошибках в соответствии с требованиями среды, которую вы используете, или изменить эту функцию для соответствующей обработки ошибок.

HAL stm32

Итак, HAL позволяет абстрагироваться от работы с регистрами и прочей сложной магии. Грубо говоря, HAL это обёртка над низкоуровневыми операциями. Конечно же это не отменяет необходимости понимания устройства микроконтроллеров, но значительно снижает уровень вхождения.

Например, чтоб запустить таймер, достаточно перед бесконечным циклом прописать вот такую функцию…

То есть нам не нужно знать какие регистры отвечают за это, и что в них записывать. Более того, эта функция будет работать на любых микроконтроллерах серии stm32.

Сама функция выглядит так:

Вначале происходит проверка параметров на ошибки (assert_param), и после этого активируется прерывание и запускается таймер.

Строчки начинающиеся с __двойного подчеркивания , это макросы, с помощью которых можно устанавливать/снимать необходимые биты в регистрах. Как и в случае с самой функцией, макросы будут одинаковы для всей линейки микроконтроллеров.

Однако я немного забежал вперёд. Прежде чем изучать HAL, нужно познакомиться с программой CubeMX (в просторечии «Куб») так как HAL является неотъемлемой частью «Куба», и именно в нём генерится весь начальный код будущего приложения включая описанные выше функции. Подробно про CubeMX читайте здесь.

Познакомились — тогда продолжим…

Воспользуемся примером, который сделан по ссылке выше, и рассмотрим подробно всю программу.

Итак мы сгенерировали проект, в котором есть таймер вызывающий прерывание при переполнении, и GPIO. Открываем этот проект в среде разработки (у меня TrueStudio) и в левой панели клацаем файл main.c…

Куб создал все необходимые функции инициализации…

void SystemClock_Config(void) — инициализация тактирования.
static void MX_TIM1_Init(void) — инициализация таймера.
static void MX_GPIO_Init(void) — инициализация GPIO.

… и избавил нас от возни с настройками, и от возможных ошибок.

Все функции типичны — параметры записываются в структуры, и адреса этих структур передаются в соответствующие HAL-функции. Каждая функция возвращает статус. Если возвращается ошибка, то вызывается функция Error_Handler() находящаяся в самом низу. В эту функцию хорошо бы прописать что-то, что сигнализировало бы об ошибке, например мигнуть лампочкой.

Ниже есть ещё одна функция проверок на ошибки — void assert_failed(uint8_t *file, uint32_t line) . Если макрос assert_param (упомянутый в начале статьи) возвращает ошибку, то в эту функцию прилетает имя файла в котором произошла ошибка, и номер строки.

Функция работает при условии, что задефайнен USE_FULL_ASSERT . Сам по себе этот дефаин находится в файле stm32f1xx_hal_conf.h , но он закомментирован…

В конце файла обрисован механизм передачи assert_param() в void assert_failed()…

Если хотите чтоб он раскомментировался, то надо в Кубе сделать так…


Enable Full Assert. Эти ассерты занимают определённое количество памяти, поэтому их лучше использовать только для отладки, а в релизе отключать.

В общем с проверками на ошибки у HAL’а всё очень удобно и информативно.

Теперь давайте рассмотрим процесс инициализации на примере таймера.

В функции static void MX_TIM1_Init(void) , в объявленную глобально структуру htim1 заносятся различные параметры таймера, после чего эта структура передаётся в функцию HAL_TIM_Base_Init(&htim1) .

Теперь клацните функцию if (HAL_TIM_Base_Init(&htim1) != HAL_OK) левой кнопкой, а потом правой — вылезет контекстное меню, в котором нужно выбрать Open Declaration . Откроется файл stm32f1xx_hal_tim.c …

Здесь происходит следующее:

Проверяется не пустой ли указатель структуры (htim == NULL) и заполнены ли все элементы структуры (assert_param).

Проверяется статус таймера (htim->State == HAL_TIM_STATE_RESET). В данном случае статус HAL_TIM_STATE_RESET говорит о том, что устройство еще не инициализировано или отключено.


Заголовочный файл stm32f1xx_hal_tim.h .

Если статус удовлетворяет, то снимается блокировка (htim->Lock = HAL_UNLOCKED) и вызывается функция HAL_TIM_Base_MspInit(htim) …

Здесь проверяется какой именно таймер настраивается (htim_base->Instance==TIM1) и вызываются функции которые включают тактирование таймера, активирует прерывание и настраивают приоритет.

Далее устанавливается статус «занято» (htim->State= HAL_TIM_STATE_BUSY) — если по каким-то причинам, параллельно будет вызвана ещё одна функция инициализации таймера, то она не сможет ничего испортить.

После этого вызывается функция TIM_Base_SetConfig(htim->Instance, &htim->Init) (у этой функции нет приставки HAL, поэтому можно назвать её низкоуровневой) работающая напрямую с регистрами…


Файл stm32f1xx_hal_tim.c

Ну и наконец устанавливается статус «готов к труду и обороне» (htim->State= HAL_TIM_STATE_READY) и возвращается — return HAL_OK;

Функции связанные с таймером находятся либо в том же файле (stm32f1xx_hal_tim.c), либо в stm32f1xx_hal_tim_ex.c .

Все функции имеют характерные названия определяющие их назначение…

Окончание _IT означает, что устройство будет вызывать прерывание. Это относится к любым функциям используемым в HAL.

Например запуск таймера без прерываний выглядит так:

Как вы уже наверно поняли, одна из особенностей библиотеки HAL это большое количество проверок защищающих разработчиков от массы неприятностей. Кто-то расценит это как добродетель, а кто-то решит что такое количество проверок избыточно. Однако не стоит занимать какую-либо позицию на этот счёт так как в процессе изучения вы поймёте где нужно оставлять эти проверки, а где их можно удалить. То же самое касается и всего остального, со временем вы будете отказываться от каких-то функций HAL и заменять их работой с регистрами.

При работе с любой другой периферией, все необходимые функции вы найдёте в соответствующих файлах…

Названия файлов говорят сами за себя.

Функция запуска таймера…

… сама по себе не особо интересна.

Функция устанавливает бит разрешающий прерывания по переполнению — __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE) и бит активации таймера — __HAL_TIM_ENABLE(htim).

А вот механизм вызова прерывания поможет понять устройство библиотеки HAL. Разберём его…

Когда мы в Кубе активируем прерывание от какой-либо периферии, то в файле stm32f1xx_it.c автоматически создаётся обработчик с соответствующим именем…


Сюда программа переходит как только сработает прерывание от любого из событий таймера №1.

Этот обработчик (условно назовём его низкоуровневым) вызывает HAL-обработчик HAL_TIM_IRQHandler(&htim1) находящийся в файле stm32f1xx_hal_tim.c . HAL-обработчик состоит из нескольких блоков, каждый из которых отвечает за определённое событие — захват/сравнение, переполнение, триггерный сигнал и т.д…

Программа войдя в функцию HAL_TIM_IRQHandler проверяет какой из флагов был установлен и найдя нужный блок выполняет его содержимое.

Нас интересует блок TIM Update event…

Библиотека HAL под завязку напичкана различными макросами. Как уже говорилось в начале статьи, они начинаются с __двойного подчёркивания и имеют характерные имена определяющие их назначение. Эти макросы очёнь клёвая штука, они позволяют оперировать различными битами в различных регистрах без необходимости копаться в даташитах.

_GET_ — читать биты, _SET_ — устанавливать биты, _CLEAR_ — очищать биты, и т.д. Посмотреть макросы можно в хедерах соответствующей периферии, например, всё что связано с таймерами находится в файле stm32f1xx_hal_tim.h .

Внутри макроса __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE) содержится вот такая конструкция…

Этот макрос сбрасывает бит (указанный вторым аргументом) в регистре состояния (Status Register).

В первый аргумент подставляется указатель на структуру таймера, а вторым аргументом идёт дефаин флага который взводится при возникновении прерывания…

Написав программу на HAL вы можете проследить где-какие макросы/функции вызываются, и работать с регистрами напрямую. То есть HAL можно с лёгкостью использовать как пособие для изучения низкоуровневых операций.

Если установлен флаг переполнения (TIM_FLAG_UPDATE) и источником является прерывание по переполнению (TIM_IT_UPDATE), тогда флаг сбрасывается и вызывается колбек — HAL_TIM_PeriodElapsedCallback(htim) .

Колбек это характерная фишка HAL’а. В колбеках выполняются действия которые нужно сделать при возникновении события/прерывания, в нашем случае мы будем мигать лампочкой.

В принципе нам ничто не мешает мигать лампочкой прямо в обработчике, да ещё и оперировать регистрами напрямую (немного хардкора)


В этом примере делается то же самое, что делает HAL — сбрасывается флаг прерывания и вместо вызова колбека сразу же выполняется действие (мигание светиком).

Таким образом количество кода сократилось бы в разы, но не было бы никаких проверок, и в любом случае нужно было бы самостоятельно писать проверку — какое именно событие случилось (если бы их было несколько). Тем не менее, в дальнейшем, когда хорошенько прокачаетесь, будете потихонечку переходить на работу с регистрами.

Если вы внимательно посмотрите, то увидите что у каждого события есть свой колбек. Например у захвата/сравнения их несколько…

Читать:
Почему велосипеды продают без крыльев


HAL_TIM_IC_CaptureCallback, HAL_TIM_OC_DelayElapsedCallback и HAL_TIM_PWM_PulseFinishedCallback.

Все эти колбеки прописаны в том же файле, с атрибутом __weak .

Находим нужный нам колбек…

… и переопределяем его в файл main.c

Проверяем что прерывание пришло от таймера №1 и мигаем светиком.

Проверять от какого таймера пришло прерывание нужно в том случае, если используется несколько таймеров. Тут дело вот в чём: если мы настроим ещё один таймер, например №2, и он тоже будет вызывать прерывания, тогда в файле stm32f1xx_it.c появится второй обработчик…

Не смотря на то, что обработчиков два, функция HAL_TIM_IRQHandler() одна и та же. Соответственно и колбек будет вызываться один и тот же. Поэтому для двух таймеров нужно делать так…

Это касается не только таймеров, но и прочей периферии — USART, SPI, I2C и т.д.

Программирование всего остального выглядит примерно так же как и таймера. Открываем соответствующий файл, например stm32f1xx_hal_uart.c , если работаем с USART’ом, находим там нужные функции, а в файле stm32f1xx_hal_uart.h макросы. Читаем комментарии (все функции и макросы прокомментированы) и пишем код…

Рассмотрим работу USART’а с DMA, там механизм несколько сложнее чем с таймером. В Кубе настройте USART с использованием DMA на приём…

Инициализация USART’а точно такая же как и у таймера…


Параметры загружаются в структуру и передаются в функцию.

Команда запуска опять же схожа с таймером (передаётся структура + доп. аргументы)

Первый аргумент это адрес структуры, второй — это адрес массива в который будут складываться полученные данные, третий — это количество байт которые нужно принять.

Здесь у нас много чего интересненького.

В первую очередь происходит проверка — занят USART или нет (HAL_UART_STATE_READY).

Если до этого функция уже запускалась и данные ещё не получены, то эта проверка не пройдёт и функция вернёт статус «занято» (return HAL_BUSY). Если же необходимо перезапустить функцию, то предварительно надо вызвать — HAL_UART_AbortReceive(&huart1). Как видите названия функций говорят сами за себя.

Далее проверяется не пустой ли указатель на приёмный буфер, и чтоб размер данных был не нулевой. Устанавливается блокировка (__HAL_LOCK) и начинается заполнение структуры huart . Первые четыре пункта вопросов не вызывают, а дальше в соответствующие элементы структуры записываются указатели на функции (в языке СИ имя функции без скобок является указателем на эту функцию) содержащие колбеки…

Здесь помимо проверки и нового вида макроса (CLEAR_BIT) мы наконец-то видим колбек — HAL_UART_RxCpltCallback(huart) , который и нужно прописывать в main.c . Этот колбек вызывается когда буфер будет заполнен полностью.

Прерывание может вызываться при заполнении половины буфера. За это отвечает huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt .

Для ошибки тоже есть функция с колбеком — huart->hdmarx->XferErrorCallback = UART_DMAError .

Следом идёт запуск DMA — HAL_DMA_Start_IT() …

В функцию передаётся: указатель на структуру, источник данных (в нашем случае это регистр данных (DR) USART’а), получатель данных (адрес буфера), и ожидаемое кол-во байт.

Потом всё это хозяйство передаётся в функцию конфигурирования — DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength) , после чего происходит это…

Если элемент структуры hdma->XferHalfCpltCallback не пустой, то разрешаются прерывания по заполнению буфера полностью (DMA_IT_TC), по заполнению буфера наполовину (DMA_IT_HT), и при ошибке (DMA_IT_TE). Если нам не нужно отслеживать заполнение половины буфера, то надо в huart->hdmarx->XferHalfCpltCallback записать NULL.

Далее сбрасывается флаг ошибки переполнения (__HAL_UART_CLEAR_OREFLAG), снимается блокировка (__HAL_UNLOCK), с помощью макроса SET_BIT устанавливаются различные биты и возвращается статус — return HAL_OK .

На этом функция HAL_UART_Receive_DMA(&huart1, (uint8_t*)rx_buff, BUFSIZE) закончена.

Низкоуровневый обработчик прерываний от DMA выглядит так же как и в случае с таймером…

… вызывает HAL-обработчик HAL_DMA_IRQHandler(&hdma_usart1_rx);

И опять же как и у таймера, функция состоит из нескольких блоков. Первый блок срабатывает при заполнении половинки буфера, второй — целиком, а третий при ошибке. Для примера рассмотрим блок полного буфера…

Проверяются флаги полного буфера (DMA_FLAG_TC1) и разрешённого прерывания (DMA_IT_TC).

Если отключён циклический режим DMA — hdma->Instance->CCR & DMA_CCR_CIRC) == 0U , тогда отключаются прерывания — __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC) . При работе DMA в циклическом режиме, отключать прерывания конечно же не нужно.

Заметьте, разработчики HAL снабдили всё функции, макросы и флаги связанные с прерываниями буквами IT .

Далее устанавливается статус готовности к очередному приёму — hdma->State = HAL_DMA_STATE_READY , сбрасывается флаг окончания приёма через DMA — __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma)) , и снимается блокировка…

Блокировка организована очень просто…

Если сделать __HAL_LOCK(huart) , то при обращении к структуре huart будет возвращаться статус «занято» — return HAL_BUSY;

Последнее условие связано с тем, что было сделано в функции запуска. Если мы там сделали так — huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt , то в элементе структуры будет лежать указатель на функцию UART_DMAReceiveCplt() . Соответственно условие сработает и будет вызвана функция UART_DMAReceiveCplt() , которая в свою очередь вызовет колбек.

Такая вот хитроумная конструкция

Если приём ведётся без DMA…

Тогда после включения глобального прерывания USART’а появится его обработчик…

Перейдём к функции HAL_UART_IRQHandler(&huart1) . Полностью её рассматривать не будем, разбёрём только часть отвечающую за приём. Отправка схожа с приёмом.

Тут появился ещё один макрос — READ_REG , с помощью которого читаются регистры и проверяется нет ли ошибок — (errorflags == RESET).

Далее проверятся что произошло: USART_SR_RXNE — в USART пришёл байт, USART_CR1_RXNEIE — было сгенерировано прерывание. Если всё так, то вызывается функция UART_Receive_IT(huart) . Эта функция вызывается каждый раз при приёме очередного байта.

В зависимости от длины принимаемого слова (8 или 9 бит) выбирается первая или вторая конструкция, и данные из регистра DR (Data Register) записываются в приёмный буфер — pRxBuffPtr .
Если длина слова 9 бит, то для его сохранения используется два байта — huart->pRxBuffPtr += 2U;

Следом проверяется счётчик принятых байт — RxXferCount (он считает «вниз» от максимального значения буфера), и если он равен нулю (то есть приняты все запрошенные данные), то вызывается колбек — HAL_UART_RxCpltCallback(huart);

Вы наверно обратили внимание, что при принятии одного байта происходит очень много операций (как раз за такую избыточность некоторые пользователи и ругают HAL, хотя если подумать, то там только проверки и ничего лишнего) , поэтому при большом количестве данных и/или интенсивном обмене лучше использовать DMA, там это всё происходит на аппаратном уровне.

В завершение хочется рассказать про копирование через DMA. Для этого режима у DMA есть механизм создания колбеков.

Настроим Куб для копирование массива из одной области памяти в другую при помощи DMA…


Длина слова указана Word (32 бита), то есть копироваться будет по четыре байта за один такт.

Функция регистрации колбека…

В функцию передаются три аргумента:

1. Указатель на структуру.
2. Ключ, по которому определяется какое событие должно вызвать колбек — скопирован весь буфер, скопирована половина буфера и т.д.


В нашем случае указан полный буфер — HAL_DMA_XFER_CPLT_CB_ID.

3. Название колбека. Придумайте сами.

Таким образом мы зарегистрировали колбек — DMA_m2m_Callback() , который будет вызываться после полного копирования.

Функция запуска копирования…

Аргументы: указатель на структуру, массив из которого копируется, массив в который копируется, количество байт (ячейки массива 8-ми битные, а DMA будет копировать по 32 бита за раз).

Содержимое этой функции поизучайте самостоятельно, вы уже всё знаете

По окончанию копирования произойдёт прерывание и будет вызван обработчик…

В функции HAL_DMA_IRQHandler() прописан такой же механизм как и в случае с USART’ом — несколько блоков отвечающих за каждое событие (полный буфер, половинка и т.д.) и вот это…

Элемент структуры hdma->XferCpltCallback был заполнен во время регистрации колбека.

На этом наверно всё.

Всем спасибо

Быстрый старт c микроконтроллерами STM32F10x. Использование библиотеки STM32 Standard Peripheral Library

В этой публикации я попытаюсь акцентировать внимание на основных моментах для быстрого начала работы с микроконтроллерами STM32F10x на основе библиотеки стандартной периферии от компании-производителя STMicroelectronics.

В качестве среды разработки в статье будет использоваться Eclipse CDT. Поскольку основное внимание будет сосредоточено вокруг программного кода, то вы можете спокойно проделать все манипуляции в Code::Blocks.

Общая структура проекта для ARM микроконтроллеров описана в моей статье «Программирование AVR и ARM микроконтроллеров в Eclipse. Часть 2».

Здесь я коротко напомню, что для сборки проекта для ARM — микроконтроллеров (STM32F10x в частности) понадобится скрипт компоновщика и C-Startup файл.

Скрипт компоновщика представляет собой файл с инструкциями по размещению кода программы и данных в памяти микроконтроллера. Он может скомандовать загрузить код вашей программы в Flash -память программ или SRAM -память данных.

Для микроконтроллеров с различным объемом памяти программ и данных необходимы разные скрипты компоновки. Их можно достать у производителя микроконтроллеров — компании STMicroelectronics.
Распакуйте из архива ARM_Toolchain/Lib/stm32f10x_stdperiph_lib.zip библиотеку STM32F10x standard peripheral library.
В ней имеются примеры проектов для различных сред разработки ( IAR EWB, Keil uVision, Atollic True Studio и т.д). Наиболее близким для нас является Atollic True Studio, поскольку представляет собой модификацию Eclipse.
Зайдите в каталог Project/StdPeriph_Template/TrueSTUDIO, там есть несколько подкаталогов, названия которых соответствуют названиям отладочных плат STM3210x-EVAL.

Узнайте, в какой из этих плат используется микроконтроллер той же линейки, что и ваш. Скопируйте файл stm32_flash.ld из соответствующего каталога в свой проект.

Возможно также создать универсальный скрипт, в котором будут изменяться только объем памяти программ и данных в соответствии с используемым микроконтроллером.

Стартовый код (C-Startup) для микроконтроллеров STM32 может быть написан на С или Assembler.
Хотя библиотеку STM32F10x Standard Peripheral Library (далее по тексту используется сокращение STM32F10x SPL) часто критикуют за наличие ошибок, все же для начала программирования под STM32 использование этой библиотеки предоставляет самый простой способ быстро приступить к работе.
Но всегда хочется, чтобы была какая-то альтернатива. На самом деле их множество, например, программировать на языке ассемблера �� .

Это самый тяжелый и бессмысленный путь. Второй способ — использовать библиотеку CMSIS, которая предоставляет синтаксис обращения к структурам языка С для доступа к различной периферии микроконтроллера. Самым простым и логичным способом (на мой взгляд) является использование библиотек.

Если вы категорически настроены против STM32F10x SPL, то специально для вас имеется еще одна альтернатива — библиотека libopencm3. В ней основное количество примеров сосредоточено вокруг основной серии микроконтроллеров STM32F10x , но появление примеров для других серий ( STM32F2xx/4xx) является только вопросом времени. Вы всегда можете присоединиться к проекту libopencm3 и ускорить этот процесс.

Стандарт CMSIS также является не обязательным для применения в ваших программах.
Можно обойтись и без него, потратив некоторые усилия и время для реализации HAL ( Hardware Abstraction Layer ) уровня на языке программирования С.

Такой способ может оказаться в некоторых случаях единственным доступным способом. Например, ваша организация использует заказные микросхемы на основе разработанных компанией ARM вычислительных ядер и специфической для отрасли периферией.

Или вам необходимо реализовать программное обеспечение на языке С для микроконтроллеров с ядром ARM9, для которого производители ориентируются на использование готовых операционных систем (Linux, QNX, Windows CE), поэтому библиотек для программирования на языке С в чистом виде или в сочетании с более легковесной RTOS производители могут не предоставлять.

К счастью производители микроконтроллеров на основе ядра Cortex-M3 предоставляют в распоряжение разработчиков большое количество библиотек кода. Это касается и микроконтроллеров STM32.
Продолжим рассмотрение библиотеки STM32F10x SPL. Рассматривать ее будем на примере stm32f10xQuickstart.
Вы можете открыть этот пример или же создать свой проект «с нуля», чтобы лучше осознать весь процесс происходящего.

Для второго случая я перечислю список необходимых шагов :

  • Создать в Eclipse новый пустой проект
  • Скопировать в проект скрипт компоновки и стартовый файл
  • Создать новый или скопировать шаблонный Makefile
  • При использовании в качестве шаблона Makefile из моего примера необходимо создать внутри проекта каталоги src, inc, bin, obj , внутри каталогов bin и obj создать подкаталоги Debug и Release.
  • Скопировать необходимые исходные и заголовочные файлы из библиотек CMSIS и STM32F10x SPL.
  • Внести необходимые изменения в секции настроек пользователя шаблонного Makefile, если он используется.
  • Создать в окне Eclipse “make target ” новые цели “Debug”, “cleanDebug”, “Release”, “cleanRelease”, “Program”.
  • Запустить на выполнение цель «Debug» и проследить за ее выполнением в окне «Console».

Для лучшего понимания материала я разбил статью на несколько независимых параграфов, в каждом из которых описывается только какой-то один аспект работы с библиотекой STM32F10x SPL.

Конфигурирование STM32F10x SPL с помощью макроопределений

Для конфигурирования библиотеки используются предопределенные значения макросов, которые мы сейчас и рассмотрим.
Их можно задать внутри заголовочных файлов с помощью директивы препроцессора #define или же передать значения макроопределений через ключ -D компилятора GCC.
В своем примере я использую второй способ.
В Makefile переменная DEFINE содержит макросы, необходимые для компиляции библиотеки STM32F10x SPL.
Макроопределение STM32F10X_MD задает принадлежность используемого микроконтроллера к линейке Medium-density.
Сюда входят микроконтроллеры с объемом Flash-памяти от 64 до 128кБ .
В следующей таблице перечислены названия макросов для разных серий микроконтроллеров :

Наименование серии Макрос Описание
Low density Value line STM32F10X_LD_VL микроконтроллеры серии STM32F100xx с объемом Flash-памяти 16 — 32кБ
Low density STM32F10X_LD микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx
с объемом Flash-памяти 16 — 32кБ
Medium density Value line STM32F10X_MD_VL микроконтроллеры серии STM32F100xx с объемом Flash — памяти
64 — 128кБ
Medium-density STM32F10X_MD микроконтроллеры серии STM32F101xx, STM32F102xx, STM32F103xx с объемом Flash- памяти 64 — 128кБ
High density Value line STM32F10X_HD_VL микроконтроллеры серии STM32F100xx с объемом
Flash — памяти 256 — 512кБ
High density STM32F10X_HD микроконтроллеры серии STM32F101xx, STM32F103xx с объемом
Flash- памяти 256 — 512кБ
XL-density STM32F10X_XL микроконтроллеры серии STM32F101xx, STM32F103xx с объемом
Flash- памяти 512 — 1024кБ
Connectivity line STM32F10X_CL микроконтроллеры серии STM32F105xx и STM32F107xx

Для задания тактовой частоты микроконтроллера необходимо раскомментировать в файле system_stm32f10x.c макрос с необходимым значение тактовой частоты.

Assert param stm32 что это

Авг 5, 2014 | 0 comments

assert_param stm32

При отладке программы очень удобно для вывода сообщений об той или иной ситуации, иметь универсальную функцию, которая позволяла бы выводить сообщение об ошибке, номер строки и файл в котором эта ошибка возникла. Для этого, в библиотеке периферии от ST, используется assert_param.

Перед тем как использовать данную функцию необходимо указать, что мы ее используем:

И создать функцию обработчик:

На самом деле assert_param представляет собой:

Или в более простой записи:

expr — это выражение, которое передается в assert_param(expr)assert_param(x > 10).

Похожие публикации