Olá caro leitor. Continuando com a série de artigos “Bibliotecas de Software para a Freedom Board KL25Z“, neste artigo será abortado o PIT – Periodic Interrupt Timer, apresentada a biblioteca de software para a sua utilização e exemplo de aplicação.
As bibliotecas aqui apresentadas nesta série de artigos foram desenvolvidas utilizando o Kinetis Design Studio IDE. As bibliotecas são facilmente portáveis para as demais Freedom Board.
O microcontrolador presente na Freedom Board KL25Z é o MKL25Z128VLK4. O PIT (Periodic Interrupt Timer) é um dos recursos de Timer que esse microcontrolador possui. Além do PIT, ele conta também com TPM (Timer/PWM Module) abordado no último artigo sobre PWM, LPTMR (Low-Power Timer) e RTC (Real Time Clock).
O periférico PIT possui dois canais (0 e 1), conta com contador de 32 bits. As características que mais de destacam neste módulo são: capacidade de temporizar e gerar disparo para o DMA, capacidade de temporizar e gerar interrupções e temporizador independente para cada canal.
Demonstrarei a seguir a biblioteca de software com as configurações mínimas para trabalhar com PIT, apresentando funções de inicialização e funções de manipulação de um Timer com o PIT.
Inicializando o PIT
Configurando o Clock do Periférico
O primeiro item a ser configurado para inicializar o PIT é habilitar o Clock para o periférico. Essa operação é feita utilizando o registrador System Clock Gating Control Register 6 (SIM_SCGC6). Para habilitar o Clock deve-se utilizar a macro SIM_SCGC6_PIT_MASK.
Nas figuras abaixo temos a representação dos bits do registrador System Clock Gating Control Register 6 (SIM_SCGC6).
Figura 1 – Detalhes do Registrador System Clock Gating Control Register 6 (SIM_SCGC6)
Configurando os parâmetros do PIT
O primeiro parâmetro a ser configurado é o registrador PIT Module Control Register (PIT_MCR). Esse registrador é responsável em ativar o temporizador e modo de depuração.
É através do Bit MDIS (Module Disable – (PIT section)) que é ativado o temporizador. Para manipular este Bit deve-se utilizar a macro PIT_MCR_MDIS(x), onde “x” é valor do bit (0 ou 1).
O bit FRZ (Freeze) é responsável por ativar o módulo de depuração. Para atuar sobre esse bit, deve-se utilizar a macro PIT_MCR_FRZ(x), onde “x” é valor do bit (0 ou 1).
As imagens abaixo detalham o registrador PIT Module Control Register (PIT_MCR).


Figura 2 – Detalhes Registrador PIT Module Control Register (PIT_MCR).
O segundo parâmetro a ser configurado é o contador. Como já dito anteriormente, o PIT possui temporizador de 32 bits. O registrador Timer Load Value Register (PIT_LDVALn) é responsável por receber o valor do temporizador. Para configurar deve-se usar a macro PIT_LDVAL_REG(base,index), onde a “base” é o ponteiro do periférico (PIT) e “index” é o numero do canal (0 ou 1).
A seguir temos uma figura que ilustra o registrador Timer Load Value Register (PIT_LDVALn).
Figura 3 – Detalhes do Registrador Timer Load Value Register (PIT_LDVALn).
O registrador Timer Control Register (PIT_TCTRLn) possui três Bits a serem configurados, são eles: o Bit CHN (Chain Mode), Bit TIE (Timer Interrupt Enable) e Bit TEN (Timer Enable).
Para manipular o registrador, deve-se utilizar a macro PIT_TCTRL_REG(base,index), onde a “base” é o ponteiro do periférico (PIT) e “index” é número do canal (0 ou 1).
Para configurar o Bit CHN deve-se usar a macro PIT_TCTRL_CHN(x), onde “x” é o valor do Bit (0 ou 1).
A macro PIT_TCTRL_TIE(x), onde “x” é o valor do Bit (0 ou 1), é utilizada para configurar o Bit TIE. Para manipular o Bit TEN devesse utilizar a macro PIT_TCTRL_TEN(x), onde “x” é o valor do Bit (0 ou 1).
Nas figuras abaixo são apresentados detalhes do registrador Timer Control Register (PIT_TCTRLn).

Figura 4 – Detalhes do Registrador Timer Control Register (PIT_TCTRLn)
O último a ser configurado na inicialização do PIT é habilitar a interrupção, para essa tarefa deve utilizar a função:
// Enable External Interrupt void NVIC_EnableIRQ(IRQn_Type IRQn)
Com o parâmetro “PIT_IRQn”, que é numero da interrupção do PIT no vetor de interrupção.
Inicializando Contagem e Encerando Contagem no PIT
Para iniciar e encerar a contagem com o periférico Periodic Interrupt Timer, é feita através do registrador Timer Control Register (PIT_TCTRLn), manipulando o Bit TEN (Timer Enable), onde o Bit com o valor em 1 inicia a contagem e com valor em 0 para a contagem.
Rotina de Interrupção
É através do registrador Timer Flag Register (PIT_TFLGn) que ocorre a notificação de interrupção do PIT. A macro utilizada para realizar a consulta a Flag da interrupção PIT_TFLG_REG(base,index), onde “base” é ponteiro do periférico (PIT) e “index” é o numero do canal (0 ou 1). Para limpar a Flag devesse utilizar a macro PIT_TFLG_TIF_MASK.
A seguir temos imagem que ilustra com maior detalhes o registrador Timer Flag Register (PIT_TFLGn).

