Небольшая предыстория

Жил себе спокойно, писал кодик в Keil и не парился. Писал изначально на СИ, но кода становилось все больше, а я все ленивее, перешел на С++ и ARM Compiler V6,19. Но пришел к тому что простых прерываний в таймере стало не достаточно, даже можно сказать не правильный подход. Задался желанием подключить какую-нибудь ОС. Выбор пал на FreeRTOS. Довольный, скачиваю операционку, подтягиваю файлы к проекту на плюсах, и получаю кучу ошибок. Попытка их устранить не увенчалась успехом. Вспомнил что есть CubeMX и там можно сгенерировать проект с уже подключенным freertoos. Проверил, 5-ым компилятором и языком СИ, проект отлично собирается, но как только переименовываем main.c в .cpp и выбираем компилятор 6,19, получаем кучу ошибок на ядро ОС, на определение inline и т.п. Так я и не смог подружить подружить ОС и C++ в Keil. Пришлось заменить среду разработки, изначально выбор пал на CubeIDE, все отлично дружится и собирается, но в душе оставались сомнения. В итоге финальным выбором стал vscode, далее опишу как создать проект именно в нем.

Здесь на сайте уже есть статьи по сборке проекта, ими и руководствовался, но пошел немного другим путем.

Пожалуй начнем

  1. Скачиваем CubeMX, CubeIDE, VScode.

  2. В 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

  3. Запускаем CubeMX и настраиваем новый проект.

    Первым делом настраиваем пины под дебаг, но это можно не делать, и самое главное - тактирование, можно еще инициализировать какой-нибудь пин для того чтобы писать потом меньше (заполнять структуру портов):

Hidden text

Далее подключаем FreeRTOS. Выбираем версию прокладки CMSIS_FreeRTOS. Вторая версия CMSIS_V2 насколько помню под микроконтроллеры F7:

Hidden text
Тут можно пару задач(очередей) дополнительно добавить, поднастроить config
Тут можно пару задач(очередей) дополнительно добавить, поднастроить config
Добавляю только необходимые библиотеки, генерировать с/h файлы для периферии, включить полный доступ
Добавляю только необходимые библиотеки, генерировать с/h файлы для периферии, включить полный доступ
Тут как кому нравится, я от куба беру только LL_RCC и LL_GPIO, порты мне нравится настраивать компактно и читаемо, с rcc функция тактирования появляется в main, ранее писал это все вручную, но с LL получается почти тоже самое
Тут как кому нравится, я от куба беру только LL_RCC и LL_GPIO, порты мне нравится настраивать компактно и читаемо, с rcc функция тактирования появляется в main, ранее писал это все вручную, но с LL получается почти тоже самое

Выбираем сгенерировать проект под 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
Я в проект добавляю две папки Files и Classes в которых вложены inc и srs
Я в проект добавляю две папки Files и Classes в которых вложены inc и srs

Симэйк настроен. Прописываем пути для 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)


  1. DungeonLords
    15.09.2023 03:41

    "Поместить туда код не так то просто. Я сделал файл обертку TaskWrapper.cpp + TaskWrapper.hpp, куда в хедер копирую прототип функции, а в cpp вырезаю с freertos.c саму функцию. И в файл прокладку подключаю файлы на C++. "
    А я думал получится CMSIS-RTOS2 использовать в C++ проекте?


    1. Sechih Автор
      15.09.2023 03:41
      +1

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


  1. 1eg10n
    15.09.2023 03:41
    +1

    ОС и C++ в Keil

    В main.c можно экспортировать функции из .cpp:
    extern void TempTask(void *pvParameters);
    Задачи раскидать по соответствующим файлам и уже в них использовать c++


    1. 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