Programmable Timing Functions Part 1: Timer-Generated Interrupts
Programmable Timing Functions Part 1: Timer-Generated Interrupts
Programmable Timing Functions Part 1: Timer-Generated Interrupts
Interrupt
handler
Main
thread
Timer
events
T
2T
3T
4T
5T
Flag
Output
Control
Interrupt
PWM
Current Count
Counter mode: count pulses which indicate events (e.g. odometer pulses)
Timer mode: periodic clock source, so count value proportional to elapsed time (e.g.
stopwatch)
TIM2 to TIM5
Input capture, output compare, PWM, one pulse mode
16-bit or 32-bit auto-reload register
TIM9 to TIM14
Input capture, output compare, PWM, one pulse mode
Only 16-bit auto-reload register
CK_PSC = CK_INT
when count enabled
ARR
Update Event
Signaled when UIF sets,
if enabled (UIE=1)
Scaled clock triggers
up-counter/down-counter
FCK_CNT = FCK_PSC Prescale
CNT resets to 0 (if count up) or reloads ARR (if count down)
UIF flag is set in the status register
TIMx_PSC
TIMx_CNT
16 bits
16 bits
Fcnt
Reload
Event
Current Count
TIMx_SR
UIF
&
Interrupt
UIE
TIMx_DIER
TIMx_PSC prescale value multiplies the input clock period (1/ Fclk) to produce counter clock
period: Tcnt = 1/Fcnt = (PSC+1)(1/Fclk)
Periodic time interval is the ARR (Auto-Reload Register) value times the counter clock period:
Counter timing:
Prescale = 1
ARR = 36
Counter timing:
Prescale = 4
ARR = 36
11
12
Examples:
TIM4->CR1 |= 0x01;
//Enable counting
TIM4->CR1 &= ~0x01; //Disable counting
Counter Enable
1 = enable, 0 = disable
CEN=1 to begin counting
(apply CK_INT to CK_PSC)
UIF
Update interrupt flag
UIE
UDE
1 = enable
0 = disable
Examples:
TIM4->DIER |= 0x01;
//Enable interrupt
TIM4->DIER &= ~0x01; //Disable interrupt
Assembly:
RCC_TIM4EN EQU 0x04
arr_value EQU 4999
psc_value EQU 9999
Enable clock to Timer4
DIER_UIE EQU 1
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; CR1_CEN EQU 1
Enable counting
TIM4->CR1 |= TIM_CR1_CEN;
ldr r0,=RCC
ldr r1,[r0,#APB1ENR]
orr r1,#RCC_TIM4EN
str r1,[r0,#APB1ENR]
ldr r0,=TIM4
mov r1,#arr_value
str r1,[r0,#ARR]
mov r1,#psc_value
str r1,[r0,#PSC]
ldr r1,[r0,#DIER]
orr r1,#DIER_UIE
str r1,[r0,#DIER]
ldr r1,[r0,#CR1]
orr r1,#CR1_CEN
str r1,[r0,#CR1]
Interrupt Handler
Interrupts should be enabled in the CPU
__enable_irq() ;
Assembly: CPSIE I
CMSIS ISR name: TIM4_IRQHandler
NVIC_EnableIRQ(TIM4_IRQn); //n = 30 for TIM4
Assembly: Enable TIM4_IRQn (bit 30) in NVIC_ISER0
ISR activities
Clear pending IRQ
NVIC_ClearPendingIRQ(TIM2_IRQn);
Assembly: write 1 to bit 30 of NVIC_ICPR0
Do the ISRs work
Clear pending flag for timer
TIM2->SR &= ~TIM_SR_UIF;
Assembly: write 0 to UIF (bit 0) of TIM4_SR
Example: Stopwatch
Measure time with 100 us resolution
Display elapsed time, updating screen every 10 ms
Use SysTick (basic time unit in the system)
Counter increment every 100 us
LCD Update every 10 ms
Update LCD every nth periodic interrupt
n = 10 ms/100us = 100
Dont update LCD in ISR! Too slow.
Instead set flag LCD_Update in ISR, poll it in main loop
Usually the ISR is only for update the timer or for delaying (precise timing!)
SysTick Timer
Address
$E000E010
$E000E014
$E000E018
31-24
0
0
0
23-17
0
16
COUNT
15-3
2
1
0
0
CLK_SRC INTEN ENABLE
24-bit RELOAD value
24-bit CURRENT value of SysTick counter
Name
NVIC_ST_CTRL_R
NVIC_ST_RELOAD_R
NVIC_ST_CURRENT_R
Initialization (4 steps)
Step1: Clear ENABLE to stop counter
Step2: Specify the RELOAD value
Step3: Clear the counter via NVIC_ST_CURRENT_R
Step4: Set CLK_SRC=1 and specify interrupt action via INTEN in NVIC_ST_CTRL_R
SysTick Timer
SysTick_Init
; disable SysTick during setup
24-bit Countdown Timer
LDR R1, =NVIC_ST_CTRL_R
MOV R0, #0
; Clear Enable
STR R0, [R1]
; set reload to maximum reload value
LDR R1, =NVIC_ST_RELOAD_R
LDR R0, =0x00FFFFFF;
; Specify RELOAD value
STR R0, [R1]
; reload at maximum
; writing any value to CURRENT clears it
LDR R1, =NVIC_ST_CURRENT_R
MOV R0, #0
STR R0, [R1]
; clear counter
; enable SysTick with core clock
LDR R1, =NVIC_ST_CTRL_R
MOV R0, #0x0005
; Enable but no interrupts (later)
STR R0, [R1]
; ENABLE and CLK_SRC bits set
BX LR
SysTick Timer
;------------SysTick_Wait-----------; Time delay using busy wait.
; Input: R0 delay parameter in units of the core clock
;
80 MHz(12.5 nsec each tick)
; Output: none
; Modifies: R1
SysTick_Wait
SUB R0, R0, #1
; delay-1
LDR R1, =NVIC_ST_RELOAD_R
STR R0, [R1]
; time to wait
LDR R1, =NVIC_ST_CURRENT_R
STR R0, [R1]
; any value written to CURRENT clears
LDR R1, =NVIC_ST_CTRL_R
SysTick_Wait_loop
LDR R0, [R1]
; read status
ANDS R0, R0, #0x00010000 ; bit 16 is COUNT flag
BEQ SysTick_Wait_loop
; repeat until flag set
BX
LR
SysTick Timer
;------------SysTick_Wait10ms-----------; Call this routine to wait for R0*10 ms
; Time delay using busy wait. This assumes 80 MHz clock
; Input: R0 number of times to wait 10 ms before returning
; Output: none
; Modifies: R0
DELAY10MS EQU 800000
; clock cycles in 10 ms
SysTick_Wait10ms
PUSH {R4, LR}
; save R4 and LR
MOVS R4, R0
; R4 = R0 = remainingWaits
BEQ SysTick_Wait10ms_done
; R4 == 0, done
SysTick_Wait10ms_loop
LDR R0, =DELAY10MS
; R0 = DELAY10MS
BL SysTick_Wait
; wait 10 ms
SUBS R4, R4, #1
; remainingWaits-BHI SysTick_Wait10ms_loop
; if(R4>0), wait another 10 ms
SysTick_Wait10ms_done
POP {R4, PC}
SysTick Timer in C
#define NVIC_ST_CTRL_R(*((volatile uint32_t *)0xE000E010))
#define NVIC_ST_RELOAD_R(*((volatile uint32_t *)0xE000E014))
#define NVIC_ST_CURRENT_R(*((volatile uint32_t *)0xE000E018))
void SysTick_Init(void){
NVIC_ST_CTRL_R = 0; // 1) disable SysTick during setup
NVIC_ST_RELOAD_R = 0x00FFFFFF; // 2) maximum reload value
NVIC_ST_CURRENT_R = 0; // 3) any write to CURRENT clears it
NVIC_ST_CTRL_R = 0x00000005; // 4) enable SysTick with core clock
}
// The delay parameter is in units of the 80 MHz core clock(12.5 ns)
void SysTick_Wait(uint32_t delay){
NVIC_ST_RELOAD_R = delay-1; // number of counts
NVIC_ST_CURRENT_R = 0;
// any value written to CURRENT clears
while((NVIC_ST_CTRL_R&0x00010000)==0){ // wait for flag
}
}
// Call this routine to wait for delay*10ms
void SysTick_Wait10ms(uint32_t delay){
unsigned long i;
for(i=0; i<delay; i++){
SysTick_Wait(800000); // wait 10ms
Bard, Gerstlauer, Valvano, Yerraballi
}