Вы находитесь на странице: 1из 60

SISTR

Sistemas operativos de tempo real


2017/2018
Objetivos

● Introdução aos sistemas operativos de tempo


real;
● FreeRTOS:
● Criação de tarefas;
● Controlo de ciclo de uma tarefa;
● Escalonador
● API.

2
MEEC – SISTR
2017/2018
O que vamos fazer?

● Usar o STM32F103RBT6 ARM Cortex-M3


estudado;
● Instalar um sistema operativo de tempo real
(RTOS);
● Estudar o RTOS desenvolvendo aplicações que
foquem:
– Gestão de tarefas (2 semanas);
– Mutex, semáforos; ( 1 semana);
– Filas de mensagens; (1 semana);
– Gestão de memória; (1 semana);
– Trabalho Final; (2 semanas);

3
MEEC – SISTR
2017/2018
Um Sistema Operativo?

● Um sistema operativo
no Cortex-M3?
● Não é um sistema
operativo convencional;
● Usaremos um sistema
operativo de tempo real
desenvolvido
especificamente para
aplicações embebidas,
neste caso para o
Cortex-M3.

4
MEEC – SISTR
2017/2018
Sistema Operativo
● Num sistema operativo (SO) clássico tal como o Linux, Windows, Mac, etc:
● O sistema operativo é instalado primeiro e de seguida são instaladas as aplicações
separadamente;
● O SO inicia e só depois as aplicações são iniciadas;
● O computador é completamente controlado pelo SO, o kernel não pode se parado;
● Neste caso as aplicações não podem bloquear o SO (ou pelo menos não deviam).

● Num sistema operativo de tempo real (RTOS):


● As aplicações são 'instaladas' juntamente com o RTOS;
● A aplicação desenvolvida pelo utilizador corre primeiro e só depois o RTOS é iniciado;
● Uma aplicação pode controlar o escalonador (por exemplo: parando-o);
● Se uma aplicação tiver um problema, esta pode bloquar completamente o RTOS (nestas
situações é necessário fazer um reset ao sistema ).

● Como podemos observar através desta comparação, por forma a poupar os


(poucos) recursos, o RTOS são assume que o utilizador escreve código com
bugs ou mesmo um vírus. Não há proteção para as situações normais que
ocorrem noutro SO, tais como: memory leaks, ciclos infinitos, etc.

5
MEEC – SISTR
2017/2018
Sistema Operativo de Tempo Real

● Sem um RTOS o programador é responsável pelo que


acontece dentro do processador, todos os bits de
configuração, controlo temporal, gestão das interrupções,
etc;
● Um RTOS pode ser usado para permitir uma maior
simplicidade e desenvolvimento rápido. Apenas há a
necessidade de escrever um conjunto de rotinas (tarefas)
independentes para que o conjunto todo funcione;
● Recorrendo a um RTOS o programador pode:
– Separar aplicações complexas em pequenas tarefas (que pode levar a
um melhor reaproveitamento do código);
– Evitar a complexidade da gestão temporal e de sequenciação de tarefas
(isto passa a ser uma das responsabilidades do sistema operativo);
– Melhorar a eficiência da comunicação entre as diferente partes do
programa (até agora era feito por variáveis globais entre as rotinas de
interrupção e a função principal, a main).

6
MEEC – SISTR
2017/2018
Arquitetura típica de um RTOS

application
User

Task 1 Task 2 ... Task n


application
System

RTOS
Hardware

CPU Peripherals

7
MEEC – SISTR
2017/2018
Características típicas num RTOS

● Um RTOS típico fornece as seguintes


funcionalidades:
– Gestão de tarefas– O RTOS deve ser capaz de criar e
apagar tarefas, bem como decidir quando lhes atribuir
tempo de CPU;
– Gestão de memória – alocar e gerir dinamicamente a
memória para cada tarefa e para o escalonador;
– Temporização – fornecer mecanismos de temporização;
– Comunicação entre tarefas e sincronização – fornecer
mecanismos que permitam que as tarefas comuniquem
entre si e se possam sincronizar.

8
MEEC – SISTR
2017/2018
Exemplos e aplicações

● Exemplo de alguns RTOS disponíveis:


