ÍNDICE DE CONTEÚDO
- Biblioteca de Soft Timers
- Debouncing de teclas usando Soft Timers
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:
1 2 3 4 5 6 7 8 9 10 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#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
[…] Biblioteca de Soft Timers […]