Autor Tema: freeRTOS en los STM32F4 con System Workbench  (Leído 4846 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
freeRTOS en los STM32F4 con System Workbench
« en: 27 de Mayo de 2015, 16:42:32 »
Bueno, estoy haciendo mi incursion por freeRTOS con los STM32F4, específicamente con la placa Nucleo 411.
Ya estuve probando y mostrando como crear un proyecto con CubeMX que nos permite tener el codigo de inicializacion listo para usar. El problema con esta técnica es que no nos pormetite, por ahora (o por lo menos yo no lo conseguí) es tener las librerías HAL como una librería compartida para los proyectos del worksapece, sino que se copian los driver usados para el proyecto a la carpeta del proyecto. Entonces, si tenemos varios proyectos que hacen uso de las HAL, nuestro workspace ocupa un tamaño terrible en disco y si queremos compartir los proyectos sucede lo mismo, cada proyecto posee una copia de las HAL. En este caso, voy a mostrar como configurar un workspace con las HAL como librerías compartidas, sin usar MX y con freeRTOS incluido.

Bueno, como primer paso hay que crear una carpeta nueva en el disco, preferentemente cerca del C:\ y sin espacios en el nombre. Dicha carpeta la usaremos como workspace. Entramos al SW (system workbench) y elegimos dicha carpeta como worksapce. Nos vamos a File -> New -> C Project. Ponemos el nombre al proyecto y elejimos AC6 como tipo de proyecto:



luego apretamos Next (se crean las configuraciones para Debug y Release) -> Elegimos la MCU o la placa de evaluacion que estamos usando y en la siguiente pantalla elejimos las Cube HAL como ilbrerias de firmware y seleccionamos freeRTOS como third party utility:



Apretamos finish y se nos crean 2 proyectos en el workspace, uno de las librerías Cube HAL como librería compartida y otro del proyecto nuestro. Si en este punto intentamos compilar nos aparecem varios errores y hay que hacer varios retoques:



Como primer medida está faltando el freeRTOS_Config.h necesario para el funcionamiento. Hay un template (freeRTOS_ConfigTemplate.h) con el que renombrandolo podemos compilar, pero hay que revisarlo para ver que se adapte a nuestros requerimientos.
Otro problema es que están todos los ports de los distintos micros. A nosotros nos sirven solo el de GCC->ARM_CM4F y por lo tanto debemos eliminar el resto, dejando el proyecto como sigue:



(ver que aún no renombre el freeRTOS_Config.h)
Otro cambio importante es habilitar en ambos proyectos el uso del FPU por hardware:



bien, con todos estos cambios, vamos a Build y tendríamos que poder crear nuestro proyecto sin problemas:



Espero les sirva!
« Última modificación: 22 de Agosto de 2015, 14:16:19 por elgarbe »
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Armando el workspace del SystemWorkbench para freeRTOS
« Respuesta #1 en: 09 de Agosto de 2015, 00:34:23 »
Bueno, por suerte, con la nueva version del SystemWorkbench (1.3) al crear un nuevo proyecto con freeRTOS se incluye y preconfigura todo lo necesario para tener un proyecto listo para ser compilado y empezar a escribir código del RTOS.



Hasta aquí tenemos un simple programa, el cual está preparado para funcionar con el RTOS, pero que no ha creado ninguna tarea ni ha iniciado el scheduler. La configuracion del reloj es la mínima para funcionar y es la que se configura en startup_stm32f411xe.s en la línea 111:
Código: [Seleccionar]
/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
    bl __libc_init_array
/* Call the application's entry point.*/
  bl  main
  bx  lr   
.size  Reset_Handler, .-Reset_Handler

Esa funcion SystemInit la encontramos en el archivo system_stm32f4xx.c que pertenece a nuestro proyecto.

Lo ue vamos a hacer en este proyecto es tomar la inicializacion de las librerias HAL, vamos a configurar el reloj interno, pero encendiendo el PLL para tener mayor velocidad, configurar los periféricos y luego agregar el código del RTOS.

Saludos
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Armando el workspace del SystemWorkbench para freeRTOS
« Respuesta #2 en: 09 de Agosto de 2015, 23:45:04 »
Bien, partiendo entonces de un proyecto nuevo, vacío, pero con el freeRTOS incluido vamos a hacer un primer ejemplo simple. La idea es una sola tarea que haga destellar un led... simple!

Primero incluimos los .h necesarios para el RTOS:

Código: [Seleccionar]
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"

luego definimos el handler de la tarea en cuestion:

Código: [Seleccionar]
void ToggleLED_Timer(void*);

luego agregamos el código que inicializa las HAL, la configuracion del oscillador externo y la inicializacion de un led de la placa:

Código: [Seleccionar]
void SystemClock_Config(void);

int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();

BSP_LED_Init(LED4);

Ahora biene la parte interesante del RTOS, la creacion de la primer tarea:

Código: [Seleccionar]
/* Create tasks */
xTaskCreate(
 ToggleLED_Timer,                 /* Function pointer */
 "Task1",                          /* Task name - for debugging only*/
 configMINIMAL_STACK_SIZE,         /* Stack depth in words */
 (void*) NULL,                     /* Pointer to tasks arguments (parameter) */
 tskIDLE_PRIORITY + 2UL,           /* Task priority*/
 NULL                              /* Task handle */
);

con esto ya podemos iniciar el scheduler:

Código: [Seleccionar]
/* Start the RTOS Scheduler */
vTaskStartScheduler();

/* HALT */
while(1);
}

Como se supone que la funcion del scheduler nunca retorna, ponemos un while(1) al final.

Finalmente el código de la tarea:

Código: [Seleccionar]
/**
 * TASK 1: Toggle LED via RTOS Timer
 */
void ToggleLED_Timer(void *pvParameters){

while (1)
{
BSP_LED_Toggle(LED4);

/*
Delay for a period of time. vTaskDelay() places the task into
the Blocked state until the period has expired.
The delay period is spacified in 'ticks'. We can convert
yhis in milisecond with the constant portTICK_RATE_MS.
*/
vTaskDelay(1500 / portTICK_RATE_MS);
}
}

El ultimo paso está un poco escondido. El RTOS necesita una base de tiempo para funcionar, generalmente usa el SysTick Timer. Pero al generar el proyecto se nos ha creado un archivo stm32f4xx_it.c en el que están los handler de las interrupciones y encontramos justamente la del Systick:

Código: [Seleccionar]
/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
HAL_IncTick();
HAL_SYSTICK_IRQHandler();
}

el freeRTOS tiene un port llamado xPortSysTickHandler, entonces lo que debemos hacer es colocar dicha funcion dentro de la rutina del SysTick_Handler:

Código: [Seleccionar]
void SysTick_Handler(void)
{
xPortSysTickHandler();
HAL_IncTick();
HAL_SYSTICK_IRQHandler();
}

Estoy leyendo que hay formas más elegantes de hacer esto, pero hasta que no las entienda bien, dejo esta que sé que funciona...

Bueno, compilamos descargamos a la discovery y obtendremos nuestro led verde destellando cada 1.5 seg

En la próxima veremos de implementar mas tareas y manejar la prioridades....

Saludos!
« Última modificación: 23 de Agosto de 2015, 11:30:39 por elgarbe »
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Armando el workspace del SystemWorkbench para freeRTOS
« Respuesta #4 en: 10 de Agosto de 2015, 22:58:39 »
Avancemos un poco más y creemos un proyecto con 2 tareas. Para ello, tomamos el proyecto anterior, le damos click con el botón derecho y elegimos Copy, luego boton derecho nuevamente y Paste. Ahí cambiamos el nombre del proyecto y tendremos una copia funcional del anterior. Hacemos boton derecho y le damos a Clean.

Bueno, ahora agreguemos una segunda tarea que haga un toggle de otro LED pero con 500mseg de intervalo. Las dos con la misma prioridad:

Código: [Seleccionar]
#include "stm32f4xx.h"
#include "stm32f4_discovery.h"

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"

void ToggleLED_Timer(void*);
void ToggleLED_IPC(void*);
void SystemClock_Config(void);

int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();

BSP_LED_Init(LED4);
BSP_LED_Init(LED3);

/* Create tasks */
xTaskCreate(
  ToggleLED_Timer,                 /* Function pointer */
  "Task1",                          /* Task name - for debugging only*/
  configMINIMAL_STACK_SIZE,         /* Stack depth in words */
  (void*) NULL,                     /* Pointer to tasks arguments (parameter) */
  tskIDLE_PRIORITY + 2UL,           /* Task priority*/
  NULL                              /* Task handle */
);
  xTaskCreate(
  ToggleLED_IPC,
  "Task3",
  configMINIMAL_STACK_SIZE,
  (void*) NULL,
  tskIDLE_PRIORITY + 2UL,
  NULL);

/* Start the RTOS Scheduler */
vTaskStartScheduler();

/* HALT */
while(1);
}

/**
 * TASK 1: Toggle LED via RTOS Timer
 */
void ToggleLED_Timer(void *pvParameters){

while (1)
{
BSP_LED_Toggle(LED4);

/*
Delay for a period of time. vTaskDelay() places the task into
the Blocked state until the period has expired.
The delay period is spacified in 'ticks'. We can convert
yhis in milisecond with the constant portTICK_RATE_MS.
*/
vTaskDelay(1500 / portTICK_RATE_MS);
}
}


/**
 * TASK 3: Toggle LED via Inter-Process Communication (IPC)
 *
 */
