Bluepill: 2 different timers to read 2 different FREQS

Post here first, or if you can't find a relevant section!
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by dannyf »

A channel is nothing but an input pin who will trigger an input capture, on a change of state or level, depending on the chip. The avrs have them for sure, called timer input pins.

The basic concept is the same.

The actual set up for your chip will depend on the tools you have. Personally I prefer use just cmsis...
ozcar
Posts: 143
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by ozcar »

julian_lp wrote: Sat Jul 29, 2023 5:15 am
Well the first thing that confused me was the whole channel thing (cause there are no "channel" in atmega328 world)
Now I'm starting to think about a channel, and the starting point is that all the frequencies should be "related" or "not too different" to share the same timer... I'll have to read much more yet about that topic
The 16-bit timer on ATMEGA328 has one input capture register, while one STM32F1 timer may have up to four input capture registers (the same registers are also usable for output compare/PWM). Each of those capture/compare registers, or "channels", is associated with an I/O pin, or in some cases, there may be a choice of more than one pin. You can also pull some tricks to route a single input pin to two channels, to say capture rising edges in one channel and falling edges in the other channel - useful if you wanted to measure duty cycle as well as frequency.

Of course, if you use two channels on the same timer, then they share a common counter register and that constrains what you can do.

What else you are using GPIO pins for could also perhaps force your hand in channel and timer choice.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by dannyf »

Interesting option given the stm32 cpu clock.
the advantage of using input capture is that you don't have to read the timer counter right away - as its value is stored in the capture register.

Using external interrupt or pin change interrupts gives you more flexiblity in pin assignment. however you have to read the counter quickly, or consistently. that means the external interrupt or pin change interrupts must be of high priority.

on F1, using DWT would be great for this - I have a version of gps-disciplined oscillator running using this approach.
Not very doable/recommended in the 328 version tough
works on the atmega as well.
julian_lp
Posts: 8
Joined: Sun Jul 09, 2023 10:23 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by julian_lp »

Need help now to confirm if my undestanding of this is correct
(using hardwaretimer abstaction)
First I go to the pinout diagram (bluepill) and see that PA1 is tied to TIMER 1 channel 2

so, with the intention of using timer1 and channel 2, I declare

Code: Select all

 
#define pin_CAPTURE_T1C2  PA1
Now,

Code: Select all

 	TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin_CAPTURE_T1C2  ), PinMap_PWM);
    	channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_CAPTURE_T1C2), PinMap_PWM));
     	 MyTim = new HardwareTimer(Instance);
And after that, automatically, MyTim is using the timer 1 module and channel value is 2 ?

If in another chip PA1 were tied to, for instance, timer4 and channel 3, MyTim would use timer4 and channel value would be 3?

And another question

If I was declaring

Code: Select all

#define pin_CAPTURE_T1C2  PA1
#define pin_CAPTURE_T1C3  PA2
(Note that both pins share the timer1 in bluepill)

Code: Select all


HardwareTimer *MyTim;
HardwareTimer *MyTim_2;


 	TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin_CAPTURE_T1C2  ), PinMap_PWM);
    	channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_CAPTURE_T1C2), PinMap_PWM));
     	 MyTim = new HardwareTimer(Instance);


 	TIM_TypeDef *Instance_2 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin_CAPTURE_T1C3  ), PinMap_PWM);
    	channel_3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_CAPTURE_T1C3  ), PinMap_PWM));
     	 MyTim_2= new HardwareTimer(Instance_2);
would instance and instance_2 be using the same timer? is that the correct way to instantiate two channels or "instance_2" in unnecesary?

dannyf wrote: Sun Jul 30, 2023 12:42 pm on F1, using DWT would be great for this - I have a version of gps-disciplined oscillator running using this approach.
Actually dont know anything about what DWT is, so I'll have to keep reading ...
julian_lp
Posts: 8
Joined: Sun Jul 09, 2023 10:23 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by julian_lp »

Just came again to paste some working code that is able to compute 2 different frequencies (same timer, 2 channels), just as I needed in my first post.
I'll hopefully improve it but what's important so far is that, with all your help, I'm starting to understand how "hardwaretimer" works
regards and thanks again
julian

Code: Select all

#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION  < 0x01090000)
#error "Due to API change, this sketch is compatible with STM32_CORE_VERSION  >= 0x01090000"
#endif

///display para debug
#include <U8x8lib.h>
U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

///Timer 1 Channel 2 (ver pinout)
#define pin_T1_CH_2  PA1 ///"pin_T"imer"1_CH_"annel"2" => pin_T1_CH_2
uint32_t T1_channel_2;

///Timer 1 Channel 3 (ver pinout)
#define pin_T1_CH_3  PA2
uint32_t T1_channel_3;

///Timer 3 Channel 3 (ver pinout)
#define pin_T3_CH_1 PA6
uint32_t T3_channel_3;


#define PIN_SALIDA_PWM PA4


uint32_t T1_input_freq = 0;

uint32_t T3_input_freq = 0;


volatile uint32_t v_FrequencyMeasured_CH2, v_LastCapture_CH2 = 0, v_CurrentCapture_CH2;
volatile uint32_t v_overflowCompareCount_CH2 = 0;


volatile uint32_t v_FrequencyMeasured_CH3, v_LastCapture_CH3 = 0, v_CurrentCapture_CH3;
volatile uint32_t v_overflowCompareCount_CH3 = 0;



HardwareTimer *gTimer1;


void InputCapture_CH3_IT_callback(void){
    ///CAPTURE DEL CANAL 3

   v_CurrentCapture_CH3 = gTimer1->getCaptureCompare(T1_channel_3);
    /* frequency computation */
    
    if (v_overflowCompareCount_CH3){
        v_FrequencyMeasured_CH3 = T1_input_freq / ( (v_overflowCompareCount_CH3 * 0x10000) + v_CurrentCapture_CH3 - v_LastCapture_CH3);
    }else{    
        v_FrequencyMeasured_CH3 = T1_input_freq / (v_CurrentCapture_CH3 - v_LastCapture_CH3);
    }

    v_LastCapture_CH3 = v_CurrentCapture_CH3;
    v_overflowCompareCount_CH3 = 0;

}    


void InputCapture_IT_callback(void){
    /* capture del TIMER_1 CHANNEL_2*/
    v_CurrentCapture_CH2 = gTimer1->getCaptureCompare(T1_channel_2);
    /* frequency computation */
    
    if (v_overflowCompareCount_CH2){
        v_FrequencyMeasured_CH2 = T1_input_freq / ( (v_overflowCompareCount_CH2 * 0x10000) + v_CurrentCapture_CH2 - v_LastCapture_CH2);
    }else{    
        v_FrequencyMeasured_CH2 = T1_input_freq / (v_CurrentCapture_CH2 - v_LastCapture_CH2);
    }

    v_LastCapture_CH2 = v_CurrentCapture_CH2;
    v_overflowCompareCount_CH2 = 0;
}


void Overflow_IT_callback(void){
    v_overflowCompareCount_CH2++;
    v_overflowCompareCount_CH3++;
}

void setup(){
    Serial.begin(115200);


    u8x8.begin();
    u8x8.setFont(u8x8_font_7x14_1x2_f);
    u8x8.drawString(0, 0, "* FREQ *");

    TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin_T1_CH_2), PinMap_PWM);

    T1_channel_2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_T1_CH_2), PinMap_PWM));
    T1_channel_3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_T1_CH_3), PinMap_PWM));


    gTimer1 = new HardwareTimer(Instance);
   

    gTimer1->setMode(T1_channel_2, TIMER_INPUT_CAPTURE_RISING, pin_T1_CH_2);
    gTimer1->setMode(T1_channel_3, TIMER_INPUT_CAPTURE_RISING, pin_T1_CH_3);

    uint32_t PrescalerFactor = 1;
    gTimer1->setPrescaleFactor(PrescalerFactor);

    gTimer1->setOverflow(0x10000); // Maximo valor de overflow para timer de 16 bits EN HEXADECIMAL



    gTimer1->attachInterrupt(T1_channel_2, InputCapture_IT_callback); ///esta es la interrupcion del capture (en cada rising edge)
    

    gTimer1->attachInterrupt(T1_channel_3, InputCapture_CH3_IT_callback); ///esta es la interrupcion del capture (en cada rising edge)
    

    gTimer1->attachInterrupt(Overflow_IT_callback); ///<<<  esta es la interrupcion de ROLLOVER o Overflow.... (no depende del canal, porque el overflow es comun para todos los canales que dependen del timer en cuestiòn)
    
    gTimer1->resume();

    
    
    ///TIMER 3 como PWM para hacer un feed al Timer 1 y comprobar lectura de frequencia de otro canal -------------------------------------------------
    HardwareTimer *gTimer3 = new HardwareTimer(TIM3);
    T3_channel_3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin_T3_CH_1), PinMap_PWM));
    
    gTimer3->setMode(T3_channel_3, TIMER_OUTPUT_COMPARE_PWM1, pin_T3_CH_1);   
    gTimer3->setOverflow(135000, MICROSEC_FORMAT); // 100000 microseconds = 100 milliseconds = 10 HZ    
    gTimer3->setCaptureCompare(T3_channel_3, 50, PERCENT_COMPARE_FORMAT); // 50% duty cycle
    
    gTimer3->attachInterrupt(IRQ_T3_Update_IT_callback); ///OVERFLOW IRQ
    gTimer3->attachInterrupt(T3_channel_3, IRQ_T3_Compare_IT_callback);
    gTimer3->resume();     


    T1_input_freq = gTimer1->getTimerClkFreq() / gTimer1->getPrescaleFactor();

    T3_input_freq = gTimer3->getTimerClkFreq() / gTimer3->getPrescaleFactor();


}