Figura 5 – Detalhes do Registrador Timer Flag Register (PIT_TFLGn)
Funções para Timer
Na biblioteca de software que estou apresentando é para a utilização do Periodic Interrupt Timer. Optei em apresentar duas maneiras de trabalhar com o Timer, a primeira é utilizando uma Flag para sinalizar o término da contagem do PIT. A segunda é um contador, que retorna o número de interrupção do PIT.
Para a rotina com a Flag são apresentadas duas funções:
bool pit_GetFlag_Isr(void)
que é através dessa função que é feita a consulta a Flag. A segunda função é limpar a Flag:
void pit_ClearFlag_Isr(void)
A rotina de contador de interrupções também são apresentadas nas duas funções:
uint64_t pit_GetCounter_Isr(void)
que retorna o número de interrupções. A segunda função é responsável por zerar a variável que armazena o número de interrupções ocorridas:
void pit_ClearCounter_Isr(void)
A seguir é apresentado o código fonte da biblioteca desenvolvida para o periférico.
/* * pit.h * * Created on: 18/04/2017 * Author: Evandro */ #ifndef SOURCES_PIT_H_ #define SOURCES_PIT_H_ #include "externs.h" //#include "stdbool.h" #define PIT_CH_0 0 #define PIT_CH_1 1 #define FLAG_TPM 1 //#define COUNTER_TPM 2 #define NOFLAG_TPM 0 #define MODEISRTPM (NOFLAG_TPM) #define OTHER_TPM (DUAL_TPM) #define COUNTER_TPM 1 #define DUAL_TPM 0 //void init_pit(uint32_t value); bool pit_Init(uint32_t value,bool ch); void pit_Start(bool ch); void pit_Stop(bool ch); #if (MODEISRTPM == FLAG_TPM) bool pit_GetFlag_Isr(bool ch); void pit_ClearFlag_Isr(bool ch); #else //if (MODEISRTPM == COUNTER_TPM) #if (OTHER_TPM == COUNTER_TPM) void pit_ClearCounter_Isr(bool ch); uint64_t pit_GetCounter_Isr(bool ch); #else // Flag - channel 0 bool pit_GetFlag_Isr(void); void pit_ClearFlag_Isr(void); // counter - channel 1 void pit_ClearCounter_Isr(void); uint64_t pit_GetCounter_Isr(void); #endif /*#else //if (MODEISRTPM == DUAL_TPM) // Dual mode bool pit_GetFlag_Isr(bool ch); void pit_ClearFlag_Isr(bool ch); void pit_ClearCounter_Isr(bool ch); uint64_t pit_GetCounter_Isr(bool ch);*/ #endif #endif /* SOURCES_PIT_H_ */
/*
* pit.c
*
* Created on: 18/04/2017
* Author: Evandro
*/
#include "pit.h"
/*****************************************************************************************
* Global Variable
*****************************************************************************************/
#if (MODEISRTPM == FLAG_TPM)
static bool pitIsrFlag[2] = {false};
#else // if (MODEISRTPM == COUNTER_TPM)
#if (OTHER_TPM == COUNTER_TPM)
static uint64_t tpm_i[2] = {0};
#else
static bool pitIsrFlag = false;
static uint64_t tpm_i = 0;
#endif
#endif
/*****************************************************************************************
*
*****************************************************************************************/
bool pit_Init(uint32_t value,bool ch)
{
// Verifica parametros
if((ch < 2) && (value < 0x100000000))
{
// Habilita clock para o PIT
SIM_SCGC6 |= SIM_SCGC6_PIT_MASK;
// Habilita PIT Module Control Register (PIT_MCR)
PIT_MCR = 0;
// Configura contador
PIT_LDVAL_REG(PIT,ch) = value;
// Enable interrupt and enable timer
//PIT_TCTRL0 |= PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK;
PIT_TCTRL_REG(PIT,ch) |= PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK;
// Enable External Interrupt
NVIC_EnableIRQ(PIT_IRQn);
return true;
}
else
{
return false;
}
}
/*****************************************************************************************
*
*****************************************************************************************/
void pit_Start(bool ch)
{
PIT_TCTRL_REG(PIT,ch) |= PIT_TCTRL_TEN_MASK;
}
/*****************************************************************************************
*
*****************************************************************************************/
void pit_Stop(bool ch)
{
PIT_TCTRL_REG(PIT,ch) &= ~PIT_TCTRL_TEN_MASK;
}
/*****************************************************************************************
*
*****************************************************************************************/
#if (MODEISRTPM == FLAG_TPM)
bool pit_GetFlag_Isr(bool ch)
{
return pitIsrFlag[ch];
}
/********************************************************************************
*
*********************************************************************************/
void pit_ClearFlag_Isr(bool ch)
{
pitIsrFlag[ch] = false;
}
#else //if (MODEISRTPM == COUNTER_TPM)
#if (OTHER_TPM == COUNTER_TPM)
void pit_ClearCounter_Isr(bool ch)
{
tpm_i[ch] = 0;
}
/******************************************************************************
*
*******************************************************************************/
uint64_t pit_GetCounter_Isr(bool ch)
{
return tpm_i[ch];
}
#else
// Flag - channel 0
bool pit_GetFlag_Isr(void)
{
return pitIsrFlag;
}
/******************************************************************************
*
*******************************************************************************/
void pit_ClearFlag_Isr(void)
{
pitIsrFlag = false;
}
/******************************************************************************
*
*******************************************************************************/
// counter - channel 1
void pit_ClearCounter_Isr(void)
{
tpm_i = 0;
}
/******************************************************************************
*
*******************************************************************************/
uint64_t pit_GetCounter_Isr(void)
{
return tpm_i;
}
#endif
#endif
/*****************************************************************************************
* Handles PIT interrupt if enabled
*****************************************************************************************/
void PIT_IRQHandler(void)
{
uint8_t index = 0;
for(index=0;index<2;index++)
{
if( PIT_TFLG_REG(PIT,index) )
{
// Clear interrupt
//PIT_TFLG0 = PIT_TFLG_TIF_MASK;
PIT_TFLG_REG(PIT,index) = PIT_TFLG_TIF_MASK;
#if (MODEISRTPM == FLAG_TPM)
pitIsrFlag[index] = true;
#else //if (MODEISRTPM == COUNTER_TPM)
#if (OTHER_TPM == COUNTER_TPM)
tpm_i[index] += 1;
#else
if(index)
tpm_i++;
else
pitIsrFlag = true;
#endif
#endif
}
}
}
/****************************************************************************************/
Aplicação Dual Blink LED
A aplicação proposta é utilizar os dois canais do PIT para sincronizar o acionamento de cada LED. Para o LED vermelho é utilizado o canal 0 e fazendo uso da rotina de Flag. O LED verde utiliza o canal 1, inicializado com valor 100, menor em relação ao canal 0. A rotina de contador de interrupção foi utilizada para sincronizar o acionamento do LED. Quando a contagem atinge o valor de 100 o LED verde é acionado.
// Projeto: Dual Blink LED
// Autor: Evandro Teixeira
// Endereço da Lib: /home/evandro/Documentos/lib-frdm-kl25z/Library-FRDM-KL25Z-master
#include "/home/evandro/Documentos/lib-frdm-kl25z/Library-FRDM-KL25Z-master/externs.h"
#include "MKL25Z4.h"
static int i = 0;
int main(void)
{
// Inicializa PIT canal 0
pit_Init(10000000,PIT_CH_0);
// Inicia PIT canal 0
pit_Start(PIT_CH_0);
// Incializa PIT canal 1
pit_Init(100000,PIT_CH_1);
// Inicia PIT canal 1
pit_Start(PIT_CH_1);
// Inicializa PTB18 - LED Vermelho
gpio_Init(GPIOB,18,OUTPUT,NO_PULL_RESISTOR);
// Inicializa PTB18 - LED Verde
gpio_Init(GPIOB,19,OUTPUT,NO_PULL_RESISTOR);
// Apaga LED Vermelho
gpio_Set(GPIOB,18,1);
// Aciona LED Verde
gpio_Set(GPIOB,19,0);
for (;;)
{
// Checa de Flag
if(pit_GetFlag_Isr()==true)
{
// Limpa Flag
pit_ClearFlag_Isr();
// Inverte o valor do LED Vermelho
gpio_Toggle(GPIOB,18);
}
// Verifica o valor do contador
if(pit_GetCounter_Isr() >= 100)
{
// Reset contador
pit_ClearCounter_Isr();
// Inverte o valor do LED Verde
gpio_Toggle(GPIOB,19);
}
}
/* Never leave main */
return 0;
}
Conclusão
Neste artigo foi apresentada mais uma biblioteca de software para a Freedom Board KL25Z utilizando o periférico PIT – Periodic Interrupt Timer.
Nos próximos artigos vamos apresentar mais bibliotecas de software (Timer, UART e entre outras) para a Freedom Board KL25Z. A biblioteca apresentada está disponível no meu Github.
Referências





O valor 100 é em que: ns, ms???
o valor 100 corresponde a numero de interrupções