at 72Mhz, that means you have 720 instructions to spare between each interrupt. your overhead is about 20 ticks, and your task may take 20 ticks. So your cpu utilization from this task along is about 10%.10 us is the fastest I could get
it is fairly easy:
1) pick a timer. For this, a timer with overflow interrupt or compare interrupt would work. I prefer the use of compare channels.
2) install a user handler.
this is what I use for tim4 (it should be easily copied over to other channels or other timers).
Code: Select all
//tmr4
//global variables
static void (* _tim4_ovfisrptr)(void)=empty_handler; //TIM4_ptr pointing to empty_handler by default
static void (* _tim4_cc1isrptr)(void)=empty_handler; //TIM4_ptr pointing to empty_handler by default
static void (* _tim4_cc2isrptr)(void)=empty_handler; //TIM4_ptr pointing to empty_handler by default
static void (* _tim4_cc3isrptr)(void)=empty_handler; //TIM4_ptr pointing to empty_handler by default
static void (* _tim4_cc4isrptr)(void)=empty_handler; //TIM4_ptr pointing to empty_handler by default
static uint16_t _tim4_cc1=0; //output compare registers
static uint16_t _tim4_cc2=0;
static uint16_t _tim4_cc3=0;
static uint16_t _tim4_cc4=0;
//isr for timer1 capture / compare
void TIM4_IRQHandler(void) {
//oc1..4 portion
//if ((TIM4->DIER & TIM_DIER_CC1IE) && (TIM4->SR & TIM_SR_CC1IF)) { //output compare 1 flag is set
if ((TIM4->SR & TIM_SR_CC1IF)) {TIM4->SR &=~TIM_SR_CC1IF; TIM4->CCR1 += _tim4_cc1; _tim4_cc1isrptr();}
if ((TIM4->SR & TIM_SR_CC2IF)) {TIM4->SR &=~TIM_SR_CC2IF; TIM4->CCR2 += _tim4_cc2; _tim4_cc2isrptr();}
if ((TIM4->SR & TIM_SR_CC3IF)) {TIM4->SR &=~TIM_SR_CC3IF; TIM4->CCR3 += _tim4_cc3; _tim4_cc3isrptr();}
if ((TIM4->SR & TIM_SR_CC4IF)) {TIM4->SR &=~TIM_SR_CC4IF; TIM4->CCR4 += _tim4_cc4; _tim4_cc4isrptr();}
//ovf isr
if ((TIM4->SR & TIM_SR_UIF)) {TIM4->SR &=~TIM_SR_UIF; _tim4_ovfisrptr();}
}
//initialize the timer4 (16bit)
void tmr4Init(uint16_t ps) {
//route the clock to timer
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
//source from internal clock -> disable slave mode
TIM4->SMCR &=~TIM_SMCR_SMS; //clear sms->use internal clock
//stop the timer to configure it
TIM4->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer
TIM4->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved
TIM4->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter
TIM4->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer
//or to simply zero the register
//TIM4->CR1 = 0; //much easier
//clear the status register bits for capture / compare flags
TIM4->SR &=~(TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF);
//disable the interrupt by clearing the enable bits
TIM4->DIER &=~(TIM_DIER_UIE | TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE);
//set the prescaler
TIM4->PSC = ps - 1; //set the prescaler
TIM4->RCR = 0; //repetition counter = 0 (=no repetition)
TIM4->ARR = -1; //auto reload register / period = 0; - need to change for downcounters
TIM4->CNT = 0; //reset the counter
//enable the timer.
TIM4->CR1 |= TIM_CR1_CEN; //enable the timer
}
//set up the period
void tmr4SetPR(uint32_t pr) {
TIM4->ARR = pr - 1;
}
//activate the isr handler
void tmr4OVFAttachISR(void (*isrptr)(void)) {
NVIC_DisableIRQ(TIM4_IRQn); //disable irq
_tim4_ovfisrptr = isrptr; //install user handler
//clear the flag
TIM4->SR &=~TIM_SR_UIF; //clear the interrupt flag
TIM4->DIER |= TIM_DIER_UIE; //enable the isr
NVIC_EnableIRQ(TIM4_IRQn); //enable irq
//priorities not set -> default values used.
}
//set TIM4_oc1 period
//pr is 16-bit. 32-bit used for compatability;
void tmr4OC1SetPR(uint16_t pr) {
//save the period value
_tim4_cc1 = pr - 0;
TIM4->CCR1 = _tim4_cc1;
//clear the flag
//TIM4->SR &=~TIM_SR_CC1IF; //clear the interrupt flag
//TIM4->DIER &=~TIM_DIER_CC1IE; //disable the isr
}
//activate the isr handler
void tmr4OC1AttachISR(void (*isrptr)(void)) {
NVIC_DisableIRQ(TIM4_IRQn); //disable irq
_tim4_cc1isrptr = isrptr; //install user handler
//clear the flag
TIM4->SR &=~TIM_SR_CC1IF; //clear the interrupt flag
TIM4->DIER |= TIM_DIER_CC1IE; //enable the isr
NVIC_EnableIRQ(TIM4_IRQn); //enable irq
//priorities not set -> default values used.
}
the usage is fairly simple:
Code: Select all
tmr4Init(TIM_PS1x); //initialize tmr4 to 1:1 prescaler, free running.
tmr4OC1SetPR(10*clockCyclesPerMicrosecond); //set the period for compare ch1 to be invoked. 10us in this case
tmr4OC1AttachISR(myISR); //install user isr - where you flip the output pin(s)
this particular timer on this particular device has 4 compare channels + 1 overflow. so you in theory can have it trigger 3 independent tasks using compare ch2/3/4, and one more for the overflow channel (not independent however).