You are on page 1of 8

EA871 – Experimento 11 – TIMERS (I): SYSTICK E WATCHDOG

Rose Landeira de Macedo RA 137550


Introdução e objetivos
Neste experimento foi utilizado o software “CodeWarrior 10.4” para executar dois programas
determinados pelo professor. O objetivo do primeiro programa é exibir um relógio no visor de LCD que indica
as horas, os minutos e os segundos (a partir de uma entrada de horário inicial). Já o segundo programa visa
piscar duas cores de LEDs enquanto o programa segue na sua execução normal, e um LED vermelho quando
um tempo limite de processamento é esgotado (ou seja, ocorre algum travamento na execução do programa).
O objetivo real do experimento é familiarizar os alunos com um recurso muito importante nos
sistemas digitais: os timers. São introduzidos dois tipos de timers: o timer do sistema (SysTick) e o watchdog.
Além de compreender os conceitos, os alunos também conseguirão ver exemplos de como esses recursos
podem ser aplicados de maneira útil.

Descrição do experimento e resultados obtidos
Antes do início do experimento, o Capítulo 3.4.10 do manual “KL25 Sub-Family Reference Manual” e a
Seção B3.3 do manual “ARM v6-M Architecture Reference Manual” foram lidos. Eles explicam os detalhes de
funcionamento dos timers existentes no programa: o tick do sistema (SysTick) e o watchdog.
Inicialmente foi criado um projeto básico em C no software “CodeWarrior 10.4”. O seu arquivo “main.c”
foi editado de acordo com as instruções e o código fornecidos pelo professor (anexo 1). As funções InitGPIO(),
del3ms(), pulso(), enviaLCD(), RS(), InitLCD(), ConvNpraDec(), mandaString() e limpaLCD(), presentes
no programa, já são conhecidas e foram utilizadas e exploradas em experimentos anteriores. Entretanto, duas
novas funções foram introduzidas neste experimento.
A primeira delas é a InitSysTick():

Essa função é responsável por inicializar o timer tick do sistema (SysTick). Esse timer recebe um valor
determinado pelo programador, inicia uma contagem decrescente com ele de acordo com a frequência de
clock desejada e, quando a contagem se encerra, uma interrupção é chamada. Após o cumprimento do evento,
a contagem é reiniciada a partir de um determinado valor de reload. Dessa maneira, conclui-se que ele opera
gerando uma interrupção periodicamente, permitindo que o sistema mantenha um contador de tempo
atualizado. Para que ele seja corretamente configurado, quatro registradores recebem valores que
determinam como esse recurso deverá operar.
A primeira linha, SYST_RVR = 20480, determina que o valor de reload do SysTick vale 20480. Ou seja,
quando o contador atingir o número 0, a contagem se reiniciará a partir do valor determinado como reload,
20480.
Já a segunda linha, SYST_CVR = 0, é responsável por setar o valor do contador em 0. Dessa maneira, a
instrução anterior automaticamente torna-se válida e, em seguida, o valor 20480 é atribuído ao contador do
sistema.
Por fim, a terceira linha, SYST_CSR = 0x00000007, é responsável por determinar algumas flags que
atribuem características ao SysTick:

 A COUNTFLAG é uma flag que determina se o contador possui valor igual ou diferente de 0.
Nessa instrução, ela recebe o número 0, indicando que o contador possui um valor diferente de
0.
 A CLKSOURCE é uma flag responsável por determinar se o clock utilizado para realizar a
contagem será o do processador ou algum outro referencial externo. Como ela possui valor
lógico 1, o clock que será utilizado pelo SysTick neste caso é o clock do processador.
 A TICKINT é uma flag responsável por determinar se as interrupções estão ativadas ou não.
Como ela também possui valor lógico 1, as interrupções estão ativadas e ocorrerão quando o
contador atingir o fim da contagem
 A ENABLE é uma flag que determina se a contagem do contador está ativa ou não. Como
Recebendo o valor 1, é permitido que o contador inicie sua contagem.
Considerando as observações realizadas acima à respeito das três linhas da função isoladamente, é
possível sintetizar a sua finalidade. A função InitSysTick() determina que o clock do programa (de
20.48MHz) será utilizado para a contagem, que inicia no valor 20480. Desse modo, o contador inicia com o
valor 20480 e, a cada 1/20.48 ms, o seu valor decresce em uma unidade. Realizando os cálculos, conclui-se
que o contador irá chegar ao valor 0 à cada 1ms. Sabendo que, quando o valor 0 é atingido, o SysTick inicia
uma interrupção, a cada 1ms a função SysTick_Handler() – responsável por executar a interrupção – será
chamada.
A função SysTick_Handler() foi então analisada:

Primeiramente ela contém a declaração de uma variável do tipo static unsigned int com nome ms. O
tipo de declaração “static” indica que o valor da variável é mantido após a execução da rotina; ou seja, o valor
que ms conterá será sempre o mesmo que ela possuía na última execução da função SysTick_Handler().
Posteriormente, ocorre um incremento da variável ms. Caso ela atinja o valor 1000, um comando condicional
é também executado. Nele, a variável ms recebe o valor 0 e é atribuído o número 1 à uma variável global fl.
Interpretando as instruções acima, foi possível concluir o real objetivo da interrupção. Como a cada
1ms a função SysTick_Handler() é chamada, a variável ms será incrementada 1000 vezes antes do comando
condicional ser iniciado. Ou seja, se passarão 1000*1 ms a partir da sua primeira execução – o que resulta em
1s. Quando as instruções internas ao if forem executadas, a variável ms receberá o valor 0 (assim, a contagem
de 0 à 1000 será reiniciada por mais 1 segundo) e a variável fl, o valor 1. Portanto, essa interrupção realiza a
contagem de 1s e, quando esse é completado, atribui o valor 1 à variável fl, utilizada na função main.

Na função main, primeiramente são declaradas as variáveis s, m e h e é atribuído o valor 16 à h, 30 à m
e 0 à s. A variável fl também recebe o valor 0. Posteriormente, são realizadas as inicializações das GPIOs, do
visor LCD e do SysTick.
É criado um loop for, que enlaça todo o restante da função. Internamente à ele, é checado se a variável
fl – a mesma que foi analisada a função SysTick_Handler() - possui valor 1. Caso positivo (ou seja, foi
completado o tempo de 1 segundo), fl recebe o valor 0 – e, como ela é uma variável global, ela também
possuirá esse valor na função de interrupção, auxiliando o reinício da contagem – e a variável s é
incrementada. Um novo comando condicional, interno ao primeiro, é aplicado: caso a variável s atinja o valor
60 (ou seja, passaram-se 60 segundos), ela recebe o valor 0 e a variável m é incrementada. Internamente à
esse if, mais uma condição deve ser analisada: caso a variável m obtenha o valor 60, ela é zerada e a variável h
sofre um incremento. Por fim, mais uma condição é realizada internamente à essa: caso h seja superior à 23,
ela recebe o valor 0.
Ainda dentro do loop for, mas externamente aos comandos condicionais, os valores de h, m e s são
enviados para o visor LCD, separados por dois pontos (‘:’).
Analisando a essa função, foi possível concluir como todo o programa opera e que ele desempenha a
função de exibir um relógio no visor LCD. Primeiramente, através do valor da frequência do clock do
processador, é possível determinar um certo valor para que o SysTick realize sua contagem a cada 1ms. Assim,
a cada 1ms uma interrupção é atendida. Após o atendimento de 1000 interrupções – ou seja, o tempo de 1s –,
uma variável “flag” muda de valor, informando à função main que se passou 1 segundo. O valor dos segundos
é atualizado e, caso ele seja maior que 59, os minutos são atualizados e o valor dos segundos passa a ser 0,
como em qualquer relógio. De maneira análoga, o programa também checa se o valor dos minutos é maior que
59 – atualizando o valor das horas e zerando o valor dos minutos – e se o valor das horas é maior que 23 –
reiniciando assim a contagem das horas. Por fim, os valores são enviados ao visor LCD (que, assim, é
atualizado a cada segundo).
Como o valor de h foi inicializado em 16, o de m, em 30, e o de s, em 0, o visor exibiu, em um primeiro
momento, o valor de 16h30 (16:30:00). Logo após a contagem foi iniciada e o relógio desempenhou sua
função corretamente.

Após a análise completa do programa acima, foi criado um novo projeto básico em C no software
“CodeWarrior 10.4”. O seu arquivo “main.c” foi editado de acordo com as instruções e o código fornecidos pelo
professor (anexo 2):



Em seguida, o arquivo kinetis_sysinit.c foi aberto e a constante “KINETIS_WDOG_DISABLED_CTRL” foi
trocada pelo valor 0x0000000C:

Assim, o watchdog foi ativado e recebeu o valor 0x0C.
O watchdog é um timer do sistema que realiza uma contagem crescente até um valor determinado pelo
programador. Caso esse valor máximo seja atingido, ocorre um overflow: o valor do watchdog retorna à 0 e o
programa é reiniciado. Esse timer tem como principal finalidade evitar que o programa continue sendo
executado por grandes períodos de tempo durante um travamento. Forçando um reset do sistema, ele impede
que tempo e processamento sejam desperdiçados.
Após consultar o manual “KL25 Sub-Family Reference Manual”, foi possível obter mais detalhes sobre o
que essa mudança altera na execução do programa e o seu watchdog:

Através dela, o watchdog é habilitado em seu modo normal (COPW recebe o bit 0), configurado para
utilizar o clock interno do sistema (COPCLKS recebe o bit 0) como referência e ter como quantidade limite 2
10

ciclos de clock (COPT recebe o valor 11).
Assim, o watchdog realizará a contagem de 0 até 1024. Caso ela seja atingida, o sistema reiniciará.
Sabendo que o clock interno opera em uma frequência de 1kHz, é possível estimar que o watchdog aguarda o
tempo limite de, aproximadamente, 1 segundo de espera antes de reiniciar o sistema.
Após a realização dessa análise, o projeto foi compilado e debugado. O programa foi então executado
fora do modo debug – com o auxílio do botão Disconnect.
Inicialmente, a primeira observação foi que o LED da placa KL25Z piscava alternadamente nas cores
verde e azul. Para explorar o funcionamento do programa, um dos botões da placa auxiliar foi pressionado.
Quando o mesmo foi apertado por um tempo menor que 1 segundo, a cor do LED automaticamente foi
alterada e a sequência prosseguiu. Entretanto, quando a botoeira ficou pressionada por mais que 1 segundo, o
LED tornou-se da cor vermelha. Para compreender melhor tais observações, o código do programa foi
cuidadosamente analisado.
Primeiramente, dois valores são atribuídos a duas variáveis, através do comando define. São eles
define DELINIC 1500000 e define DELPISCA 650000. Depois, a função InitGPIO() inicializa e configura as
GPIOs que serão utilizadas.
A função ClrCOP() foi então definida. Basicamente, ela atribui os valores 0x55 e, depois, 0xAA ao
registrador SIM_SRVCOP. Consultando o manual “KL25 Sub-Family Reference Manual”, percebe-se que,
quando esses dois valores são atribuídos a esse registrador, em sequência, o watchdog reinicia sua contagem.
Posteriormente, a função pisca() é determinada. Ela executa duas ações: inverte o nível lógico do pino
19 da porta B, correspondente ao LED verde, e o do pino 1 da porta D, correspondente ao LED azul. Dessa
maneira, ela alterna a cor do LED que está sendo exibido: o que estava aceso, apaga, e o que estava apagado,
acende.
Em seguida, a função delayPisca() é apresentada. Primeiramente é declarada uma variável inteira
sem sinal tempo. Depois, é construído um loop for que possui como parâmetros a variável tempo iniciando com
o valor definido para DELPISCA (nesse caso, 650000) e sendo decrementada até atingir o valor zero. A cada
execução do loop, é realizado um comando condicional if, que testa se algum dos pinos 4, 5 ou 12 da porta A
possuem valor lógico diferente de 1. Caso afirmativo (ou seja, algum dos pinos possui valor 0), a variável
tempo recebe o valor 1000. A finalidade real desse comando if é testar se algum dos botões está pressionado.
Em caso afirmativo, a variável tempo recebe um novo valor e, assim, o loop continua e a execução do programa
fica presa ali até que todos os botões estejam soltos.
Por fim, a função delayInic() é definida. Novamente, uma variável inteira sem sinal tempo é declarada
e um loop for é construído. Os parâmetros do loop são a variável tempo iniciando com o valor definido para
DELINIC (nesse caso, 1500000) e sendo decrementada até atingir o valor zero. A cada execução do loop, a
função ClrCOP() é chamada. Dessa maneira, a função garante que, mesmo que um tempo de delay seja
executado nela, o watchdog continue sendo alimentado e não dê um reset no sistema.
Assim, torna-se mais fácil compreender, por último, o que a função main desempenha. Após inicializar
os GPIOs e configurar corretamente os LEDs, é implementado um loop for que contém - nessa ordem - as
chamadas de função para ClrCOP(), pisca() e delayPisca(). Dessa maneira, o watchdog é reinicializado à cada
entrada no loop; em seguida, os LEDs verde e azul se alternam- o que estiver aceso, apaga, e o que estiver
apagado, acende - e a função delayPisca() checa se algum botão ainda está pressionado. Em caso negativo, a
execução prossegue normalmente e, após um intervalo de tempo (definido por DELPISCA), os LEDs verde e
azul se alternam novamente. Caso algum botão ainda esteja pressionado, a execução do programa fica presa
no loop interno à função delayPisca(), fazendo com que o watchdog reinicie o sistema quando esse tempo de
execução ultrapasse 1s e, assim, o LED torna-se vermelho (início da função main).
Para explorar o programa e as utilizações do watchdog, o valor de DELPISCA no define foi alterado
para um valor maior: O programa foi então compilado e executado.
O resultado obtido foi que o LED verde passou a piscar juntamente com o LED vermelho. Além disso,
também foi observado que o tempo que cada uma das cores do LED permanece acesa era maior do que no
programa anterior.
Considerando as observações realizadas e analisando a modificação realizada no programa, é possível
concluir que o tempo de delay determinado foi grande demais. Assim, antes mesmo da alternância entre o LED
verde e o LED azul e uma execução completa do programa, o watchdog atinge o seu valor máximo e reinicia o
sistema, exibindo o LED vermelho. Portanto, é possível inferir que, para intervalos de delay muito grandes, é
necessário zerar o valor do watchdog (através de uma chamada de função ClrCOP() com periodicidade menor
que 1 segundo), evitando assim um reset do sistema em um momento indesejado.
O valor de DELPISCA foi então retornado para o original ( ). Com o auxílio
de “//”, a instrução tempo = 10000 foi desativada e a SIM_SRVCOP = 0x00, ativada:


Após compilar e executar o programa, foi observado que os LEDs verde e azul piscam normalmente.
Entretanto, quando qualquer botão é pressionado, o programa reinicia – ou seja, retorna para o LED verde.
Tal observação pode ser explicada pelo fato de que, segundo o manual “KL25 Sub-Family Reference
Manual”, quando qualquer valor diferente de 0x55 e 0xAA é atribuído ao registrador SIM_SRVCOP, o sistema
automaticamente sofre um reset. Portanto, independentemente do valor que for determinado a esse
registrador – que exceto os dois supracitados -, o programa reiniciará da mesma maneira.
Por fim, foi realizada uma última alteração no programa: após ser retornado para o código original, a
instrução ClrCOP() – que estava determinada na forma de comentário na função delayPisca() – foi ativada. O
programa foi novamente compilado e executado.
O resultado observado foi que os LEDs verde e azul piscam, novamente, de maneira normal. Além
disso, independentemente de qual botão fosse pressionado, os LEDs continuavam se alternando com a mesma
frequência, sem nenhuma mudança.
Tal modificação no programa só é possível já que, através da chamada da função ClrCOP() dentro da
função delayPisca(), o watchdog é constantemente limpo e, assim, não atinge seu valor limite. Dessa maneira,
o programa fica incapaz de sofrer um reset. Assim, fica claro que reiniciar o watchdog internamente à um loop
faz com que o sistema nunca seja reiniciado: essa observação pode ser muito útil em casos de programas mais
complexos, que possuem um grande número de interações e no qual um reset poderia ser algo indesejado.

Conclusão
Através desse experimento, foi possível compreender melhor o que é um timer, seus recursos e como
ele opera. Por meio de dois programas, dois diferentes tipos de timers foram introduzidos: o SysTick e o
watchdog.
O primeiro programa - que realiza uma contagem decrescente e, ao seu fim, realiza uma interrupção –
foi aplicado na construção de um relógio. A partir de um horário inicial configurado no programa, o visor LCD
exibia horas, minutos e segundos e, claramente, era atualizado a cada segundo. Isso foi possível graças ao
SysTick, que criava uma interrupção à cada 1ms – com uso do clock do processador – e era capaz de “marcar” o
tempo transcorrido.
Já o segundo programa consistia em piscar duas cores de LEDs (verde e azul) enquanto o programa
estivesse sendo executado corretamente. Quando, por algum motivo, a execução do programa falhava e o
watchdog precisava ser acionado, o LED tornava-se vermelho. Esse “erro” ocorria devido à alterações
realizadas no programa inicial, como intervalos muito grandes de delay nas funções internas ao programa.
Dessa maneira, foi possível compreender como os timers funcionam, como são interpretados e os
recursos existem para operá-los de modo que realizem um determinado objetivo. Também foram analisados
os erros que podem ocorrer quando um timer não é corretamente programado, além dos cuidados necessários
ao utilizar esse recurso.