– FreeRTOS (http://www.freertos.org);
– MicroC/OS (http://micrium.com);
– eCos (http://ecos.sourceware.org);
– VxWorks (http://www.windriver.com);
– RTEMS (http://www.rtems.org)
– ...

● Áreas de aplicação:
– Robótica (Ex: Mars Pathfinder);
– Telecomunicações (Ex: routers)
– Equipamento médico (Ex: pacemaker);
– Aviónica (Ex: Sistemas de navegação);
– Automóvel (Ex: Injeção de combustível);
– ...
9
MEEC – SISTR
2017/2018
Porquê o FreeRTOS?

● Apesar de existirem muitos RTOS disponíveis, iremos estudar o


FreeRTOS;

● Principais características:
– Várias arquiteturas suportadas (ARM, AVR, PIC, 8051, MSP430, Renesas, etc.);
– Funciona em microcontroladores com poucos recursos (tais como microcontroladores
de 8 bits com 8 KiB de memória ou menos em alguns casos);
– Suportadas várias ferramentas de desenvolvimento (incluindo o GCC);
– Projetos configurados para cada microcontrolador/ambiente de desenvolvimento;
– Open source (licença GPL modificada), podendo ser usado em aplicações comerciais;
– Grande comunidade de utilizadores (não há suporte técnico – apenas na versão
comercial).

10
MEEC – SISTR
2017/2018
Versões de software usadas

● O código exemplo fornecido nas aulas funciona


nas seguintes versões de software:
● FreeRTOS versão 9.0.0
● STM32F10X Standard Library versão 1.5.1
(22/05/2015)
● OpenSTM32 Ac6 V1.8.0 e V2.2

– Nota: se forem usadas outras versões poderão haver


problemas de compatibilidade (no entanto,
provavelmente tudo funcionará corretamente).

11
MEEC – SISTR
2017/2018
FreeRTOS

12
MEEC – SISTR
2017/2018
Caso de estudo

● Lembram-se do Sojourner (Mars Rover)?

● Ler a história dos problemas no .pdf do Moodle.

13
MEEC – SISTR
2017/2018
Introdução ao FreeRTOS

● Uma boa forma de começar


a usar o FreeRTOS
consiste em usar um
projeto já configurado
(exemplo) para o nosso
microcontrolador/ambiente
de desenvolvimento.

14
MEEC – SISTR
2017/2018
Estrutura ficheiros do FreeRTOS
FreeRTOS

Ficheiros GPL Código


Demo exemplo License modificada Source fonte

Licens
Ficheiros usados e
por
Common todos os exemplos Include Ficheiros .h

Ficheiros específicos para


compilador/microcontrolador
Demo_port 1 Portable

Alocadores de
Demo_port 2 Lista de todos os
exemplos disponíveis
MemMang memória

... tasks.c

Demo_port n list.c Isto é o kernel!

Dentro de cada exemplo:


queue.c
- FreeRTOSConfig.h
- main.c crotine.c
timers.c
event_
groups.c
15
MEEC – SISTR
2017/2018
Nomes das variáveis FreeRTOS

● Os nomes das variáveis são sempre precedidos


com uma letra correspondente ao seu tipo: c
(char), l (long), e (enum), s (short) e v (void).
Exemplo:
● cStatus – variável do tipo char

● É possível ter mais do que um identificador: p


(pointer) e u (unsigned). Exemplos:
● pcWriteBuffer – do tipo pointer char
● ulTotalTime – do tipo unsigned long

● Outros tipos de variáveis, tais como estruturas e


dados dependentes da arquitetura (BaseType_t)
têm o prefixo x.
16
MEEC – SISTR
2017/2018
Nomes das funções FreeRTOS

● Os nomes das funções seguem as mesmas


regras que as variáveis. Exemplos:
● vTaskDelete – Função que não retorna nada (void);
● xTaskCreate – retorna um valor do tipo BaseType_t

● A seguir ao carácter de identificação, os nomes


das funções são também precedidos com o nome
do ficheiro de onde estão definidos. Exemplos:
● xQueueHandle – definido no queue.h
● xTaskCreate – definido no task.h

● As funções privadas têm o identificador prv.

17
MEEC – SISTR
2017/2018
Nomes das macros FreeRTOS

● Todas as macros têm o prefixo dos nomes dos


ficheiros onde estão definidas. Exemplos:
● configUSE_PREEMPTION – definido no FreeRTOSconfig.h;
● queueSEND_TO_BACK – está definido no queue.h.

● Macros usadas em algumas funções que


retornem verdadeiro/falso:
● pdTRUE e pdPASS retorna 1;
● pdFALSE e pdFAIL retorna 0.

18
MEEC – SISTR
2017/2018
Tipos de dados usados

● Alguns tipos de dados importantes no FreeRTOS:


– TickType_t (antigo portTickType) – pode ser de 16 ou 32
bit, conforme configurado no FreeRTOSConfig.h (usaremos
32 bit);
– BaseType_t (antigo portBASE_TYPE) – dependente da
arquitetura (no nosso caso é de 32 bit).
– UBaseType_t – dependente da arquitetura mas sem sinal
(unsigned).

● Nota: o portTickType e portBASE_TYPE são tipos de


variáveis que foram descontinuados a partir da versão
8.0 do FreeRTOS (tal como o portCHAR, portLONG,
etc). A partir desta versão são usados os tipos de
variáveis declaradas no stdint.h.

19
MEEC – SISTR
2017/2018
Partes importantes no FreeRTOS

● Tarefa
– Rotina que isoladamente desempenha uma função qualquer (p.e. ler dados de
um sensor);
● Prioridade
– Nível de importância de uma dada tarefa face às outras;
● Escalonador (ou kernel)
– Algoritmo que gere as tarefas que vão correr mediante a sua prioridade;
● Tarefa Idle
– Tarefa criada aquando do arranque do escalonador;
– Tarefa executada sempre que não há mais nada para fazer;
– Tem a prioridade mais baixa.
● Tick counter
– Contador de ticks que é incrementado a uma cadência fixa;
– Contém o número de ticks que passaram desde que o escalonador foi iniciado;
– Cada tick corresponde a uma base de tempo conhecida.

20
MEEC – SISTR
2017/2018
Criação de uma aplicação

● Como vimos anteriormente, uma aplicação


baseada no FreeRTOS pode ser criada
editando apenas dois ficheiros:
● FreeRTOSConfig.h
– Configuração das várias funcionalidades do FreeRTOS;

● main.c
– Criação das tarefas, arranque do escalonador, etc…

● Vamos analisar o ficheiro de configuração, o


FreeRTOSConfig.h e o ficheiro main.c para o
exemplo fornecido.
21
MEEC – SISTR
2017/2018
Configuração do FreeRTOS (I)

● FreeRTOSConfig.h

#define configUSE_PREEMPTION 1 – se o sistema é preemptivo (1) ou não(0).


#define configUSE_IDLE_HOOK 0 – se é usado uma função de callback em idle (1) ou não (0).
#define configUSE_TICK_HOOK 0 – se é usada uma função de callback quando o tick counter é
incrementado (1) ou não(0). É usado normalmente para implementar funcionalidades de temporização.
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) – Frequência do core do
microcontrolador em Hertz.
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) – Taxa do tick interrupt. Um número elevado
significa uma maior resolução mas reduz o tempo atribuído a cada tarefa.
#define configMAX_PRIORITIES ( ( UBaseType_t ) 5 ) – Número máximo de diferentes prioridades
utilizadas. É possível atribuir a mesma prioridade a diferentes tarefas. Um número elevado significa uma
prioridade alta, um número baixo significa uma prioridade baixa.

22
MEEC – SISTR
2017/2018
Configuração do FreeRTOS (II)
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) – Especificado em words.
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) – RAM disponível para o kernel do RTOS.
#define configMAX_TASK_NAME_LEN ( 16 ) – Tamanho máximo do nome atribuído a cada tarefa.
#define configUSE_16_BIT_TICKS 0 – Usar (1) ou não (0) 16 bit no registo que conta o número de ticks
que passaram desde que o kernel foi iniciado.
#define configIDLE_SHOULD_YIELD 1 – Quando as tarefas tem a mesma prioridade que a tarefa de idle,
se o CPU é ou não dividido por todas duas de igual forma(1), ou se as tarefas com a mesma prioridade de idle
têm mais tempo de CPU disponível (0);
#define configUSE_MUTEXES 1 – Ativar (1) ou não (0) a funcionalidade mutex.

● Nota: Existem outras configuração possíveis que serão consideradas à medida que vão sendo necessárias.

23
MEEC – SISTR
2017/2018
LED pisca – função main

TaskHandle_t HandleTask1; Início

int main( void )


{
/* Setup the hardware* / Setup
prvSetupRCC(); hardware
prvSetupGPIO();

Criar as
/* Create the tasks */
tarefas
xTaskCreate( prvFlashTask1, "Flash1", configMINIMAL_STACK_SIZE,
NULL, 1, &HandleTask1);

Iniciar o
/* Start the scheduler. */
Escalonador
vTaskStartScheduler();

/* Will only get here if there was not enough heap space to create the idle
task. */
Fim
return 0;
}

24
MEEC – SISTR
2017/2018
LED pisca – hardware setup
static void prvSetupRCC( void )
{
/* RCC configuration - 72 MHz */
...
}

static void prvSetupGPIO( void )


{
/* GPIO configuration */
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB , ENABLE );

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

25
MEEC – SISTR
2017/2018
LED pisca – Tarefa
static void prvFlashTask1( void *pvParameters )
{
for( ;; ) Início
{
/* Block 1 second. */
VTaskDelay( ( TickType_t ) 1000 / portTICK_RATE_MS ); Bloqueia
1 segudo

/* Toggle the LED */


GPIO_WriteBit(GPIOB, GPIO_Pin_0, Troca estado
1 – GPIO_ReadOutputDataBit( GPIOB, GPIO_Pin_0 ) );
do LED

}
}

26
MEEC – SISTR
2017/2018
Criação de tarefas
● BaseType_t xTaskCreate( pdTASK_CODE pvTaskCode, const char * const
pcName, unsigned short usStackDepth, void *pvParameters, UBaseType_t
uxPriority, xTaskHandle *pvCreatedTask );

– pvTaskCode - apontador para uma função que não retorna;


– pcName - string com nome para identificar a tarefa (apenas para debug);
– usStackDepth - tamanho da stack para a tarefa;
– pvParameters - parâmetros de entrada para a função da tarefa;
– pxPriority - valor da prioridade da tarefas;
– pvCreatedTask - handle de controlo da tarefa.

● Retorna:
– pdPass no caso de sucesso na criação da tarefa
– errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY no caso de não criar a tarefa.

● Exemplo:
#include “task.h”
TaskHandle_t xHandle;
BaseType_t xReturn;

xReturn = xTaskCreate ( vATaskFunction, "NAME", STACK_SIZE, NULL, 1, &xHandle );

27
MEEC – SISTR
2017/2018
Task Control Block (TCB)

● Toda a informação gerada a partir da criação


de uma tarefa é guardada numa estrutura
chamada de Task Control Block (TCB);
● Esta estrutura fica guardada em memória RAM
e mantém informações importantes da tarefa,
tais como:
– nome;
– prioridade;
– apontador para o início da stack;
– tamanho da stack;
– etc.

28
MEEC – SISTR
2017/2018
Implementação de uma tarefa

● A função vATaskFunction deve ser implementada


como um ciclo infinito sem nunca retornar:

void vATaskFunction( void *pvParameters ) Início

{
/* Initialization code goes here. */
Inicializações
for( ;; )
{
/* Task application code goes here. */ Executar a
tarefa
}
/* Tasks must be an infinite loop it should
never get here. */
}

29
MEEC – SISTR
2017/2018
Suspensão de uma tarefa

● Suspensão de uma tarefa:


– void vTaskSuspend( xTaskHandle xTaskToSuspend );
● xTaskToSuspend é o handler da tarefa que vai ser suspensa (NULL para a tarefa atual);

● Retomar a execução de uma tarefa:


– void vTaskResume( xTaskHandle xTaskToResume );
● xTaskToResume é o handler da tarefa cuja execução vai ser retomada;
● A função vTaskResume tem de ser chamada a partir de outra tarefa (se uma tarefa estiver
suspensa não pode retomar a execução sozinha).

● Retomar a execução de uma tarefa a partir de uma ISR


– BaseType_t xTaskResumeFromISR( xTaskHandle xTaskToResume );
● xTaskToResume é o handler da tarefa cuja execução vai ser retomada;
● Retorna pdTRUE se for necessário forçar a mudança do contexto ou pdFALSE noutra situação.

● Apagar uma tarefa:


– void vTaskDelete( xTaskHandle_t xTaskToDelete );
● xTaskToDelete é o handler da tarefa que vai ser apagada (NULL para a tarefa atual);

30
MEEC – SISTR
2017/2018
Controlo de ciclo de uma tarefa

31
MEEC – SISTR
2017/2018
Controlo de ciclo - vTaskDelay
● A função vTaskDelay permite fazer um delay numa tarefa por um determinado
tempo (em ticks):
– void vTaskDelay( TickType_t xTicksToDelay );
● xTicksToDelay é o número de ticks que se pretende esperar

● portTICK_RATE_MS pode ser usado para transformar tempo num número de ticks:
– const TickType_t xTicksToDelay = 1000 / portTICK_RATE_MS;
● A variável xTicksToDelay terá o número de ticks equivalente a 1000 ms.

● Exemplo:
for( ;; )
{
/* the function will block here for 1000 ms. */
vTaskDelay( 1000 / portTICK_RATE_MS);
/* do some stuff here every 1000 ms. */
/* … */
}

32
MEEC – SISTR
2017/2018
Problemas vTaskDelay (I)

● Apesar de poder ser usada para fazer delays


temporais, esta função não é a melhor para
controlar a frequência de execução de uma tarefa
cíclica;
● Nesta situação, o tempo de execução depende do
tempo usado na própria tarefa do tempo de outras
tarefas e do tempo de execução de interrupções
que ocorram entretanto.
vTaskDelay( 1000 / portTICK_RATE_MS )

T1 T1
t t1 t2 Time

1000 ms 1000 ms + task 1 time


33
MEEC – SISTR
2017/2018
Controlo de ciclo vTaskDelayUntil

● O vTaskDelayUntil usa um tempo absoluto para o controlo do ciclo.


● Para isso, é necessário guardar o tempo que passou desde a última
vez para calcular quando a função deverá desbloquear novamente.

vTaskDelayUntil( &xLastExecutionTime, 1000 / portTICK_RATE_MS )

T1 T1
t t1 t2 Time

1000 ms 1000 ms
– void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, TickType_t
xTimeIncrement )
● pxPreviousWakeTime contém o valor do tempo em que a função desbloqueou a última vez;
● xTimeIncrement contém o valor do período de quando a função deverá desbloquear novamente.

