A maior rede de estudos do Brasil

Grátis
256 pág.
Descobrindo o STM32

Pré-visualização | Página 43 de 47

a pilha precisa
1FreeRTOS necessita de um alocador de memória para alocar dinamicamente as estru-
turas de dados principais.
Revision: (None) ((None)) 231
CAPÍTULO 16. SISTEMAS OPERACIONAIS DE TEMPO-REAL
ser grande o suficiente para armazenar todos os dados alocados na pilha e
quaisquer registros salvos. A maioria dos threads irá precisar de 128-256
bytes.
O kernel do FreeRTOS também cria um “idle thread”, que tem a menor
prioridade e é executado sempre que nenhum outro thread pode executar. (O
“idle thread” (thread ocioso) tem uma pilha também!). O fluxo básico de um
programa multi-thread segue:
main() {
// include misc.h
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
// Initialize hardware
...
// Create tasks
xTaskCreate( ... );
...
// Start Scheduler
vTaskStartScheduler();
}
Tal como acontece com todos os programas considerados até aqui, o
código começa com a inicialização do hardware. Uma inicialização do hard-
ware que chamamos a atenção é a configuração NVIC. O FreeRTOS requer
prioridades de thread preemptiva; aqui vamos ativar o NVIC para suportar 16
prioridades (e nenhuma subprioridades). Esta é a configuração assumida pela
versão do FreeRTOS para o STM32. Uma vez que o hardware é inicializado, o
conjunto inicial de threads é criado. Enquanto FreeRTOS permite alocação di-
nâmica de threads, a RAM limitada (8K) disponível na Discovery Board exige
que nós criemos os threads inicialmente. O escalonador (scheduler) nunca re-
torna nada (há uma chamada da API para sair do scheduler, mas isso não é
particularmente útil para as nossas aplicações). Um exemplo mais completo,
com dois threads que piscam dois LEDs é ilustrado na Listagem 16.1. Observe
que os threads utilizam vTaskDelay para dormir.
232 Revision: (None) ((None))
16.1. THREADS
static void Thread1(void *arg) {
int dir = 0;
while (1) {
vTaskDelay(300/portTICK_RATE_MS);
GPIO_WriteBit(GPIOC, GPIO_Pin_9 , dir ? Bit_SET : Bit_RESET);
dir = 1 - dir;
}
}
static void Thread2(void *arg) {
int dir = 0;
while (1) {
vTaskDelay(500/portTICK_RATE_MS);
GPIO_WriteBit(GPIOC, GPIO_Pin_8 , dir ? Bit_SET : Bit_RESET);
dir = 1 - dir;
}
}
int main(void)
{
// set up interrupt priorities for FreeRTOS !!
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
// initialize hardware
init_hardware();
// Create tasks
xTaskCreate(Thread1, // Function to execute
"Thread 1", // Name
128, // Stack size
NULL, // Parameter (none)
tskIDLE_PRIORITY + 1 , // Scheduling priority
NULL // Storage for handle (none)
);
xTaskCreate(Thread2, "Thread 2", 128,
NULL, tskIDLE_PRIORITY + 1 , NULL);
// Start scheduler
vTaskStartScheduler();
// Schedule never ends
}
Listing 16.1: Exemplo FreeRTOS
Revision: (None) ((None)) 233
CAPÍTULO 16. SISTEMAS OPERACIONAIS DE TEMPO-REAL
16.2 Configuração do FreeRTOS
FreeRTOS/
Demo/CORTEX_STM32F100_Atollic/Simple_Demo_Source/
FreeRTOSConfig.h
Source/
include/
list.c
portable/
MemMang/
heap_1.c
GCC/ARM_CM3/
port.c
portmacro.h
queue.c
tasks.c
timers.c
Figura 16.3: Partes Principais da Distribuição do FreeRTOS
O código-fonte do FreeRTOS está disponível gratuitamente em http://
sourceforge.net/projects/freertos/files/. A distribuição pode ser um
pouco confusa. Os arquivos-chave que usamos são mostrados na Figura 16.3.
Ao criar uma aplicação de exemplo, será necessário ampliar seu makefile para
incluir os recursos necessários, como mostrado na Listagem 16.2.
FreeRTOS = ... path_to_FreeRTOS ...
CFLAGS += -I$(FreeRTOS)/include -DGCC_ARMCM3
...
vpath %.c $(FreeRTOS)/
vpath %.c $(FreeRTOS)/portable/MemMang
vpath %.c $(FreeRTOS)/portable/GCC/ARM_CM3
...
OBJS+= tasks.o queue.o list.o timers.o heap_1.o port.o
Listing 16.2: Build Paths para o FreeRTOS
Cada projeto FreeRTOS requer um arquivo de configuração. Ele é
usado para definir os parâmetros-chave do projeto; por exemplo, ativar ou
desativar recursos do núcleo – FreeRTOSConfig.h. Baseamos nossos projetos
no arquivo de configuração mostrado na Figura 16.3. O arquivo de configu-
234 Revision: (None) ((None))
16.3. SINCRONIZAÇÃO
ração serve a vários propósitos. Primeiro ele define parâmetros-chave – por
exemplo frequência de clock, tamanho do heap, número de prioridades, etc.
Segundo, ele define recursos a serem ativados numa compilação -– recursos
são habilitados (1) ou desabilitados (0) por definições da seguinte forma:
#define INCLUDE_xxx 0+
Exemplos das características chave incluem
#define configUSE_MUTEXES 1
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
Finalmente, o FreeRTOS também fornece a oportunidade para o código
do usuário de fazer um “hook” (“gancho”) no kernel em lugares chave:
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
Se você compilar um projeto com FreeRTOS e tem erros de linkedição
para funções não encontradas, então você deve verificar para ver se um gancho
correspondente foi definido. Depois, você tem a opção de fornecer a função
exigida ou desativar o gancho.
Exercise 16.1 RTOS – Blinking Lights
Complete o programa demo Pisca Led. Você deve desabilitar qualquer
recurso que seu projeto não precise fazendo uma copia local do arquivo de
configuração. Você achará necessário reduzir o tamanho do heap (de 7k para
5k) para seu projeto compilar.
16.3 Sincronização
Threads não podem compartilhar de modo seguro as estruturas de da-
dos e nem hardware sem ummecanismo de sincronização. Considere a seguinte
implementação de getchar, discutida no Capitulo 5:
int getchar(void){
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return USARTx->DR & 0xff;
}
Revision: (None) ((None)) 235
CAPÍTULO 16. SISTEMAS OPERACIONAIS DE TEMPO-REAL
Lembre-se que isso funciona através da leitura do registrador de status
da UART até que registrador de dados recebidos esteja “not empty” (“não
vazio”) e então lendo o registrador. Supondo que dois threads acessem a
UART – se thread 1 lê o registrador de status e encontra “not empty”, mas
em seguida é preemptado pelo thread 2 que também testa o status e lê o
registro de dados, e então quando o thread 1 é retomado, seu conhecimento
do registrador de status será incorreto e pode ler lixo do registrador de dados.
A idéia-chave para fazer isso funcionar é encontrar um método para
garantir o acesso exclusivo pelo thread 1 ao registrador de dados da UART.
Uma abordagem é a de impedir a preempção desabilitando as interrupções,
no entanto, considere o que aconteceria se nenhum caractere chegar ou sim-
plesmente chegar depois de um atraso substancial. O que é necessário é um
mecanismo para bloquear apenas os threads que estão disputando o acesso ao
hardware de recepção da UART. A solução é a utilização de um semáforo.
xSemaphoreHandle uartRcvMutex;
uart_init(...){
...
uartRcvMutex = xSemaphoreCreateMutex();
}
int getchar(void){
int c;
if (xSemaphoreTake(uartRcvMutex , portMAX_DELAY) == pdTRUE)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
c = USARTx->DR & 0xff;
xSemaphoreGive(uartRcvMutex);
}
return c;
}
Um semáforo é uma primitiva de sincronização padrão (na verdade, a
primeira descrita) para a estruturação do código de sistemas operacionais. A
ideia é proporcionar a um thread uma interface segura para gerenciar recursos
compartilhados. A um thread que solicita um recurso ou é concedido ou
bloqueado pelo escalonador (scheduler) até que o recurso se torne disponível.
Um thread liberando um recurso pode, como efeito colateral, desbloquear um
thread em espera.
FreeRTOS suporta três tipos de semáforos – semáforos binários, exclu-
sões mútuas (Mutex) e semáforos contáveis. Mutex e semáforos binários são
semelhantes, mas se comportam de forma diferente em relação a prioridade
236 Revision:

Crie agora seu perfil grátis para visualizar sem restrições.