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...
Bluepill: 2 different timers to read 2 different FREQS
Re: Bluepill: 2 different timers to read 2 different FREQS
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.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
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.
Re: Bluepill: 2 different timers to read 2 different FREQS
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.Interesting option given the stm32 cpu clock.
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.
works on the atmega as well.Not very doable/recommended in the 328 version tough
Re: Bluepill: 2 different timers to read 2 different FREQS
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
Now,
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
(Note that both pins share the timer1 in bluepill)
would instance and instance_2 be using the same timer? is that the correct way to instantiate two channels or "instance_2" in unnecesary?
(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
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);
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
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);
Actually dont know anything about what DWT is, so I'll have to keep reading ...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.
Re: Bluepill: 2 different timers to read 2 different FREQS
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
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
Re: Bluepill: 2 different timers to read 2 different FREQS
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.
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.
Re: Bluepill: 2 different timers to read 2 different FREQS
Can the interrupt itself be interrupted?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;

You mean that overflow count could change while proccessing the code inside capture isr?
Cleverdannyf 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.

Re: Bluepill: 2 different timers to read 2 different FREQS
higher priority isrs can interrupt lower priority isrs.