In this article we will discuss about programming Timer0 module. How to use interrupts and make a suitable reusable library. We will take Atmel AT89C55WD controller as target and IAR Embedded workbench as compiler. Lets start the journey.
First of all we will discuss about the registers of the target relating Timer0/1 module. Before that its important to note that 8051 peripheral clock takes 1 complete system clock period. So maximum peripheral clock will be Fper = Fosc/2 if X2 not selected.
Timer0/1 peripheral takes 6 complete peripheral clock period as its cycle. So maximum Timer0/1 clock rate will be Ftimer0/1 = Fper/6 = Fosc/12 in standard mode. Timer0/1 has 4 modes of operation. We will discuss about those shortly. Its time to make a short overview of the registers related to Timer0/1 module.
[TMOD register]
TMOD Register controls operating modes and source of clocking. If clock comes from internal clock source then we can call its operating as timer otherwise counter , if clock comes from external Tx pin. Gray colored bits within TMOD register related to Timer0 module.
[TCON register]
TCON register control timers and other operating parameters like external trigger options. It also reflects the timer internal or external interrupt status.Gray colored bits within TCON register related to Timer0 module.
Remember, though INT0(for Timer0), INT1(for Timer1) both controls the timer when external control option selected, those pins are actually used to generate general purpose interrupt. Options for these general purpose interrupts are selected by using ITx bits, status are reflected by IEx bits.
[Interrupt vector addresses]
Both Timer0/1 module overflow interrupt and general purpose External0/1 interrupt addresses are shown in this table.
Now I will show 4 block diagrams for Timer0/1 operating modes. Before that I will discuss some symbolic text used to describe block diagrams.
x = 0 or 1
Tx = External clock source pin
INTx = External pin for controlling Timer0/1 module. These pins actually used for general purpose interrupt.
C/Tx = Counter / Timer operation control bit
TRx = Timer run control bit
THx = Timer register high byte
TLx = Timer register low byte
TFx = Timer overflow detection bit
[Timer0/1 mode = 0 (13 bit)]

[Timer0/1 mode = 1 (16 bit)]
[Timer0/1 mode = 2 (8 bit auto reload)]
[Timer0/1 mode = 3 (8 bit split)]
static struct timer01_callbacks_t *p_timer01_callbacks;
static t_timer01_register timer0_counter;
static t_timer01_register timer1_counter;
#pragma vector=timer0
__interrupt void timer0_interrupt_handler(void) {
timer0_counter.high = TH0;
timer0_counter.low = TL0;
p_timer01_callbacks->pfnOverflow(timer01_id_0,&timer0_counter);
}
#pragma vector=timer1
__interrupt void timer1_interrupt_handler(void) {
timer1_counter.high = TH1;
timer1_counter.low = TL1;
p_timer01_callbacks->pfnOverflow(timer01_id_1,&timer1_counter);
}
#pragma vector=extern0
__interrupt void extern0_interrupt_handler(void) {
timer0_counter.high = TH0;
timer0_counter.low = TL0;
p_timer01_callbacks->pfnExternal(timer01_id_0,&timer0_counter);
}
#pragma vector=extern1
__interrupt void extern1_interrupt_handler(void) {
timer1_counter.high = TH1;
timer1_counter.low = TL1;
p_timer01_callbacks->pfnExternal(timer01_id_1,&timer1_counter);
}
void Timer01_RegCallbacks(timer01_ids id,struct timer01_callbacks_t *pCallbacks) {
switch(id) {
case timer01_id_0:
IE_bit.ET0 = 1;
IE_bit.EX0 = 1;
break;
case timer01_id_1:
IE_bit.ET1 = 1;
IE_bit.EX1 = 1;
break;
}
p_timer01_callbacks = pCallbacks;
}
void Timer01_UnregCallbacks(timer01_ids id) {
switch(id) {
case timer01_id_0:
IE_bit.ET0 = 0;
IE_bit.EX0 = 0;
break;
case timer01_id_1:
IE_bit.ET1 = 0;
IE_bit.EX1 = 0;
break;
}
}
void Timer01_Init(timer01_ids id,timer01_modes modes,enum timer01_clk_src clk_src) {
switch(id) {
case timer01_id_0:
TMOD = modes;
TMOD_bit.C_T0 = clk_src;
break;
case timer01_id_1:
TMOD = modes << 4;
TMOD_bit.C_T1 = clk_src;
break;
}
}
void Timer01_SetControl(timer01_ids id,timer01_ctrl_types ctrl_type,timer01_trigger_types trigger_type) {
switch(id) {
case timer01_id_0:
TMOD_bit.GATE0 = ctrl_type;
TCON_bit.IT0 = trigger_type;
break;
case timer01_id_1:
TMOD_bit.GATE1 = ctrl_type;
TCON_bit.IT1 = trigger_type;
break;
}
}
Now I will show the main routine to initiate various modes. For example Timer0 has been taken, If Timer1 required, only change the id parameter to timer01_id_1. Remember, if Timer0 mode 3 is selected the Timer1 mode 3 cannot be instantiated in parallel. Because Timer0 reserves Timer1 control bits in mode 3.
[Timer0 mode = 0 (13 bit)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_13,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 1 (16 bit)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_16,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 2 (8 bit auto reload)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_reload,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0xF000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 3 (8 bit split)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_split,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_RegCallbacks(timer01_id_1,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { // User codes will go here}
return 0;
}
Now I will show a complete main.c file should look like to work with.
#include <timer_0_1.h>
#include <io80C51RC.h>
void TimerOvrflow(timer01_ids,t_timer01_register *);
void TimerExternal(timer01_ids,t_timer01_register *);
static struct timer01_callbacks_t timer01_callbacks = {
.pfnOverflow = TimerOvrflow,
.pfnExternal = TimerExternal
};
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_split,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_RegCallbacks(timer01_id_1,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0xF000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
void TimerOvrflow(timer01_ids id,t_timer01_register *p_timer01_register) {
if(id == timer01_id_0) {
TIMER01_REG_LOW(timer01_id_0) = 0x00;
P0 = P0 ^ 0x01; // Toggle to show action P0.0
} else {
TIMER01_REG_HIGH(timer01_id_0) = 0x00;
P0 = P0 ^ 0x02; // Toggle to show action P0.1
}
}
void TimerExternal(timer01_ids id,t_timer01_register *p_timer01_register) {
P0 = P0 ^ 0x03; // Toggle to show action P0.2
}
N.B: Thanks for reading my article. I will update this article latter. Requesting for your suggestions. Timer2 and Serial port is coming soon.
First of all we will discuss about the registers of the target relating Timer0/1 module. Before that its important to note that 8051 peripheral clock takes 1 complete system clock period. So maximum peripheral clock will be Fper = Fosc/2 if X2 not selected.
Timer0/1 peripheral takes 6 complete peripheral clock period as its cycle. So maximum Timer0/1 clock rate will be Ftimer0/1 = Fper/6 = Fosc/12 in standard mode. Timer0/1 has 4 modes of operation. We will discuss about those shortly. Its time to make a short overview of the registers related to Timer0/1 module.
[TMOD register]
TMOD Register controls operating modes and source of clocking. If clock comes from internal clock source then we can call its operating as timer otherwise counter , if clock comes from external Tx pin. Gray colored bits within TMOD register related to Timer0 module.
[TCON register]
TCON register control timers and other operating parameters like external trigger options. It also reflects the timer internal or external interrupt status.Gray colored bits within TCON register related to Timer0 module.
Remember, though INT0(for Timer0), INT1(for Timer1) both controls the timer when external control option selected, those pins are actually used to generate general purpose interrupt. Options for these general purpose interrupts are selected by using ITx bits, status are reflected by IEx bits.
[Interrupt vector addresses]
Both Timer0/1 module overflow interrupt and general purpose External0/1 interrupt addresses are shown in this table.
Now I will show 4 block diagrams for Timer0/1 operating modes. Before that I will discuss some symbolic text used to describe block diagrams.
x = 0 or 1
Tx = External clock source pin
INTx = External pin for controlling Timer0/1 module. These pins actually used for general purpose interrupt.
C/Tx = Counter / Timer operation control bit
TRx = Timer run control bit
THx = Timer register high byte
TLx = Timer register low byte
TFx = Timer overflow detection bit
[Timer0/1 mode = 0 (13 bit)]

[Timer0/1 mode = 1 (16 bit)]
[Timer0/1 mode = 2 (8 bit auto reload)]
[Timer0/1 mode = 3 (8 bit split)]
As Timer0 and Timer1 both are almost same those can be
controlled with a common type library. Here we will discuss how can we do that. Timer01.h and
Timer01.c are the library files that I am going do describe. Timer01.h contains
various structures and function signatures where Timer01.c contains the actuall implementations.
[Here is the detailed code of Timer01.h]
#ifndef __TIMER_0_1_H__
#define __TIMER_0_1_H__
#include <io80C51RC.h>
#define TL_timer01_id_0 TL0
#define TH_timer01_id_0 TH0
#define TL_timer01_id_1 TL1
#define TH_timer01_id_1 TH1
#define TR_timer01_id_0(val) \
TL0 = (unsigned char) val; \
TH0 = (unsigned char) (val >> 8)
#define TR_timer01_id_1(val) \
TL1 = (unsigned char) val; \
TH1 = (unsigned char) (val >> 8)
#define TIMER01_REG_LOW(id) TL_##id
#define TIMER01_REG_HIGH(id) TH_##id
#define Timer01_SetRegister(id,val) TR_##id(val)
#define TS_timer01_id_0(state) TCON_bit.TR0 = state;
#define TS_timer01_id_1(state) TCON_bit.TR1 = state;
#define Timer01_Run(id) TS_##id(timer01_on)
#define Timer01_Stop(id) TS_##id(timer01_off)
#define Timer01_SetState(id,timer01_states) TS_##id(timer01_states)
typedef union {
unsigned int value;
struct {
unsigned char low;
unsigned char high;
};
} t_timer01_register;
typedef enum {
timer01_id_0 = 0,
timer01_id_1 = 1
} timer01_ids;
typedef enum {
timer01_mode_13 = 0, // 13 bit
timer01_mode_16 = 1, // 16 bit
timer01_mode_reload = 2, // 8 bit auto reload
timer01_mode_split = 3 // Split
} timer01_modes;
typedef enum {
timer01_ctrl_auto = 0,
timer01_ctrl_manual = 1
} timer01_ctrl_types;
typedef enum {
timer01_trigger_level = 0,
timer01_trigger_edge = 1
} timer01_trigger_types;
typedef enum {
timer01_off = 0,
timer01_on = 1
} timer01_states;
enum timer01_clk_src {
timer01_clk_internal = 0,
timer01_clk_external = 1
};
struct timer01_callbacks_t {
void (*pfnOverflow) (timer01_ids,t_timer01_register *);
void (*pfnExternal) (timer01_ids,t_timer01_register *);
};
void Timer01_Init(timer01_ids,timer01_modes,enum timer01_clk_src);
void Timer01_SetControl(timer01_ids,timer01_ctrl_types,timer01_trigger_types);
void Timer01_RegCallbacks(timer01_ids,struct timer01_callbacks_t *);
void Timer01_UnregCallbacks(timer01_ids);
#endif
#define __TIMER_0_1_H__
#include <io80C51RC.h>
#define TL_timer01_id_0 TL0
#define TH_timer01_id_0 TH0
#define TL_timer01_id_1 TL1
#define TH_timer01_id_1 TH1
#define TR_timer01_id_0(val) \
TL0 = (unsigned char) val; \
TH0 = (unsigned char) (val >> 8)
#define TR_timer01_id_1(val) \
TL1 = (unsigned char) val; \
TH1 = (unsigned char) (val >> 8)
#define TIMER01_REG_LOW(id) TL_##id
#define TIMER01_REG_HIGH(id) TH_##id
#define Timer01_SetRegister(id,val) TR_##id(val)
#define TS_timer01_id_0(state) TCON_bit.TR0 = state;
#define TS_timer01_id_1(state) TCON_bit.TR1 = state;
#define Timer01_Run(id) TS_##id(timer01_on)
#define Timer01_Stop(id) TS_##id(timer01_off)
#define Timer01_SetState(id,timer01_states) TS_##id(timer01_states)
typedef union {
unsigned int value;
struct {
unsigned char low;
unsigned char high;
};
} t_timer01_register;
typedef enum {
timer01_id_0 = 0,
timer01_id_1 = 1
} timer01_ids;
typedef enum {
timer01_mode_13 = 0, // 13 bit
timer01_mode_16 = 1, // 16 bit
timer01_mode_reload = 2, // 8 bit auto reload
timer01_mode_split = 3 // Split
} timer01_modes;
typedef enum {
timer01_ctrl_auto = 0,
timer01_ctrl_manual = 1
} timer01_ctrl_types;
typedef enum {
timer01_trigger_level = 0,
timer01_trigger_edge = 1
} timer01_trigger_types;
typedef enum {
timer01_off = 0,
timer01_on = 1
} timer01_states;
enum timer01_clk_src {
timer01_clk_internal = 0,
timer01_clk_external = 1
};
struct timer01_callbacks_t {
void (*pfnOverflow) (timer01_ids,t_timer01_register *);
void (*pfnExternal) (timer01_ids,t_timer01_register *);
};
void Timer01_Init(timer01_ids,timer01_modes,enum timer01_clk_src);
void Timer01_SetControl(timer01_ids,timer01_ctrl_types,timer01_trigger_types);
void Timer01_RegCallbacks(timer01_ids,struct timer01_callbacks_t *);
void Timer01_UnregCallbacks(timer01_ids);
#endif
[Here is the detailed code of Timer01.c]
#include "timer_0_1.h"static struct timer01_callbacks_t *p_timer01_callbacks;
static t_timer01_register timer0_counter;
static t_timer01_register timer1_counter;
#pragma vector=timer0
__interrupt void timer0_interrupt_handler(void) {
timer0_counter.high = TH0;
timer0_counter.low = TL0;
p_timer01_callbacks->pfnOverflow(timer01_id_0,&timer0_counter);
}
#pragma vector=timer1
__interrupt void timer1_interrupt_handler(void) {
timer1_counter.high = TH1;
timer1_counter.low = TL1;
p_timer01_callbacks->pfnOverflow(timer01_id_1,&timer1_counter);
}
#pragma vector=extern0
__interrupt void extern0_interrupt_handler(void) {
timer0_counter.high = TH0;
timer0_counter.low = TL0;
p_timer01_callbacks->pfnExternal(timer01_id_0,&timer0_counter);
}
#pragma vector=extern1
__interrupt void extern1_interrupt_handler(void) {
timer1_counter.high = TH1;
timer1_counter.low = TL1;
p_timer01_callbacks->pfnExternal(timer01_id_1,&timer1_counter);
}
void Timer01_RegCallbacks(timer01_ids id,struct timer01_callbacks_t *pCallbacks) {
switch(id) {
case timer01_id_0:
IE_bit.ET0 = 1;
IE_bit.EX0 = 1;
break;
case timer01_id_1:
IE_bit.ET1 = 1;
IE_bit.EX1 = 1;
break;
}
p_timer01_callbacks = pCallbacks;
}
void Timer01_UnregCallbacks(timer01_ids id) {
switch(id) {
case timer01_id_0:
IE_bit.ET0 = 0;
IE_bit.EX0 = 0;
break;
case timer01_id_1:
IE_bit.ET1 = 0;
IE_bit.EX1 = 0;
break;
}
}
void Timer01_Init(timer01_ids id,timer01_modes modes,enum timer01_clk_src clk_src) {
switch(id) {
case timer01_id_0:
TMOD = modes;
TMOD_bit.C_T0 = clk_src;
break;
case timer01_id_1:
TMOD = modes << 4;
TMOD_bit.C_T1 = clk_src;
break;
}
}
void Timer01_SetControl(timer01_ids id,timer01_ctrl_types ctrl_type,timer01_trigger_types trigger_type) {
switch(id) {
case timer01_id_0:
TMOD_bit.GATE0 = ctrl_type;
TCON_bit.IT0 = trigger_type;
break;
case timer01_id_1:
TMOD_bit.GATE1 = ctrl_type;
TCON_bit.IT1 = trigger_type;
break;
}
}
Now I will show the main routine to initiate various modes. For example Timer0 has been taken, If Timer1 required, only change the id parameter to timer01_id_1. Remember, if Timer0 mode 3 is selected the Timer1 mode 3 cannot be instantiated in parallel. Because Timer0 reserves Timer1 control bits in mode 3.
[Timer0 mode = 0 (13 bit)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_13,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 1 (16 bit)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_16,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 2 (8 bit auto reload)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_reload,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0xF000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
[Timer0 mode = 3 (8 bit split)]
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_split,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_RegCallbacks(timer01_id_1,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0x0000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { // User codes will go here}
return 0;
}
Now I will show a complete main.c file should look like to work with.
#include <timer_0_1.h>
#include <io80C51RC.h>
void TimerOvrflow(timer01_ids,t_timer01_register *);
void TimerExternal(timer01_ids,t_timer01_register *);
static struct timer01_callbacks_t timer01_callbacks = {
.pfnOverflow = TimerOvrflow,
.pfnExternal = TimerExternal
};
int main( void )
{
Timer01_Init(timer01_id_0,timer01_mode_split,timer01_clk_internal);
Timer01_SetControl(timer01_id_0,timer01_ctrl_auto,timer01_trigger_edge);
Timer01_RegCallbacks(timer01_id_0,&timer01_callbacks);
Timer01_RegCallbacks(timer01_id_1,&timer01_callbacks);
Timer01_SetRegister(timer01_id_0,0xF000);
IE_bit.EA = 1; // Enable global interrupt
Timer01_Run(timer01_id_0);
Timer01_SetState(timer01_id_1,timer01_on);
while(1) { /* User codes will go here */ }
return 0;
}
void TimerOvrflow(timer01_ids id,t_timer01_register *p_timer01_register) {
if(id == timer01_id_0) {
TIMER01_REG_LOW(timer01_id_0) = 0x00;
P0 = P0 ^ 0x01; // Toggle to show action P0.0
} else {
TIMER01_REG_HIGH(timer01_id_0) = 0x00;
P0 = P0 ^ 0x02; // Toggle to show action P0.1
}
}
void TimerExternal(timer01_ids id,t_timer01_register *p_timer01_register) {
P0 = P0 ^ 0x03; // Toggle to show action P0.2
}
N.B: Thanks for reading my article. I will update this article latter. Requesting for your suggestions. Timer2 and Serial port is coming soon.






Good work. Keep it up.
ReplyDelete