34
MEEC – SISTR
2017/2018
Exemplo vTaskDelayUntil

/* Declare the variable xLastExecutionTime. */


TickType_t xLastExecutionTime;

/* Initialise the xLastExecutionTime variable by getting the current tick count. */


xLastExecutionTime = xTaskGetTickCount();

for( ;; )
{
/* The function will block here for 1000 ms. */
vTaskDelayUntil( &xLastExecutionTime, 1000 / portTICK_RATE_MS);

/* Do some stuff in here every 1000 ms. */


/* … */
}

35
MEEC – SISTR
2017/2018
Escalonador do FreeRTOS

36
MEEC – SISTR
2017/2018
Escalonamento das tarefas (I)

● Temos estado a discutir a existência de várias


tarefas a correr sem nos preocuparmos com o
facto de haver necessidade de acontecerem
coisas ao mesmo tempo e só termos um
microcontrolador/core.

● Como é que o escalonador gere estas tarefas?

37
MEEC – SISTR
2017/2018
Escalonamento das tarefas (II)

● As tarefas não estão todas a correr ao mesmo


tempo, apesar de assim o parecer. O RTOS vai
atribuindo tempo de CPU para cada tarefa dando
a ideia de que todas correm ao mesmo tempo.

Task 3
execution
Task 2
execution
Task 1
execution
t1 t2 time tn

