Em muitos projetos que desenvolvi, sempre tive a necessidade de usar uma biblioteca de timers que possuíssem a capacidade de executar tarefas, sejam elas periódicas ou de uma única chamada.
Pensando nisso eu acabei implementando uma biblioteca de soft timers bem simples que permite ao usuário o registro de funções de callback, as quais são chamadas em um contexto de interrupção.
A biblioteca em questão foi desenvolvida para microcontroladores Texas da família TM4C123G e pode ser utilizada sem muitos problemas para outros microcontroladores. Ela utiliza apenas um timer para essa operação e pode ser utilizada em conjunto com RTOSs ou em sua aplicação bare-metal. Foi preparada para ser compilada com IAR e será fornecida no final do artigo.
Introdução a Soft Timers
Os soft timers são contadores decrescentes que executam uma ação quando chega a zero. O usuário fornece uma ação através de uma função de callback, que é uma função declarada pelo usuário e chamada quando o timer expira. Esta função de callback deve ser utilizada para operações bem rápidas, pois ela está sendo executada no contexto de uma interrupção. É muito importante que não se faça operações que possam bloquear o processamento pois, com isso, a execução de outras atividades será prejudicada.
Soft timers são úteis em protocolos de comunicação (timers de retransmissão, por exemplo) e também podem ser utilizados para polling de dispositivos de E/S em intervalos regulares.
A granularidade do timer é realizada na sua inicialização e esta deve ser ajustada para cada projeto: quanto menor a granularidade, maior é a freqüência que as ISR associadas ao Timer chamam as funções de callback e com isso poderemos prejudicar outras operações que o microcontrolador desempenha. A seguir apresento os diferentes tipos de timers implementados nesta biblioteca.
One-Shot Soft Timers
Como o nome diz, o one-shot timer irá contar de seu valor inicial, chamar a função de callback quando a contagem alcançar zero e parar. A figura 1 descreve a operação de um one-shot timer.
Como mostrado na figura 2, one-shot soft timers podem ser regatilhados chamando-se a função Start. Esse comportamento pode ser útil para implementar watchdogs ou coisas semelhantes.
Periodic Soft Timers
Periodic soft timers, como o nome diz, irá contar do valor inicial, chamar a função de callback quando o seu contador alcançar o valor zero e recarregar o contador com o valor inicial, entrando em um ciclo contínuo até que a função stop seja chamada.
Implementação de Soft Timers
Internamente, um timer é um objeto definido pelo tipo interno de dado STTimer, como apresentado na listagem abaixo:
struct STTimer
{
DWORD dwHandle; //(1)
long long iCount; //(2)
long long iReloadValue; //(3)
TimerType type; //(4)
void * lpParam; //(5)
BYTE bStarted; //(6)
callbacktimer_func callback_func; //(7)
};
Descrição de cada membro da estrutura:
- identificador único, que o usuário pode usar para iniciar, parar ou desregistrar um timer;
- contador de ticks de interrupção do timer que, quando alcança o valor zero, permite ao timer executar a função de callback, dada por callback_func;
- valor de recarga do timer;
- tipo de timer: oneShot ou Periodic;
- parâmetro que é passado à função de callback;
- indica se o timer foi inciado ou não através do método start;
- ponteiro para a função de callback.
É instanciado um array de estruturas STTimer, sendo que a quantidade de timers que pode ser implementada em software depende principalmente do espaço de memória que o usuário tem à disposição. No projeto pode-se configurar, através de macros, qual é a quantidade de timers que se quer utilizar.
Timer_ISR_Handler
O motor que impulsiona os soft timers é implementado em uma rotina de interrupção periódica que, para a nossa aplicação com a família TM4C123G da Texas, é obtida através do uso do periférico Timer, conforme datasheet do microcontrolador TM4C123GH6PGE e da biblioteca TivaWare Peripheral Library.
A cada vez que a rotina de interrupção Timer_ISR_Handler é chamada, o campo .iCount de todos timers que estão iniciados, indicados pelo campo .bStarted de STTimer, é decrementado de uma unidade. Se o valor de iCountfor zero, a função de callback é executada. Atente que somente uma função de callback é executada por execução de Timer_ISR_Handler, o que pode provocar um jitter na frequência de chamada da função de callback. Foi implementado dessa forma para que a execução das funções de callback não prejudique a execução das outras funcionalidades do microcontrolador.
Para mais detalhes a respeito da implementação, sugiro que olhe o código fonte.
TTimer – Application Programming Interface
A interface para a biblioteca TTimer é bem simples e intuitiva. Temos basicamente uma função para configurar o tempo de interrupção do timer e registro de uma função de callback, uma para iniciar e outra parar o timer.
A listagem abaixo mostra a API da biblioteca TTimer:
#ifndef __TTIMER_H__
#define __TTIMER_H__
#include "defs.h"
#define TTIMER_1MS_INTERVAL (1000U)
#define TTIMER_1SEC_INTERVAL (1000*TTIMER_1MS_INTERVAL)
#define TTIMER_1MIN_INTERVAL (60*TTIMER_1SEC_INTERVAL)
typedef uint32_t (*callbacktimer_func)( void* );
typedef enum
{
TimerOneShot = 0,
TimerPeriodic
} TimerType;
/**
* \brief configure the time out of the timer
* \param[in] dwTimeMicro the timer timeout value in us
*/
void TTimerCfgTimeOut( DWORD dwTimeMicro );
/**
* \brief get the time base value
* \return time base value
*/
DWORD TTimerGetTimeBase( void );
/**
* \brief register a function callback timer
* \param[in] delay delay to call the callback funtion. Delay units us
* \param[in] type function timer type
* \param[in] callback_func function timer callback
* \param[in] lpParam function timer param
* \param[out] cbHandle function timer handle
* \return callback function creation success
*/
DWORD TTimerRegisterCallBack( DWORD dwDelay, TimerType type, callbacktimer_func callback_func, void* lpParam, DWORD* cbHandle );
/**
* \brief unregister a callback timer
* \param dwHandle handle of the timer
* \return success or fail
*/
DWORD TTimerUnregisterCallBack( DWORD dwHandle );
/**
* \brief start a software timer
* \param[in] dwHandle handle of the timer to start
* \return success or fail
*/
DWORD TTimerStart( DWORD dwHandle );
/**
* \brief stop a software timer
* \param[in] dwHandle handle of the timer to stop
* \return success or fail
*/
DWORD TTimerStop( DWORD dwHandle );
/**
* \brief restart the timer
* \param[in] dwHandle handle of the timer to stop
* \return success or fail
*/
DWORD TTimerRestart( DWORD dwHandle );
#endif
Exemplo de uso
Implementamos um caso simples, ligando e desligando o LED UserLed presente na placa DK-TM4C123G. O código fonte foi escrito com o IAR 6.50.
#include "ttimer.h"
#include <driverlib/sysctl.h>
#include <driverlib/gpio.h>
#include <inc/hw_memmap.h>
DWORD testTask( void* lpParam );
volatile unsigned long g_ulSystemClock;
void main()
{
//! handler para o timer
DWORD dwTimerHandle;
//! parâmetro a ser passada para a função de callback
DWORD dwTimerParam = 1000;
//! configura o clock do processador
SysCtlClockSet( SYSCTL_SYSDIV_3 |
SYSCTL_USE_PLL |
SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ );
g_ulSystemClock = SysCtlClockGet();
//! específico para os dispositivos TIVA: habilita e configura o periférico
SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOG );
GPIOPinTypeGPIOOutput( GPIO_PORTG_BASE, GPIO_PIN_2 );
//! inicializa o timer para gerar interrupção a cada 500us
TTimerCfgTimeOut( 500 );
//! registra a função de callback testTask para ser executada a cada 250 ms
TTimerRegisterCallBack( 250*TTIMER_1MS_INTERVAL ,
TimerPeriodic ,
testTask ,
&dwTimerParam ,
&dwTimerHandle );
//! inicia o TTimer dado por dwTimerHandle
TTimerStart( dwTimerHandle );
for( ;; );
}
/******************************************************************************/
DWORD testTask( void* lpParam )
{
DWORD* pVal = (DWORD*)lpParam;
static DWORD dwCount = 0;
if( dwCount & 1 )
{
GPIOPinWrite( GPIO_PORTG_BASE, GPIO_PIN_2, GPIO_PIN_2 );
}
else
{
GPIOPinWrite( GPIO_PORTG_BASE, GPIO_PIN_2, ~GPIO_PIN_2 );
}
dwCount++;
*pVal = dwCount;
return 0;
}
/******************************************************************************/
Veja o projeto dessa biblioteca de soft timers no GitHub.
Referências