void IRQ_T3_Update_IT_callback(void){ // Update event correspond to Rising edge of PWM when configured in PWM1 mode
  digitalWrite(PIN_SALIDA_PWM, LOW); // pin2 will be complementary to pin
}

void IRQ_T3_Compare_IT_callback(void){ // Compare match event correspond to falling edge of PWM when configured in PWM1 mode

 
  digitalWrite(PIN_SALIDA_PWM, HIGH);
}




void loop(){
    
 
    //es NECESARIO INICIALIZAR EL BUFFER para que la funciòn de alineaciòn funcione bien
    char buffFreqToPrint[12] = "           ";
  
    ///se imprime la frecuencia del CANAL 2
    formatLongRightAlign(buffFreqToPrint,v_FrequencyMeasured_CH2);    
    buffFreqToPrint[0]  = 'H';
    buffFreqToPrint[1]  = 'z';
    buffFreqToPrint[2]  = ' ';
    u8x8.drawString(0, 3, buffFreqToPrint);
    //u8x8.print(buffFreqToPrint);
  
    ///se imprime la frecuencia del CANAL 3
    formatLongRightAlign(buffFreqToPrint,v_FrequencyMeasured_CH3);    
    buffFreqToPrint[0]  = 'H';
    buffFreqToPrint[1]  = 'z';
    buffFreqToPrint[2]  = ' ';
    u8x8.drawString(0, 5, buffFreqToPrint);


    delay(1000);
}





//void formatLongRightAlign (char* ref_str_result,  unsigned long a_ul_a_mostrar){
void formatLongRightAlign (char* ref_str_result,  uint32_t a_ul_a_mostrar){


    byte len_result = strlen(ref_str_result);
    for(int i = 0; i < len_result; i++) {
        ref_str_result[i] = ' '; //SE RELLENA el buffer recibido CON ESPACIOS EN BLANCO
    }

    char arrchar_itoa[12];
    ultoa(a_ul_a_mostrar, arrchar_itoa, 10);

    int i_result = len_result - 1;
 
    for (int arr_loop = strlen(arrchar_itoa)-1; arr_loop >=0 ; arr_loop--) {
         ref_str_result[i_result] = arrchar_itoa[arr_loop];
         i_result--;
    }
}///formatLongRightAlign

dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by dannyf »

for your capture callback function, may want to think about how to make it a little bit faster and more atomic.

would suggest the following:
1. atomicity: the first thing to do in the isr is to save the overflow counter value;
2. no multiplification: in the overflow counter, advance the counter by 0x10000ul;
3. no division: in the isr, just calculate the number of ticks passed, and set a flag so you process that tick in the main loop.
julian_lp
Posts: 8
Joined: Sun Jul 09, 2023 10:23 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by julian_lp »

dannyf wrote: Wed Aug 02, 2023 8:31 pm would suggest the following:
1. atomicity: the first thing to do in the isr is to save the overflow counter value;
Can the interrupt itself be interrupted? :roll:
You mean that overflow count could change while proccessing the code inside capture isr?

dannyf wrote: Wed Aug 02, 2023 8:31 pm 2. no multiplification: in the overflow counter, advance the counter by 0x10000ul;
3. no division: in the isr, just calculate the number of ticks passed, and set a flag so you process that tick in the main loop.
Clever ;)
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Bluepill: 2 different timers to read 2 different FREQS

Post by dannyf »

higher priority isrs can interrupt lower priority isrs.
Post Reply

Return to “General discussion”