38
MEEC – SISTR
2017/2018
Escalonador do FreeRTOS

● Ao escalonador associamos normalmente uma


política de escalonamento, que não é mais do
que um algoritmo que decide que tarefa será
executada. O FreeRTOS simplesmente garante
que a tarefa com a maior prioridade será
executada
● Escalonamento Round Robin com as tarefas de
igual prioridade;
● O escalonador usa uma interrupção de um
temporizador para gerar a base de tempo
associada às operações de escalonamento. No
caso do Cortex-M3 é usada a interrupção do
SysTick.
39
MEEC – SISTR
2017/2018
SysTick

● (Relembrando o SysTick de Sistemas


Embebidos…);
● O Cortex System Timer (SysTick) consiste num
contador decrescente de 24 bit que, quando
configurado e ativado, gera uma interrupção
sempre que atinge o valor 0 recomeçando
novamente a contagem.

HCLK Cortex system timer


/8 máx. 9 MHz (HCLK/8)

Cortex system timer


máx. 72 MHz (HCLK)

40
MEEC – SISTR
2017/2018
Mudança de contexto

● Sempre que a rotina de interrupção do SysTick corre,


o tick count é incrementado e o FreeRTOS verifica se
é necessário parar a tarefa atual e colocar outra
tarefa a correr;
● Este processo é chamado de mudança de
contexto;
● A mudança de contexto é desencadeada e
executada pelo escalonador, ou seja, as tarefas não
sabem quando é que é lhes vai ser dado ou retirado
tempo de CPU;
● Há sempre algum tempo que se perde no processo
de mudança de contexto (que em algumas situações
pode ser necessário ter em conta).
41
MEEC – SISTR
2017/2018
Configuração do SysTick

