Biblioteca de Soft Timers

soft timers
Este post faz parte da série 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.

Soft Timers - One-shot timer
Figura 1 – 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.

Soft Timers - One-shot timer sendo iniciado diversas vezes
Figura 2 – One-shot timer sendo iniciado diversas vezes

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.

Soft Timers - Periodic timer
Figura 3 – Periodic timer

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:

  1. identificador único, que o usuário pode usar para iniciar, parar ou desregistrar um timer;
  2. 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;
  3. valor de recarga do timer;
  4. tipo de timer: oneShot ou Periodic;
  5. parâmetro que é passado à função de callback;
  6. indica se o timer foi inciado ou não através do método start;
  7. 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

https://www.ti.com/product/TM4C123GH6PGE
https://www.ti.com/tool/sw-tm4c
https://www.ti.com/tool/dk-tm4c123g
https://github.com/rdmeneze/TTimerExample

Soft Timers

Debouncing de teclas usando Soft Timers
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Software » Biblioteca de Soft Timers

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: