В уроке научимся создавать пользовательские библиотеки на примере класса Debounce. Также я расскажу, как применять в проекте готовые библиотеки.
Предыдущий урок Список уроков Следующий урок
В предыдущем уроке мы разработали класс Debounce для обработки дискретных сигналов. С помощью него можно устранять дребезг контактов, фильтровать сигнал, ослаблять влияние электромагнитных помех и т.п.
Класс отлажен, проверен, закончен. Но использовать его в других программах достаточно неудобно. Главная проблема – необходимо копировать исходный код класса в новую программу. При этом можно ошибиться и долго отлаживать давно проверенный и забытый код. Ну и, конечно, ненужный код будет загромождать текст программы, ухудшать читаемость.
Выход очевиден – оформить класс библиотекой. Давайте этим и займемся.
Последовательность действий для создания библиотеки STM32.
Библиотека, по крайней мере, на нашем уровне программирования, это тот же класс, только размещенный в других файлах. В текст программы файлы библиотеки включаются директивой препроцессора #include. Таким образом, текст библиотеки в нашей программе мы не видим. А на этапе компиляции он “подсовывается” компилятору директивой #include . И благополучно транслируется вместе с остальным исходным кодом.
Я скопировал проект предыдущего урока Lesson13_1 в новую папку Lesson14.
Переименовал его в Lesson14_1.
Открыл в Atollic TrueStudio и переименовал его в IDE еще раз. Все это мы уже делали.
Получился проект с классом Debounce из предыдущего урока. Этот класс мы будем оформлять библиотекой.
Библиотека состоит как минимум из двух файлов:
- заголовочного файла, с расширением .h ;
- файла исходного текста, расширение .cpp.
Заголовочный файл должен содержать описание класса, объявление переменных, константы. Программный код в нем не размещают.
Второй файл (.cpp) содержит код методов класса.
Давайте создадим файлы библиотеки. Чтобы в структуре проекта не было путаницы, создадим для всех пользовательских библиотек папку Libraries. А в ней папку Debounce, уже для нашей библиотеки.
Правой кнопкой мыши нажимаем на имя проекта, New -> Folder.
Задаем имя папки Libraries.
Теперь нажимаем на Libraries, New -> Folder.
Создаем папку Debounce.
Теперь выбираем папку Debounce, New -> File.
Создаем в ней файлы Debounce.h и Debounce.cpp.
Заголовочный файл Debounce.h.
Неплохо в начале файла поместить краткую информацию о библиотеке. У кого, на что хватит фантазии. Естественно, надо оформить этот блок комментариями.
/*
* Debounce.h - библиотека обработки дискретных сигналов STM32.
*
* Может быть использована для устранения дребезга контактов, цифровой фильтрации сигналов от помех.
*
* Работает в фоновом режиме.
*
* В параллельном процессе регулярно должен вызываться один из методов обработки:
*
* void scanStability(void); // метод ожидания стабильного состояния сигнала
* void scanAverage(void); // метод фильтрации сигнала по среднему значению
*
* В результате формируются признаки состояния сигнала:
*
* uint8_t flagLow; // признак СИГНАЛ В НИЗКОМ УРОВНЕ
* uint8_t flagRising; // признак БЫЛ ПОЛОЖИТЕЛЬНЫЙ ФРОНТ
* uint8_t flagFalling; // признак БЫЛ ОТРИЦАТЕЛЬНЫЙ ФРОНТ
*
* Признаки могут быть прочитаны функциями:
*
* uint8_t readFlagLow(void); // чтение признака СИГНАЛ В НИЗКОМ УРОВНЕ
* uint8_t readFlagRising(void); // чтение признака БЫЛ ПОЛОЖИТЕЛЬНЫЙ ФРОНТ
* uint8_t readFlagFalling(void); // чтение признака БЫЛ ОТРИЦАТЕЛЬНЫЙ ФРОНТ
*
* Пример создания объекта:
* Debounce button(GPIOC, 1 << 11, 10); // экземпляр класса Debounce
*
* Подробно описана http://mypractic.ru/uroki-stm32 в уроках 12, 13, 14
*
* Разработана Калининым Эдуардом.
*
* http://mypractic.ru
*/
Дальше следует объявление класса, которое надо заключить в конструкцию.
/* Проверка, что библиотека не подключена */
#ifndef DEBOUNCE_H
#define DEBOUNCE_H
. . . . . . . . . . . . . . . . . . .
#endif/* DEBOUNCE_H */
Это предотвратит повторное подключение библиотеки.
И еще необходимо подключить файл stm32f103xb.h. В нем содержатся CMSIS-имена регистров.
/* Проверка, что библиотека не подключена */
#ifndef DEBOUNCE_H
#define DEBOUNCE_H
#include "stm32f103xb.h"
class Debounce {
. . . . . . . . .
};
#endif /* DEBOUNCE_H */
В файл Debounce.cpp мы копируем код методов. И в самом начале подключаем заголовочный файл. В нем содержится определение класса.
#include "Debounce.h"
//----------------- методы класса Debounce
. . . . . . . . . . . . . . .
Все библиотека создана. Теперь необходимо подключить ее.
Прежде всего, удаляем из main.cpp все то, что мы переместили в библиотечные файлы. А именно: объявление класса и код методов.
Подключаем нашу библиотеку.
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../Libraries/Debounce/Debounce.h"
Мы указали полный путь к папке библиотеки.
Можно написать короче:
#include "Debounce.h"
Но тогда путь к папке необходимо сообщить компилятору:
Project -> Build Settings -> Tool Settings -> C++ Compiler -> Directories -> Add (зеленый плюсик сверху).
В списке появится путь к нашей библиотеке
../Inc
../Libraries/Debounce
../Drivers/STM32F1xx_HAL_Driver/Inc
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
../Drivers/CMSIS/Device/ST/STM32F1xx/Include
../Drivers/CMSIS/Include
Еще вариант той же операции:
Правой кнопкой мыши нажимаем на папку Debounce в проекте, Add/Remove Include Path -> OK
В списке путей для include появится
"${workspace_loc:/${ProjName}/Libraries/Debounce}"
Транслируем проект. Получаем сообщения об ошибках компиляции.
Компилятор не нашел коды для методов класса. Мы не сообщили ему, где находится файл Debounce.cpp.
Можно поместить этот файл в папку Src. Компилятор привык там искать исходные тексты. Но мы не будем ломать стройности проекта.
Сделаем так: Project -> Build Settings -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder -> Libraries -> OK
Другой способ, немного проще:
Правой кнопкой по Libraries, Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder -> Apply
Свершилось. Теперь компилируется без ошибок.
Вот ссылка на полный проект:
Применение библиотеки.
Давайте в этом разделе научимся использовать готовые библиотеки. Повторим часть действий из первой половины урока.
Нам надо реализовать задачу – на каждое нажатие кнопки светодиод меняет свое состояние.
Мы собираемся воспользоваться готовой библиотекой Debounce.
Загружаем ее архив.
В нем 2 файла в папке Debounce.
С помощью STM32CubeMX создаем проект Lesson14_2.
- Устанавливаем конфигурацию системы тактирования.
- Вывод PB13 конфигурируем на активный выход.
- Вывод PB12 (кнопка) не трогаем.
Конвертируем проект в C++.
Создаем папку Libraries и копируем в нее папку Debounce с файлами библиотеки.
Задаем в IDE путь к библиотеке. Правой кнопкой по Libraries, Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder -> Apply
Открываем main.cpp.
Подключаем библиотеку Debounce.
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../Libraries/Debounce/Debounce.h"
Создаем объект button.
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
Debounce button(GPIOB, 1 << 12, 10); // экземпляр класса Debounce
Лучше запустить компиляцию, убедиться, что ошибок нет.
Теперь мы можем использовать объект button.
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(button.readFlagFalling() != 0)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
button.scanAverage();
HAL_Delay(1);
}
Все. Компилируем, загружаем в плату, проверяем.
Вот ссылка на проект:
Папку Libraries со своим библиотеками удобно копировать в проект целиком, а не выбирать нужные библиотеки. Не подключенные директивой #include файлы компилироваться не будут.
В следующем уроке будем работать с прерываниями по таймеру. Поговорим о параллельных процессах.
у меня вопрос а почему не создать h.cфайлы и не поместить в папках inc Src? можно ли так?
Здравствуйте!
В принципе можно. Беспорядок в программе будет.
Здравствуйте. А можно ли вынести в отдельное место папку libraries и просто указывать путь к ней в настройках проекта, а не копировать каждый раз? Например, библиотека в C:\libraries, а проекты в ..user\…\workspace. и в настройках прописывать путь C:\libraries. Если да,то какие плюсы и минусы
Здравствуйте!
При желании вы можете указать полный путь к библиотечным файлам, использую директиву include с двойными кавычками.
Invalid project path: Include path not found (LCD_I2C BOBR_STM32\#undef __ARM_FEATURE_SIMD32). да и ещё.Мне пишет ошибку.add folder-как добавить папку её не видно?
в моей папке два h файла и два С файла
Здравствуйте. А если файлы Debounce.h и Debounce.cpp располагаются в разных директориях, то в «Project -> Build Settings -> Tool Settings -> C++ Compiler -> Directories -> Add» нужно добавлять обе директории для файлов?
И можно ли как то сделать что бы необходимые мне файлы подключались из указанной мной папки рекурсивно? т.е. к примеру если у нас много #include, и все они находятся в разных папках (при условии что все имена подключаемых файлов разные) сделать рекурсивный поиск и подключить все что нашло?
При добавлении новых путей в «Project -> Build Settings -> Tool Settings -> C++ Compiler -> Directories -> Add» где в файлах проекта хранится эта информация?
Здравствуйте!
Мне кажется, надо искать стандартные средства. Или использовать свой протокол.
пробую добавить файлы библиотек с этого сайта .h .c в свой проект (stm32cubeide 1.3.0)
указанные действия на мою логику не ложатся((
все получается с ошибками, до этого работал только с АВР на си, сохдавал и добавлял файлы проектов, всегда было гладко и понятно, с ИДЕ для stm32 — черный ящик((
по привычке создал в папке— core/inc/myDelay.h
core/src/myDelay.c
и скопировал в файлы содержимое файлоф DelayDWT, добавил в main.c — myDelay.h
После компиляции == ../Core/Src/main.c:31:10: fatal error: myDelay.h: No such file or directory
#include «myDelay.h»
На всякий случай напишу если вдруг кто сталкивается с ошибкой из разряда unknown type name ‘class’.
Подключать новосозданную библиотеку на с++ нужно только из файла main.cpp а не из main.h. Как пишут сами ST в этом файле может быть только Си код.
Что бы добавить путь до своей библиотеки зайдите в Project/Properties -> C/C++ General -> Path and symbols и нажмите кнопку Add там уже сами разберетесь как хотите добавить путь/пути.
PS заметил особенность если через функцию класса создать объект класса и инициализировать в нем сразу 2 пина то потом нужно нажать одновременно обе кнопки))
Выдаёт такой варнинг:
Invalid project path: Include path not found (Lesson14-2\#undef __ARM_FEATURE_DSP).
Где это убрать пока не нашёл. В дереве проекта папка Includes там есть эта строка, горит серым. Вроде всё компилит. Но с чем варнинг связан — непонятно. В скаченом проекте этого нет. Версия куба аналогичная. А вот ТS новее, но думается это не с ней связано. И да старый Куб что-то сам выгружаться не хочет, тоже не понятно.
Добрый день. Все сделал как в уроке Lesson14_2, однако компилятор выдает сообщение:
17:51:06 **** Incremental Build of configuration Debug for project Lesson14_2 ****
make -j4 all
arm-none-eabi-g++ «../Core/Src/main.cpp» -mcpu=cortex-m3 -std=gnu++14 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -I../Libraries/Debounce/ -O0 -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-use-cxa-atexit -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF»Core/Src/main.d» -MT»Core/Src/main.o» —specs=nano.specs -mfloat-abi=soft -mthumb -o «Core/Src/main.o»
arm-none-eabi-g++ -o «Lesson14_2.elf» @»objects.list» -mcpu=cortex-m3 -T»C:\Users\Admin\STM32CubeIDE\workspace_1.10.1\Lesson14_2\STM32F103C8TX_FLASH.ld» -Wl,-Map=»Lesson14_2.map» -Wl,—gc-sections -static —specs=nano.specs -mfloat-abi=soft -mthumb -Wl,—start-group -lc -lm -lstdc++ -lsupc++ -Wl,—end-group
C:/ST/STM32CubeIDE_1.10.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.100.202309141235/tools/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: ./Core/Src/main.o: in function `__static_initialization_and_destruction_0(int, int)’:
C:/Users/Admin/STM32CubeIDE/workspace_1.10.1/Lesson14_2/Debug/../Core/Src/main.cpp:50: undefined reference to `Debounce::Debounce(GPIO_TypeDef*, unsigned short, unsigned long)’
collect2.exe: error: ld returned 1 exit status
make: *** [makefile:88: Lesson14_2.elf] Error 1
«make -j4 all» terminated with exit code 2. Build might be incomplete.
17:51:08 Build Failed. 2 errors, 0 warnings. (took 1s.430ms)
В чем может быть проблема? Уже все пересмотрел, ошибок не нашел.