● O SysTick é configurado com base no valor


configTICK_RATE_HZ do ficheiro de configuração
FreeRTOSConfig.h.
● Esta taxa deve ser configurada com um valor que
não pode ser muito elevado nem muito baixo:
● Valor muito elevado implica que o escalonador corre
demasiadas vezes, dando pouco tempo a cada tarefa.
Além disso há um overhead desnecessário na execução
do código que efetua a mudança de contexto.
● Valor muito baixo significa que o escalonador demora
demasiado tempo até fazer uma mudança de contexto,
não garantindo assim requisitos de tempo real.

42
MEEC – SISTR
2017/2018
Systick no FreeRTOS
/* Setup the systick timer to generate the tick interrupts at the required
frequency. */
__attribute__(( weak )) void vPortSetupTimerInterrupt( void )
{ Escalonador
...
/* Configure SysTick to interrupt at the requested rate. */
Configurar
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
Interrupção
Systick
/* Enable SysTick interrupt*/
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
Ativar
portNVIC_SYSTICK_INT_BIT | Interrupção
portNVIC_SYSTICK_ENABLE_BIT; Systick
}

● As constantes usadas nesta função são definidas no ficheiro FreeRTOSconfig.h: Fim


– configSYSTICK_CLOCK_HZ - frequência de funcionamento do microcontrolador (72000000 Hz no
nosso caso);
– configTICK_RATE_HZ taxa a que o kernel corre (vamos usar 1000 Hz).
● Neste caso o SysTick conta desde (72000000/1000)-1 = 71999 até 0, gerando assim uma
interrupção a cada 1 ms.