void ToggleLED_IPC(void *pvParameters) {
while (1)
{
BSP_LED_Toggle(LED3);
/*
Delay for a period of time. vTaskDelay() places the task into
the Blocked state until the period has expired.
The delay period is spacified in 'ticks'. We can convert
yhis in milisecond with the constant portTICK_RATE_MS.
*/
vTaskDelay(500 / portTICK_RATE_MS);
}
}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  __PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
                              |RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

}

aquí, el secreto de estas dos tareas simples es la funcion vTaskDelay(500 / portTICK_RATE_MS); la cual bloquea la tarea durante el tiempo pasado como parámetro y al blouear la tarea permitimos que se ejecuten las demás.

Bueno, ahora empezamos con algo un poco más interesante. Comunicacion entre Tareas. Que pasa si queremos hacer un toggle de un led al presionar un pulsador. Pues la solucion es crear dos tareas, una que lea el estado del pulsador y la otra que haga el toggle del led, pero debemos sincronizarlas entre sí. Para ello se utilizan los message queue. La tarea del LED lee la queue ni bien arranca y si no encuentra el mensaje, se queda bloqueada hasta recivirlo. Por otra parte la tarea del pulsador espera detectar la pulsacion y cuando la detecta pone el mensaje en la queue que desbloqueará la otra tarea:

Creamos la queue
Código: [Seleccionar]
/* Create IPC variables */
pbq = xQueueCreate(10, sizeof(int));
if (pbq == 0)
{
while(1); /* fatal error */
}

Luego creamos las 3 tareas

Código: [Seleccionar]
/* Create tasks */
xTaskCreate(
ToggleLED_Timer,                 /* Function pointer */
"Task1",                          /* Task name - for debugging only*/
configMINIMAL_STACK_SIZE,         /* Stack depth in words */
(void*) NULL,                     /* Pointer to tasks arguments (parameter) */
tskIDLE_PRIORITY + 2UL,           /* Task priority*/
NULL                              /* Task handle */
);
xTaskCreate(
DetectButtonPress,
"Task2",
configMINIMAL_STACK_SIZE,
(void*) NULL,
tskIDLE_PRIORITY + 2UL,
NULL);

xTaskCreate(
ToggleLED_IPC,
"Task3",
configMINIMAL_STACK_SIZE,
(void*) NULL,
tskIDLE_PRIORITY + 2UL,
NULL);

y finalmente las 3 funciones de las 3 tareas:

Código: [Seleccionar]
/**
 * TASK 1: Toggle LED via RTOS Timer
 */
void ToggleLED_Timer(void *pvParameters){

while (1)
{
BSP_LED_Toggle(LED4);

/*
Delay for a period of time. vTaskDelay() places the task into
the Blocked state until the period has expired.
The delay period is spacified in 'ticks'. We can convert
yhis in milisecond with the constant portTICK_RATE_MS.
*/
vTaskDelay(1500 / portTICK_RATE_MS);
}
}
/**
 * TASK 2: Detect Button Press
 * And Signal Event via Inter-Process Communication (IPC)
 */
void DetectButtonPress(void *pvParameters){
int sig = 1;

while (1) {
/* Detect Button Press  */
if(BSP_PB_GetState(BUTTON_KEY)>0) {
while(BSP_PB_GetState(BUTTON_KEY)>0)
vTaskDelay(100 / portTICK_RATE_MS); /* Button Debounce Delay */
while(BSP_PB_GetState(BUTTON_KEY)==0)
vTaskDelay(100 / portTICK_RATE_MS); /* Button Debounce Delay */
xQueueSendToBack(pbq, &sig, 0); /* Send Message */
}
}
}

/**
 * TASK 3: Toggle LED via Inter-Process Communication (IPC)
 *
 */
void ToggleLED_IPC(void *pvParameters)
{
int sig;
portBASE_TYPE status;

while (1)
{
status = xQueueReceive(pbq, &sig, portMAX_DELAY); /* Receive Message */
  /* portMAX_DELAY blocks task indefinitely if queue is empty */
if(status == pdTRUE) {
BSP_LED_Toggle(LED3);
}
}
}

Notar que hay una pequeña rutina de debounce. De este modo podemos crear tareas simples que se encarguen de diversas cosas e interconectarlas entre sí por medio de mensajes.

Saludos
-
Leonardo Garberoglio

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
Re:freeRTOS en los STM32F4 con System Workbench
« Respuesta #5 en: 11 de Abril de 2018, 11:57:26 »
Gracias por el hilo, elgarbe. Recientemente me inicio con FREERTOS y me ha venido bien leerlo

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:freeRTOS en los STM32F4 con System Workbench
« Respuesta #6 en: 11 de Abril de 2018, 13:24:48 »
No hay por que! me alegra que sea de utilidad!

Estoy sumergido en mis estudios y no me queda tiempo para el foro.

Espero sigan todos bien!

Abrazo y ya volveremos!
-
Leonardo Garberoglio