Небольшая предыстория
Жил себе спокойно, писал кодик в Keil и не парился. Писал изначально на СИ, но кода становилось все больше, а я все ленивее, перешел на С++ и ARM Compiler V6,19. Но пришел к тому что простых прерываний в таймере стало не достаточно, даже можно сказать не правильный подход. Задался желанием подключить какую-нибудь ОС. Выбор пал на FreeRTOS. Довольный, скачиваю операционку, подтягиваю файлы к проекту на плюсах, и получаю кучу ошибок. Попытка их устранить не увенчалась успехом. Вспомнил что есть CubeMX и там можно сгенерировать проект с уже подключенным freertoos. Проверил, 5-ым компилятором и языком СИ, проект отлично собирается, но как только переименовываем main.c в .cpp и выбираем компилятор 6,19, получаем кучу ошибок на ядро ОС, на определение inline и т.п. Так я и не смог подружить подружить ОС и C++ в Keil. Пришлось заменить среду разработки, изначально выбор пал на CubeIDE, все отлично дружится и собирается, но в душе оставались сомнения. В итоге финальным выбором стал vscode, далее опишу как создать проект именно в нем.
Здесь на сайте уже есть статьи по сборке проекта, ими и руководствовался, но пошел немного другим путем.
Пожалуй начнем
В vscode ставим расширения: STM32 VS Code Extension, stm32-for-vscode они автоматом подтянут дополнительные расширения. Дополнительно можно добавить: C/C++ Extension Pack, C/C++ Themes, Doxygen Documentation Generator, CMake Language Support, Makefile Tools, cmake-format, YAML
-
Запускаем CubeMX и настраиваем новый проект.
Первым делом настраиваем пины под дебаг, но это можно не делать, и самое главное - тактирование, можно еще инициализировать какой-нибудь пин для того чтобы писать потом меньше (заполнять структуру портов):
Hidden text
Далее подключаем FreeRTOS. Выбираем версию прокладки CMSIS_FreeRTOS. Вторая версия CMSIS_V2 насколько помню под микроконтроллеры F7:
Hidden text
Выбираем сгенерировать проект под CubeIDE, генерируем, кубик пока не закрываем.
Hidden text
Открываем проект в CubeIDE. Собираем, все должно собраться. ПКМ щелкаем по проекту, выбираем конвертануть в C++. После этого жмем ПКМ по файлу main.c и ренеймим его в main.cpp. Повторяем это с main.h->main.hpp.
Hidden text
Екстерналим с "C" прототип функции инициализации фриртоса. Жмем Build project и радуемся успешной сборке. На этом этапе можно остаться в этой среде, но мы пойдем дальше.
Hidden text
Сейчас начнется немного мутнаяя история. Выходим из CubeIDE. Архивируем либо копируем в другое место. Удаляем файлы с исходной папки, остается только файл с проектом cubeMX, он у нас открыт. Заходим в не закрытый cubeMX, выбираем сгенерировать MakeFile. Генерируем проект. Далее, можно закрыть и удалить все что сгенерил куб, кроме make-файла, он нам нужен. Копируем его сразу в предыдущий проект, тот что заархивировали, ну ли когда vscode попросит. Еще нам понадобиться *.svd файл на наш микроконтроллер. Его можно скачать с официального сайта. Гуглим например stm32f072r8.svd file, без него не будет работать дебаг. Тоже закидываем в корень проекта. Ну или когда поросят.
Hidden text
Если заархивировали то распаковываем проект. Далее запускаем vscode с установленными расширениями. Заходим на левой панели в расширение stm32 VS code Extention и импортируем проект:
Hidden text
Тут имеются два расширения: СMake и STM32 for vscode. собирать проект можно и тем и другим. Кстати симэйком уже может и соберется если пощелкать на реконфигурацию и очистку, а вот STM32 for vscode еще не настроен. Настроим обоих.
Заходим в STM32 for vscode и выбираем установить строительные инструменты.
Hidden text
У меня на этом этапе било ошибку почему-то, я до этого ковырял файлы. переустанавливал vscode, что-то поломал. Решил это удалением этих каталогов и перезапуском Install Build tools
После установки жмем очистить проект либо собрать проект. Если до этого мы не добавили make файл, жмем Cancel. И закидываем его в корень проекта.
Hidden text
Если файл найден. Предложит перейти на С++.
Hidden text
Сейчас будет собираться и тем и другим расширением, если щелкать реконфиг, очистку и сборку, будет подчеркивать красным но собираться. Идем это фиксить.
Hidden text
Сначала займемся смэйком. Открываем файл CMakeList.txt и прописываем пути к файлам и каталогам. Тут я может что-то сделал не так, но работает.
Hidden text
Симэйк настроен. Прописываем пути для STM32 for vscode в файле STM32-for-VSCode.config.yaml
Hidden text
Теперь проекты собираются двумя расширениями. Правда заметил что по разному. В настройках симэйка я взял строчку здесь из какой-то статьи, она немного уменьшает выходной код.
Задачи фриртоса что создали в кубике, даже если не создали, то все равно дефолтная задача сгенерированная кубом(не путать с IdleTask) лежит в файле freertos.c, там же и функция. Поместить туда код не так то просто. Я сделал файл обертку TaskWrapper.cpp + TaskWrapper.hpp, куда в хедер копирую прототип функции, а в cpp вырезаю с freertos.c саму функцию. И в файл прокладку подключаю файлы на C++.
Hidden text
Вот так все собирается и работает. Пробовал генерировать кубом по разному, сразу мэйк а потом cubeide проект. но так не сработало.
Комментарии (4)
1eg10n
15.09.2023 03:41+1ОС и C++ в Keil
В main.c можно экспортировать функции из .cpp:
extern void TempTask(void *pvParameters);
Задачи раскидать по соответствующим файлам и уже в них использовать c++Sechih Автор
15.09.2023 03:41Можно и без extern, просто прототип прописывать. А тут получается наоборот, проект C++( main.cpp), а операционка на СИ. Я по сути так и сделал, задачи объявлены в одном месте куда куб их и проинициализировал. В этом файле куб сразу и объявит сверху прототипы обработчиков задач, т.к. сами функции обработчиков описаны ниже функций задачи, куда в качестве аргумента передается указатель на обработчик. Я создаю два файла TaskWrapper.hpp и TaskWrapper.cpp. В заголовочный прописываю прототипы обработчиков в вставке __cplusplus. Подключаю этот хедер в cpp и переношу туда сами обработчики из freertos.c. В этот файл подключаю с++-шные файлы.
Hidden text
#ifndef TASK_WRAPPER_H_ #define TASK_WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif void StartSensorTask(void const * argument); void StartMainTask(void const * argument); void StartCAN_RxTx_Task(void const * argument); #ifdef __cplusplus } #endif #endif
#include "TaskWrapper.hpp" #include "FreeRTOS.h" #include "task.h" #include "cmsis_os.h" #include "struct_init.hpp" //extern osMessageQId MT6701_Data_Send_Queue; //extern osMessageQId CANbus_Send_Queue; //extern osThreadId Sensor_Processing_Task; /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ //void Start_Sensor_Processing_Task(void const *argument) //{ /// portBASE_TYPE xStatus; // MathAlgs::MovingAverageFilter <uint16_t, 8> s_data_average; // uint16_t s_data; // for (;;) // { // xStatus = xQueueReceive(MT6701_Data_Send_Queue, &s_data, 0);//забираем данные из очереди // s_data = s_data_average.MAF_Handler(s_data);//фильтруем // MathAlgs::Decreasing_digits_with_rounding(MT6701_Encoder.ConvertToDegrees_x1000(s_data), 2);//конвертируем угол // если прошло столько-то тиков, отправляем в очередь отправки кан // osDelay(1); //taskYIELD(); // } //} /** * @brief Function implementing the myTask02 thread. * @param argument: Not used * @retval None */ //void Start_MT6701_Get_Data_Task(void const *argument) //{ //for (;;) //{ //забираем данные с датчика и суем в очередь // xQueueSend(MT6701_Data_Send_Queue, MT6701_Encoder.Get_DataPoint(), 0); // osDelay(4); // } //} /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ //void StartCAN_TxRx_Task(void const *argument) //{ // for (;;) // { // osDelay(1); // } //} /* * Сделать задачу периодической void StartCAN_TxRx_Task(void const *argument) { TickType_t xLastWakeTime; const TickType_t xDelay3ms = pdMS_TO_TICKS(3); //pdMS_TO_TICKS(3); /переводит миллисекунды в тики, можно обойтись без него, т.к. 1 тик равен одной мс for (;;) { vTaskDelayUntil(&xLastWakeTime, xDelay3ms); } } */ /* USER CODE BEGIN Header_StartSensorTask */ /** * @brief Function implementing the SensorTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartSensorTask */ void StartSensorTask(void const * argument) { /* USER CODE BEGIN StartSensorTask */ /* Infinite loop */ for(;;) { gyro_ICM20600.readZaxisAcc(&anglezA); gyro_ICM20600.readXaxisAcc(&anglexA); gyro_ICM20600.readYaxisAcc(&angleyA); gyro_ICM20600.readZaxisGyro(&anglezG); gyro_ICM20600.readXaxisGyro(&anglexG); gyro_ICM20600.readYaxisGyro(&angleyG); CAN_LED.Toggle(); osDelay(1); } /* USER CODE END StartSensorTask */ } /* USER CODE BEGIN Header_StartMainTask */ /** * @brief Function implementing the MainTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartMainTask */ void StartMainTask(void const * argument) { /* USER CODE BEGIN StartMainTask */ /* Infinite loop */ for(;;) { osDelay(1); } /* USER CODE END StartMainTask */ } /* USER CODE BEGIN Header_StartCAN_RxTx_Task */ /** * @brief Function implementing the CAN_RxTx_Task thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartCAN_RxTx_Task */ void StartCAN_RxTx_Task(void const * argument) { /* USER CODE BEGIN StartCAN_RxTx_Task */ /* Infinite loop */ for(;;) { osDelay(1); } /* USER CODE END StartCAN_RxTx_Task */ }
Сгенерированный файл с задачами freertos.c
/* USER CODE BEGIN Header */ /** ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include <main.hpp> #include "FreeRTOS.h" #include "task.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ /* USER CODE END Variables */ osThreadId SensorTaskHandle; osThreadId MainTaskHandle; osThreadId CAN_RxTx_TaskHandle; osMessageQId SensorQueueHandle; osMessageQId CAN_Rx_QueueHandle; osMessageQId CAN_Tx_QueueHandle; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ /* USER CODE END FunctionPrototypes */ void StartSensorTask(void const * argument); void StartMainTask(void const * argument); void StartCAN_RxTx_Task(void const * argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /* GetIdleTaskMemory prototype (linked to static allocation support) */ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xIdleTaskTCBBuffer; static StackType_t xIdleStack[configMINIMAL_STACK_SIZE]; void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; *ppxIdleTaskStackBuffer = &xIdleStack[0]; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* place for user code */ } /* USER CODE END GET_IDLE_TASK_MEMORY */ /** * @brief FreeRTOS initialization * @param None * @retval None */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* Create the queue(s) */ /* definition and creation of SensorQueue */ osMessageQDef(SensorQueue, 16, uint16_t); SensorQueueHandle = osMessageCreate(osMessageQ(SensorQueue), NULL); /* definition and creation of CAN_Rx_Queue */ osMessageQDef(CAN_Rx_Queue, 16, uint16_t); CAN_Rx_QueueHandle = osMessageCreate(osMessageQ(CAN_Rx_Queue), NULL); /* definition and creation of CAN_Tx_Queue */ osMessageQDef(CAN_Tx_Queue, 4, uint16_t); CAN_Tx_QueueHandle = osMessageCreate(osMessageQ(CAN_Tx_Queue), NULL); /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* definition and creation of SensorTask */ osThreadDef(SensorTask, StartSensorTask, osPriorityNormal, 0, 128); SensorTaskHandle = osThreadCreate(osThread(SensorTask), NULL); /* definition and creation of MainTask */ osThreadDef(MainTask, StartMainTask, osPriorityNormal, 0, 128); MainTaskHandle = osThreadCreate(osThread(MainTask), NULL); /* definition and creation of CAN_RxTx_Task */ osThreadDef(CAN_RxTx_Task, StartCAN_RxTx_Task, osPriorityIdle, 0, 128); CAN_RxTx_TaskHandle = osThreadCreate(osThread(CAN_RxTx_Task), NULL); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ /* USER CODE END Application */
Если после сборки проекта не удалять файлы cubeIDE, то можно работать и в ней и в vscode. Только в cubeIDE нужно прописать путь к заголовкам и исходным "базовым" каталогам. Все ни как не могу узнать можно ли видеть глобальные переменные в риалтайме, а не по точке останова, в cubeIDE и в Keil с этим проблем нет. Здесь даже вопрос задал.
Hidden text
DungeonLords
"Поместить туда код не так то просто. Я сделал файл обертку TaskWrapper.cpp + TaskWrapper.hpp, куда в хедер копирую прототип функции, а в cpp вырезаю с freertos.c саму функцию. И в файл прокладку подключаю файлы на C++. "
А я думал получится CMSIS-RTOS2 использовать в C++ проекте?
Sechih Автор
Таким же макаром создал проект с CMSIS_RTOS_V2, проект собирается и работает, пока разницы не заметил, только разница в именах функций RTOS обертки в отличии от первой версии, буду ковырять дальше.