43
MEEC – SISTR
2017/2018
Rotina de interrupção do SysTick

● É nesta rotina de interrupção que o tick counter


é incrementado. O tick counter consiste numa
variável de 32 ou 16 bits (conforme
configuração no FreeRTOSConfig.h);
● O número de ticks corresponde ao número de
vezes que correu a interrupção do SysTick;
● Todas as operações de escalonamento e
temporização baseiam-se neste valor;
● O overflow desta variável é gerido pelo
FreeRTOS;

44
MEEC – SISTR
2017/2018
Systick handler no FreeRTOS
void xPortSysTickHandler( void )
{
Systick
/* The SysTick runs at the lowest interrupt priority, so when this Handler
interrupt executes all interrupts must be unmasked. There is
therefore no need to save and then restore the interrupt mask
value as its value is already known. */ Incrementar
tick counter
( void ) portSET_INTERRUPT_MASK_FROM_ISR();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE ) Ativar
Mudança S
{ de Interrupção
/* A context switch is required. Context switching is contexto? PendSV
performed in the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; N
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); Fim
}

45
MEEC – SISTR
2017/2018
Mudança de contexto no PendSV

● A mudança de contexto é realizada dentro do


handler de interrupção PendSV;
● O PendSV é uma interrupção ativada por software
de baixa prioridade, ou seja, apenas corre quando
todas as outras interrupções terminam;
● Desta forma minimiza-se o tempo de atendimento
a outras interrupções que possam ter surgido
entretanto;
● Durante a mudança de contexto o escalonador
guarda todos os registos de trabalho da tarefa
atual e restaura os registos da tarefa que vai ser
executada.
46
MEEC – SISTR
2017/2018
PendSV Handler no FreeRTOS
void xPortPendSVHandler( void )
{
__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
PendSV
" stmdb r0!, {r4-r11} \n" /* Save the remaining registers. */ Handler
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r3, r14} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" bl vTaskSwitchContext \n" Mudança de
" mov r0, #0 \n" Contexto
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n"
" \n" /* Restore the context, including the critical nesting count. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11} \n" /* Pop the registers. */
" msr psp, r0 \n" Fim
" isb \n"
" bx r14 \n"
" \n"
" .align 2 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
);
}

47
MEEC – SISTR
2017/2018
Estados de uma tarefa

● O escalonador vai gerindo as


tarefas mantendo-as num dos
quatro estados seguintes: Suspended
vTaskSuspend
– Running – A tarefa está a correr (só há
uma tarefa neste modo de cada vez);
vTaskSuspend
vTaskResume

– Ready – A tarefa está pronta para ser


executada (mas há uma de maior xTaskCreate Dispatch
Ready Running
prioridade a correr); Preemption, taskYIELD

– Blocked – Uma tarefa que está à espera vTaskSuspend


Event
de um evento temporal ou externo (ex: a
tarefa está à espera de usar um
periférico que ainda não está disponível); Blocking API
Blocked

– Suspended – Uma tarefa que foi


suspensa explicitamente (chamada de
vTaskSuspend).

48
MEEC – SISTR
2017/2018
Prioridade de uma tarefa

● Cada tarefa tem de ter uma prioridade atribuída;


● No FreeRTOS um valor mais elevado significa maior prioridade e um
valor mais baixo significa menor prioridade (contrário às prioridades
das interrupções do Cortex-M3!);
● O valor da prioridade pode variar entre:
– tskIDLE_PRIORITY – prioridade mais baixa e é o valor da prioridade atribuído à tarefa
de idle tendo o valor 0 (zero);
– configMAX_PRIORITIES – prioridade mais alta e está definido no FreeRTOSconfig.h
podendo ser alterado para um valor qualquer.
● Tarefas diferentes podem ter valores iguais de prioridade, incluindo o
mesmo valor da tarefa de idle.
● É aconselhável, no entanto, usar sempre valores acima da tarefa idle:
– tskIDLE_PRIORITY + 1;
– tskIDLE_PRIORITY + 2;
– tskIDLE_PRIORITY + 3
– ...

49
MEEC – SISTR
2017/2018
Alteração da prioridades

● A prioridade da tarefa pode ser atribuída na altura


da criação (é um parâmetro do xTaskCreate) ou
durante a sua execução.
● Obter a prioridade de uma tarefa:
– UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
● xTask é o handler da tarefa (NULL para a própria tarefa);
● Retorna o valor da prioridade da tarefa;
● Exemplo: uxPriority = uxTaskPriorityGet( xTask );

● Mudar a prioridade de uma tarefa:


– void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t
uxNewPriority);
● xTask é o handler da tarefa e o uxNewPriority o novo valor de
prioridade;
● Exemplo: vTaskPrioritySet( xTask, tskIDLE_PRIORITY + 1 );

50
MEEC – SISTR
2017/2018
Escalonador preemptivo

● Quando o FreeRTOS está configurado para ser preemptivo as tarefas


com prioridade mais alta podem interromper as tarefas com prioridade
mais baixa
– Tarefa 1 e 3 com a mesma prioridade e a tarefa 2 com a prioridade mais alta

Task 3
execution
Task 2
execution
Task 1
execution

t1 t2 t3 t4 t5 t6 t7 time
– Em t1 a tarefa 1 iniciou;
– Em t2 a tarefa 2 está pronta a correr por isso a tarefa 1 foi suspensa e tarefa 2 iniciou;
– Em t3 a tarefa 2 terminou a sua execução e a tarefa 1 retomou a sua execução;
– Em t4 a tarefa 1 terminou e a tarefa 3 iniciou;
– Em t5 a tarefa 2 está pronta por isso a tarefa 3 foi suspensa;
– Em t6 a tarefa 2 terminou a sua execução e a tarefa 3 retomou a sua execução.

51
MEEC – SISTR
2017/2018
Escalonador não preemptivo

● Quando o FreeRTOS está configurado para ser não preemptivo, uma


tarefa não pode interromper outra tarefa;
● No exemplo anterior, em t2 e t5 a tarefa 2 (que é a de maior prioridade)
está pronta a correr. Neste caso, como o sistema é não preemptivo, a
tarefa precisa de esperar que a outra que está a correr termine:

Task 3
execution
Task 2
execution
Task 1
execution

t1 t2 t3 t4 t5 t6 t7 time
● O FreeRTOS pode ser configurado para ser preemptivo ou não no
ficheiro FreeRTOSConfig.h (configUSE_PREEMPTION).

52
MEEC – SISTR
2017/2018
Starvation de uma tarefa

● Starvation significa que uma tarefa fica com


“fome” de CPU porque nunca consegue correr;
● Se uma tarefa com maior prioridade não incluir
um mecanismo de bloqueio (como por exemplo
o vTaskDelay ou vTaskDelayUntil), as tarefas
de menor prioridade não vão conseguir slots de
CPU para correr.

53
MEEC – SISTR
2017/2018
Funções relativas ao escalonador

● vTaskStartScheduler() - arranca o escalonador


– Após esta função ser chamada mais nenhuma linha de código será executada a
partir daqui (esta função é normalmente chamada após a configuração do sistema,
RCC, NVIC, GPIO, etc);

● vTaskEndScheduler() - para o escalonador


– Para todas as tarefas que estão a correr. O código continua como se a função
vTaskStartScheduler() tivesse retornado (em situações normais esta função não é
usada).

● vTaskSuspendAll() - suspende a execução do escalonador


– A tarefa atual (ou seja, a que chamou esta função) é a única a ser executada até
existir uma chamada à função xTaskResumeAll (esta função só deve ser usada se
tivermos a certeza do que estamos a fazer).

● xTaskResumeAll() - retoma a execução do escalonador


– Retoma a execução do escalonador previamente suspendida pela chamada da
função vTaskSuspendAll();

54
MEEC – SISTR
2017/2018
Outras funções

● taskYIELD() - força a mudança do contexto;


● Esta função força a que o escalonador vá verificar se há
a necessidade de se fazer uma mudança de contexto.
● Ao usar esta função evitamos ter de esperar até à
próxima interrupção do SysTick para que ocorra uma
mudança de contexto .
● É normalmente usada sempre que uma função retorna a
indicação de que há a necessidade de se fazer uma
mudança de contexto (p.e. xTaskResumeFromISR);
● Esta indicação acontece sempre que uma tarefa de
mais alta prioridade acabou de ser desbloqueada por
algum motivo e está pronta a correr.

55
MEEC – SISTR
2017/2018
Macros

● taskENTER_CRITICAL
– Permite definir o início de uma zona de código onde não podem
existir mudanças de contexto.

● taskEXIT_CRITICAL
– Marca o fim da zona de código onde não podem ocorrer
mudanças de contexto.

● taskDISABLE_INTERRUPTS
– Desativa todas as interrupções.

● taskENABLE_INTERRUPTS
– Ativa todas as interrupções.
56
MEEC – SISTR
2017/2018
Tick hook

● É uma função que pode ser chamada sempre que a


interrupção do SysTick corre;
● Para usar esta funcionalidade é preciso escrever um
função com o seguinte nome:
– void vApplicationTickHook( void );

● A ativação desta funcionalidade é feita colocando a 1


o configUSE_TICK_HOOK no FreeRTOSConfig.h;
● Normalmente esta funcionalidade é usada para
temporizações. O código escrito dentro desta função
tem de ser o mais curto possível e pode, por
exemplo, consistir num decremento de uma variável.

57
MEEC – SISTR
2017/2018
Idle hook

● A tarefa idle é criada sempre que o escalonador é iniciado.


Desta forma há sempre uma tarefa pronta a correr.
● Se pretendermos correr algum código dentro desta tarefa,
basta para isso definir uma função com o seguinte nome:
– void vApplicationIdleHook( void );

● Esta função é chamada no ciclo da tarefa idle com a


prioridade tskIDLE_PRIORITY (que é a prioridade mais
baixa que existe) se a flag configUSE_IDLE_HOOK estiver
a 1 no FreeRTOSConfig.h;
● Normalmente esta função é usada para colocar o
microcontrolador num modo de poupança de energia já
que, quando corre, significa que não há nenhuma tarefa
pronta a correr.

58
MEEC – SISTR
2017/2018
FreeRTOS API

● Documentação disponível em www.freertos.org

59
MEEC – SISTR
2017/2018
Referências

● FreeRTOS documentação disponível em


http://www.freertos.org/tutorial.

60
MEEC – SISTR
2017/2018

Вам также может понравиться