[Solved] Input Capture Hardware Timer example with Interrupt

MGeo
Thu Oct 04, 2018 9:26 am
Hi,

I’m trying to put together a minimum Input Capture hardware timer example using Interrupts to measure an input pulse stream (single pulse stream for now, with 6 total inputs to eventually be captured for my intended application.

I’ll document my efforts here in this thread.

George

Background
I’m hoping to gain enough understanding of this core and STM32 timer control to port this flight controller application (http://www.brokking.net/ymfc-32_main.html). This was originally developed around a slightly earlier version of Roger’s libmaple based STM32duino core (http://www.brokking.net/YMFC-32_downloads.html). It makes use some lower level bit manipulation of TIM2-4 for interrupt based input capture of pulse inputs as well as hardware PWM control of outputs. The application uses TIM2 and TIM3 (input capture of pilot commands from an RC receiver) and also TIM4 (PWM outputs to quadcopter motor controllers).

Typical input capture interrupt handler (toggles the input capture edge direction within interrupt to get enough input channels (6 pulse capture inputs are needed):
void handler_channel_1(void) { //This function is called when channel 1 is captured.
if (0b1 & GPIOA_BASE->IDR >> 0) { //If the receiver channel 1 input pulse on A0 is high.
channel_1_start = TIMER2_BASE->CCR1; //Record the start time of the pulse.
TIMER2_BASE->CCER |= TIMER_CCER_CC1P; //Change the input capture mode to the falling edge of the pulse.
}
else { //If the receiver channel 1 input pulse on A0 is low.
channel_1 = TIMER2_BASE->CCR1 - channel_1_start; //Calculate the total pulse time.
if (channel_1 < 0)channel_1 += 0xFFFF; //If the timer has rolled over a correction is needed.
TIMER2_BASE->CCER &= ~TIMER_CCER_CC1P; //Change the input capture mode to the rising edge of the pulse.
}
}


MGeo
Fri Oct 05, 2018 9:53 am
My test setup:
Arduino IDE 1.85, running on Win 10 laptop.
STM32 Arduino “Official” core V1.3.0, installed per https://github.com/stm32duino/wiki/wiki/Getting-Started
Nucleo-F103RB board, ST-Link replaced by J-Link firmware (https://www.segger.com/products/debug-p … -on-board/)
Segger Ozone debugger (https://www.segger.com/products/develop … -debugger/)
Saleae Logic analyzer (https://www.saleae.com/)
Some jumper wires

Also:
Source compiled using “Debug (-g)” option

First up is to generate a periodic pulse waveform on the Nucleo that I can feed in as input into a Timer Input Capture pin using a jumper wire. I’ll use the analogWrite function for that purpose. Below is the test sketch and its output as measured on the logic analyzer. The pulse output looks as expected, a 1KHz pulse stream with a 25% positive PWM (= 63/255).

const int testPwmOutputPin = PA0; // PA_0,D46/A0 <<-- works
//const int testPwmOutputPin = A0; // PA_0,D46/A0 <<-- works
//const int testPwmOutputPin = PA_0; // PA_0,D46/A0 <<-- does not work

void setup() {

Serial.begin(115200);
Serial.println("Initializing...");

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(testPwmOutputPin, OUTPUT);

analogWrite(testPwmOutputPin, 63);

}

void loop() {

digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second

}


MGeo
Sat Oct 06, 2018 10:08 am
Incidentially, in my simple output example above I attempted to understand the pin name aliases by searching through the files. I found that for analog output 0 for my Nucleo-F103RB variant should be PA_0 per https://github.com/stm32duino/Arduino_C … nt.cpp#L78

But testing revealed that A0 works, PA0 also works, but PA_0 compiles fine but I get no PWM output. Is this to be expected? It seems to not line up with the example I was using from here (https://www.stm32duino.com/viewtopic.php?p=34555#p34555).

See previous example code definitions.

const int testPwmOutputPin = PA0; // PA_0,D46/A0 <<-- works
//const int testPwmOutputPin = A0; // PA_0,D46/A0 <<-- works
//const int testPwmOutputPin = PA_0; // PA_0,D46/A0 <<-- does not work


MGeo
Sat Oct 06, 2018 10:34 am
Tracing through my simple example in Ozone debugger, I can see that analogWrite to A0 is utilizing TIM2 as expected since PA0 pin is default TIM2_CH1 in the STM32F103 datasheet.

So for my input capture examples I will target TIM3 and TIM4 and hope that there are no other behind the scenes conflicting use of these two hardware timers.

Thanks,
George

Image


MGeo
Sat Oct 06, 2018 11:04 am
In STM32CubeMX tool we can quickly find that TIM3_CH1 maps to PA6 by default, so I will connect a jumper from my known good 1Khz / 25% duty cycle PWM output on PA0/A0 to PA6 on the Nucleo board.

Here is a pic to show what this looks like in STM32CubeMX:
Image

This post (https://www.stm32duino.com/viewtopic.php?p=44629#p44629) tells me that HAL are available at sketch level, and that some HAL driver are enabled by default. The HAL conf is available in the variant. For example for Nucleo F103RB is at https://github.com/stm32duino/Arduino_C … hal_conf.h In this file I can see that TIM has been enabled for my board with #define HAL_TIM_MODULE_ENABLED so I think I am good to go with TIM3/TIM4. I expected this but good to know how to confirm.

IMPORTANT TO REMEMBER FOR FUTURE USE –
If the HAL module you need is not enabled, one can create a “build_opt.h” file at sketch level then add the module you want enabled:

“-DHAL_CRC_MODULE_ENABLED -DHAL_XXX_MODULE_ENABLED …” etc.


Rick Kimball
Sat Oct 06, 2018 12:10 pm
[MGeo – Sat Oct 06, 2018 10:34 am] –
If anyone knows a useful work-around please advise.

use something like imgur.com or flickr.com or tinypic.com


fpiSTM
Sat Oct 06, 2018 4:48 pm
PA_0 is a pin name not a pin number.
PA0 is not equal to PA_0.

This compile using PA_0 because it’s also an uint32_t as requested by analogWrite( uint32_t ulPin, uint32_t ulValue ) ;


MGeo
Sat Oct 06, 2018 10:00 pm
Great thanks for the inputs all. It is clear that I need to be aware of what timers are in use by a Arduino functions such as analogWrite if I want grab a second TIM for my own HAL use as input capture.

My test sketch (per above) will have a background heartbeat LED blink functionality using the delay() function. I’m hoping that delay uses SysTick as system timer and does not use one of the F103RB timers (TIM1, TIM2, TIM3, TIM4).

George


MGeo
Sun Oct 07, 2018 9:48 am
From UM1725, User Manual, Description of STM32F4 HAL and LL drivers
https://www.st.com/content/ccc/resource … 105879.pdf

If I’m understanding things correctly, I think I need to do the steps highlighted below in blue on TIM3 to be able to capture the TIM2 generated pulse stream I’ve generated on PA0 using analogWrite() as shown up above. I’m not sure about “ Configure these TIM pins in Alternate function mode using HAL_GPIO_Init()” and how I need to do this on an F1.

65.2 TIM Firmware driver API description

65.2.2 How to use this driver
1. Initialize the TIM low level resources by implementing the following functions
depending from feature used :

 Time Base : HAL_TIM_Base_MspInit()
 Input Capture : HAL_TIM_IC_MspInit()
 Output Compare : HAL_TIM_OC_MspInit()
 PWM generation : HAL_TIM_PWM_MspInit()
 One-pulse mode output : HAL_TIM_OnePulse_MspInit()
 Encoder mode output : HAL_TIM_Encoder_MspInit()
2. Initialize the TIM low level resources :
a. Enable the TIM interface clock using __TIMx_CLK_ENABLE();
b. TIM pins configuration
 Enable the clock for the TIM GPIOs using the following function:
__GPIOx_CLK_ENABLE();
 Configure these TIM pins in Alternate function mode using HAL_GPIO_Init();
3. The external Clock can be configured, if needed (the default clock is the internal clock
from the APBx), using the following function: HAL_TIM_ConfigClockSource, the clock
configuration should be done before any start function.
4. Configure the TIM in the desired functioning mode using one of the initialization
function of this driver:
 HAL_TIM_Base_Init: to use the Timer to generate a simple time base
 HAL_TIM_OC_Init and HAL_TIM_OC_ConfigChannel: to use the Timer to
generate an Output Compare signal.
 HAL_TIM_PWM_Init and HAL_TIM_PWM_ConfigChannel: to use the Timer to
generate a PWM signal.
 HAL_TIM_IC_Init and HAL_TIM_IC_ConfigChannel: to use the Timer to measure
an external signal.

 HAL_TIM_OnePulse_Init and HAL_TIM_OnePulse_ConfigChannel: to use the
Timer in One Pulse Mode.
 HAL_TIM_Encoder_Init: to use the Timer Encoder Interface.
5. Activate the TIM peripheral using one of the start functions depending from the feature
used:

 Time Base : HAL_TIM_Base_Start(), HAL_TIM_Base_Start_DMA(),
HAL_TIM_Base_Start_IT()
 Input Capture : HAL_TIM_IC_Start(), HAL_TIM_IC_Start_DMA(),
HAL_TIM_IC_Start_IT()
 Output Compare : HAL_TIM_OC_Start(), HAL_TIM_OC_Start_DMA(),
HAL_TIM_OC_Start_IT()
 PWM generation : HAL_TIM_PWM_Start(), HAL_TIM_PWM_Start_DMA(),
HAL_TIM_PWM_Start_IT()
 One-pulse mode output : HAL_TIM_OnePulse_Start(),
HAL_TIM_OnePulse_Start_IT()
 Encoder mode output : HAL_TIM_Encoder_Start(),
HAL_TIM_Encoder_Start_DMA(), HAL_TIM_Encoder_Start_IT().
6. The DMA Burst is managed with the two following functions:
HAL_TIM_DMABurst_WriteStart() HAL_TIM_DMABurst_ReadStart()


MGeo
Sun Oct 07, 2018 2:12 pm
I went off looking for Input Capture HAL examples, the CubeMX F1 package comes with an example /Projects/STM3210E_EVAL/Examples/TIM/TIM_Input_Capture that I studied and have attempted to employ here.

I put together a minimal test sketch below.

TIM3
I started with use of STM Core timer.c based functions to initialize TIM3, stealing code from an earlier hardware timer LED blink example elsewhere on this forum (https://www.stm32duino.com/viewtopic.php?p=49394#p49394).

Next I used the CubeMX F1 TIM_Input_Capture example as a guide for initialization of TIM4. I think I’m doing something wrong with how I am defining TIM4. I dropped in a TIM3/TIM4 register dump Serial.print function, I can see that TIM3 gets properly initialized, but nothing happens with TIM4. Same is true stepping through the code with Ozone debugger.

At this point I’m scratching my head as to why, so I’m posting what I have so far seeking any advice.

Thanks,
George

Minimal test sketch:
#define TIMER_PERIOD TIM3
#define TIMER_INPUT_CAPTURE TIM4

// Handle for stimer
static stimer_t TIM_Handle;

// Timer handle declaration
static TIM_HandleTypeDef TimHandle;

// Timer Input Capture Configuration Structure declaration
TIM_IC_InitTypeDef sICConfig;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin = PA6;
const int TestOutputPin = D7; // PA_8,D7

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);
digitalWrite(TestOutputPin, !digitalRead(LED_BUILTIN));
}

void setup() {

Serial.begin(115200);
Serial.println("Initializing...");
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

// ------- Period TIM3 Setup -------------------------

// Set Period timer TIM instance
TIM_Handle.timer = TIM3;
// Period timer set to 10ms
TimerHandleInit(&TIM_Handle, 10000 - 1, ((uint32_t)(getTimerClkFreq(TIM3) / (1000000)) - 1));
//attachIntHandle(&TIM_Handle, blink);

Print_TIM3_Regs();

// ------- Input Capture TIM4 Setup -------------------------

// Set Input Capture TIM instance
TimHandle.Instance = TIM4;

// Initialize TIM peripheral (prescale of (64-1) and 64MHz clock gives 1us tick)
TimHandle.Init.Period = 0xFFFF;
TimHandle.Init.Prescaler = (64 - 1);
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if(HAL_TIM_IC_Init(&TimHandle) != HAL_OK)
_Error_Handler("HAL TIM IC Init error", 1);

// Configure the Input Capture of channel 1
sICConfig.ICPolarity = TIM_ICPOLARITY_RISING;
sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
sICConfig.ICPrescaler = TIM_ICPSC_DIV1;
sICConfig.ICFilter = 0;
if(HAL_TIM_IC_ConfigChannel(&TimHandle, &sICConfig, TIM_CHANNEL_1) != HAL_OK)
_Error_Handler("HAL TIM IC Channel Config error", 2);

// Start the Input Capture in interrupt mode
if(HAL_TIM_IC_Start_IT(&TimHandle, TIM_CHANNEL_2) != HAL_OK)
_Error_Handler("HAL TIM_IC_Start_IT error", 3);

Print_TIM4_Regs();

}

void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void Print_TIM4_Regs() {
Serial.print("TIM4->CR1: "); Serial.println(TIM4->CR1, HEX);
Serial.print("TIM4->CR2: "); Serial.println(TIM4->CR2, HEX);
Serial.print("TIM4->DIER: "); Serial.println(TIM4->DIER, HEX);
Serial.print("TIM4->SR: "); Serial.println(TIM4->SR, HEX);
Serial.print("TIM4->CNT: "); Serial.println(TIM4->CNT, HEX);
Serial.print("TIM4->PSC: "); Serial.println(TIM4->PSC, HEX);
Serial.print("TIM4->ARR: "); Serial.println(TIM4->ARR, HEX);
while(1) {}
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


Rick Kimball
Sun Oct 07, 2018 3:22 pm
Maybe you are fighting with the SPI feature. Why don’t you capture on PA0 and use some other pin to generate the PWM.

This is some code that uses just the raw registers … it won’t compile for you but it will give you and idea what the minimum setup is requried for PA0 and TIM2_CH1.
/*
fabooh - TIM2CH1 input capture from PA0
*/

serial_default_t<115200, F_CPU, TX_PIN, RX_PIN> Serial;

volatile uint16_t chan1;

PA_0 PA0;

static void input_timer_setup()
{

pinMode(PA0, INPUT_PULLDOWN);

RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
NVIC_EnableIRQ(TIM2_IRQn); // Enable TIM2 IRQ

TIM2->CR1 = 0; // disable TIM2, required to configure
TIM2->CR2 = 0;
TIM2->DCR = 0;
TIM2->EGR = 0;
TIM2->SMCR = 0;
TIM2->CCMR1 = // CC1 channel is configured as input, IC1 is mapped on TI1.
TIM_CCMR1_CC1S_0
// digital filter 8 CLK_INT samples required to trigger
| (0b011 << TIM_CCMR1_IC1F_Pos)
;
TIM2->CCMR2 = 0;
TIM2->CCER = TIM_CCER_CC1E; // input capture enable for CCR1 rising
TIM2->PSC = (F_CPU/1000000)-1; // make each TIM2 CNT tick is 1 microsecond
TIM2->ARR = 0xFFFF; // count from 0 - 65535, then wrap
TIM2->DIER = TIM_DIER_CC1IE; // interrupt enable on CCR1 input
TIM2->CR1 = TIM_CR1_CEN; // enable TIM2
}

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

input_timer_setup();

delay(1);
}

volatile unsigned new_capture=0;

void loop() {
delay(100);
if ( new_capture ) {
Serial << millis() << "," << chan1 << endl;
new_capture=0;
}
}

static void handle_tim2ch1_inputcapture(void) {
static uint16_t chan1_start;

// if PA0 is high, grab start time & setup falling capture
// in either case, we have to flip capture bit to time next edge
if ( GPIOA->IDR & 0x1 ) {
chan1_start = TIM2->CCR1;
TIM2->CCER |= TIM_CCER_CC1P; // capture on falling edge
}
else {
new_capture=1;
chan1 = TIM2->CCR1 - chan1_start;
TIM2->CCER &= ~TIM_CCER_CC1P; // capture on rising edge
}
// NOTE: reading CCR1 clears the TIM2->SR CC1IF flag
// that means we don't have to clear it explicitly
}

// override the default TIM2 interrupt handler
// and look and handle TIM2CH1 input capture events
extern "C" void TIM2_IRQHandler()
{
if ( TIM2->SR & TIM_SR_CC1IF ) {
handle_tim2ch1_inputcapture();
}
}


MGeo
Mon Oct 08, 2018 1:06 am
Good progress tonight. In an attempt to validate my TIM config approach, I used STM32CubeMX to set up a project based on Nucleo-F103RB, configuring TIM3 for input capture on Channel 1. I copied the project config code into my test sketch.

I now am able to get the TIM3 register values to update. I’m still not quite clear what I had wrong, I think I failed to read the HAL manual close enough and was failing to activate the TIM peripheral correctly, so any register change attempts failed to update the hardware.

At any rate I’m able to enable TIM3 in capture mode. I set a breakpoint in my HAL_TIM_IC_CaptureCallback() in Ozone, it now looks to be firing! Next I’ll hook the logic analyzer up and see if things are firing as expected (remembering that PWM out on PA0 is jumpered to PA6 / TIM3_CH1).

George

STM32CubeMX project config (right click on image and open in new tab makes it easier to see):
Image

Test sketch:
static TIM_HandleTypeDef htim3;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin = PA6;
const int TestOutputPin = D15;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin));
}

void setup() {

Serial.begin(115200);
Serial.println("Initializing...");
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM2_Regs();

// ------- Input Capture TIM3 Setup -------------------------

// TIM3_IRQn interrupt configuration
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;

htim3.Instance = TIM3;
htim3.Init.Prescaler = (64 - 1);
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

TIM3->CR1 = TIM_CR1_CEN;
TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;

Print_TIM3_Regs();

}

void loop() {
Serial.println("loop");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

void Print_TIM2_Regs() {
Serial.print("TIM2->CR1: "); Serial.println(TIM2->CR1, HEX);
Serial.print("TIM2->CR2: "); Serial.println(TIM2->CR2, HEX);
Serial.print("TIM2->DIER: "); Serial.println(TIM2->DIER, HEX);
Serial.print("TIM2->SR: "); Serial.println(TIM2->SR, HEX);
Serial.print("TIM2->CNT: "); Serial.println(TIM2->CNT, HEX);
Serial.print("TIM2->PSC: "); Serial.println(TIM2->PSC, HEX);
Serial.print("TIM2->ARR: "); Serial.println(TIM2->ARR, HEX);
Serial.println();
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


MGeo
Mon Oct 08, 2018 9:23 am
You can see in the code above that I am toggling a digital output pin in the interrupt handler to change the pin’s state every time the handler fires. So if we hook the logic analyzer up to pin PA6 (PWM pulse stream input to TIM3_CH1) and D15 (digital output toggled by interrupt handler) we should be able to see if things work as we expect. This would be D15 changing state on every rising edge of PA6. In the attached capture you can see this is exactly what we see.

White Trace: Pin PA6 = PWM pulse stream as input into TIM3_CH1
Orange Trace: Pin D15 = digital output toggled by interrupt handler
Image


MGeo
Fri Oct 12, 2018 8:56 am
The program I’m hoping to port over measures the pulse width on an input pin using a single TIM channel by flipping the CC1P bit within TIMx CCER register inside the interrupt handler. I’ve added this logic to the test program and can now successfully measure the pulse width, outputting the result to serial terminal output.

#define HAL_WAY 0

static TIM_HandleTypeDef htim3;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin = PA6;
const int TestOutputPin = D15;

volatile int32_t channel_1_start;
volatile int32_t channel_1;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)) {
// if (0b1 & GPIOA->IDR >> 6) { // If the receiver channel 1 input pulse on A6 is high.
channel_1_start = TIM3->CCR1; // Record the start time of the pulse.
TIM3->CCER |= TIM_CCER_CC1P; // Change the input capture mode to the falling edge of the pulse.
}
else { // If the receiver channel 1 input pulse on A0 is low.
channel_1 = TIM3->CCR1 - channel_1_start; // Calculate the total pulse time.
if (channel_1 < 0)channel_1 += 0xFFFF; // If the timer has rolled over a correction is needed.
TIM3->CCER &= ~TIM_CCER_CC1P; // Change the input capture mode to the rising edge of the pulse.
}
//digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // the HAL way (also works)
}

void setup() {

Serial.begin(115200);
Serial.println("Initializing...");
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM2_Regs();

// ------- Input Capture TIM3 Setup -------------------------

// TIM3_IRQn interrupt configuration
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;

htim3.Instance = TIM3;
htim3.Init.Prescaler = (64 - 1);
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
//sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

TIM3->CR1 = TIM_CR1_CEN;
TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
//TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P;

Print_TIM3_Regs();

}

void loop() {
float ch1_msec = ((float) channel_1 / 1000.0);
Serial.print("channel_1(mSec): "); Serial.println(ch1_msec);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
}

void Print_TIM2_Regs() {
Serial.print("TIM2->CR1: "); Serial.println(TIM2->CR1, HEX);
Serial.print("TIM2->CR2: "); Serial.println(TIM2->CR2, HEX);
Serial.print("TIM2->DIER: "); Serial.println(TIM2->DIER, HEX);
Serial.print("TIM2->SR: "); Serial.println(TIM2->SR, HEX);
Serial.print("TIM2->CNT: "); Serial.println(TIM2->CNT, HEX);
Serial.print("TIM2->PSC: "); Serial.println(TIM2->PSC, HEX);
Serial.print("TIM2->ARR: "); Serial.println(TIM2->ARR, HEX);
Serial.println();
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


MGeo
Fri Oct 12, 2018 10:28 am
Next and maybe final task is to move this example over to F3 to verify portability of Input Capture with Interrupt example across families. I wanted to port a math intensive application over to this HAL based core so I can methodically move from F1 variants over to F3/F4 variants that have FPU.

I’m using a Nucleo-F302R8 I have available. First make sure I can generate a PWM output on a pin with a hardware timer like we did on F1. I needed to move Serial to Serial2 to get the serial monitor working. I’ve stared at variant.h for F302R8 vs F103RB but it is not yet obvious to me why that is, but it works so moving on. I’ll use PB8 to generate the pwm output, which looks to map to TIM16_CH1 (TIM16, TIM17 are single channel only on ‘F302).

Simple test sketch for F302R8
// From PeripheralPins.c
// {PB_8, TIM16, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM16, 1, 0)}, // TIM16_CH1
const int testPwmOutputPin = PB8; // PB_8 = D15

#define MySerial Serial2

void setup() {

MySerial.begin(115200);
MySerial.println("Initializing...");

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(testPwmOutputPin, OUTPUT);

analogWrite(testPwmOutputPin, 63);

}

void loop() {

MySerial.println("loop");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500); // wait for a second

}


MGeo
Fri Oct 19, 2018 7:27 am
My Nucleo-F303RE arrived so I’ve switched my example over to F303RE.

It took me some time to weed through the HAL, datasheet and reference manual info to move my example from F103RB to F303RE, more work than I would have had hoped. After a good bit of reading and use of CubeMX tool to understand differences and key initialization steps, I finally have my example working on Nucleo-F303RE demo board with a jumper from PWM output pin to a TIM input capture pin. I’ve learned that F1 handles alternate pin function assignment in a different manner than the rest of the STM32 family, also that F303 runs at 72MHz instead of 64MHz, etc. But the basic structure of the F1 example is more or less intact, things are now working with the code below.

So I think at this point I’ve now accomplished my learning goal. I probably have some duplicate initialization steps in my code that the core might also be duplicating, and there is some work that could be done by the core’s timer.c functions. At this point I’ll declare this one [SOLVED] and will move on to porting my application.

Some helpful links for future reference:
STM Core HardwareTimer under construction – https://github.com/stm32duino/Arduino_C … issues/146
HAL vs LL vs SPL – https://blog.domski.pl/spl-vs-hal-which … ry-part-2/

George

Arduino test sketch for Nucleo-F303RE:
#define HAL_WAY 0

// {PB_5, TIM17, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_TIM17, 1, 0)}, // TIM17_CH1

static TIM_HandleTypeDef htim17;

// From PeripheralPins.c
const int PwmOutputPin = PB8; // PB_8 = D15, uses TIM16_CH1
const int CaptureInputPin = PB5; // D4, also TIM17_CH1 Alternate Function
const int TestOutputPin = PA7; // D11

volatile int32_t channel_1_start;
volatile int32_t channel_1;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);

if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)) {
// if (0b1 & GPIOB->IDR >> 5) { // If the receiver channel 1 input pulse on PB5 is high.
channel_1_start = TIM17->CCR1; // Record the start time of the pulse.
TIM17->CCER |= TIM_CCER_CC1P; // Change the input capture mode to the falling edge of the pulse.
}
else { // If the receiver channel 1 input pulse on PB5 is low.
channel_1 = TIM17->CCR1 - channel_1_start; // Calculate the total pulse time.
if (channel_1 < 0)channel_1 += 0xFFFF; // If the timer has rolled over a correction is needed.
TIM17->CCER &= ~TIM_CCER_CC1P; // Change the input capture mode to the rising edge of the pulse.
}
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
//HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_15); // the HAL way (also works)

}

void setup() {

Serial.begin(115200);
Serial.print("Initializing...");
Serial.println(getTimerClkFreq(TIM1));
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM16_Regs();

// ------- Input Capture TIM17 Setup -------------------------

RCC_PeriphCLKInitTypeDef PeriphClkInit;
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_TIM17;
PeriphClkInit.Tim17ClockSelection = RCC_TIM17CLK_HCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

GPIO_InitTypeDef GPIO_InitStruct;

// Peripheral clock enable
__HAL_RCC_TIM17_CLK_ENABLE();

// TIM17 GPIO Configuration
// PB5 --> TIM17_CH1
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF10_TIM17;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

// TIM17 interrupt Init
HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn);

TIM_IC_InitTypeDef sConfigIC;

htim17.Instance = TIM17;
htim17.Init.Prescaler = (72 - 1);
htim17.Init.CounterMode = TIM_COUNTERMODE_UP;
htim17.Init.Period = 0xFFFF;
htim17.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim17.Init.RepetitionCounter = 0;
htim17.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim17) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim17) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim17, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

TIM17->CR1 = TIM_CR1_CEN;
TIM17->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
TIM17->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
//TIM17->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P;

Print_TIM17_Regs();

}

void loop() {
float ch1_msec = ((float) channel_1 / 1000.0);
Serial.print("channel_1(mSec): "); Serial.println(ch1_msec);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
}

void Print_TIM17_Regs() {
Serial.print("TIM17->CR1: "); Serial.println(TIM17->CR1, HEX);
Serial.print("TIM17->CR2: "); Serial.println(TIM17->CR2, HEX);
Serial.print("TIM17->DIER: "); Serial.println(TIM17->DIER, HEX);
Serial.print("TIM17->SR: "); Serial.println(TIM17->SR, HEX);
Serial.print("TIM17->CNT: "); Serial.println(TIM17->CNT, HEX);
Serial.print("TIM17->PSC: "); Serial.println(TIM17->PSC, HEX);
Serial.print("TIM17->ARR: "); Serial.println(TIM17->ARR, HEX);
Serial.println();
}

void Print_TIM16_Regs() {
Serial.print("TIM16->CR1: "); Serial.println(TIM16->CR1, HEX);
Serial.print("TIM16->CR2: "); Serial.println(TIM16->CR2, HEX);
Serial.print("TIM16->DIER: "); Serial.println(TIM16->DIER, HEX);
Serial.print("TIM16->SR: "); Serial.println(TIM16->SR, HEX);
Serial.print("TIM16->CNT: "); Serial.println(TIM16->CNT, HEX);
Serial.print("TIM16->PSC: "); Serial.println(TIM16->PSC, HEX);
Serial.print("TIM16->ARR: "); Serial.println(TIM16->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


MGeo
Wed Oct 24, 2018 9:27 am
I went back to the Nucleo-F103RB and clean up some of the direct register bit manipulation I was doing, moving it over to HAL functions in keeping with the HAL way. It took some searching of the HAL code but eventually got it working.

I also chased down the call stack that finally calls my capture interrupt handler in my sketch. When TIM3 CC hw interrupt fires we go through:

TIM3_IRQHandler() (timer.c:1075) –>
HAL_TIM_IRQHandler() (stm32f1xx_hal_tim.c:2784) –>
HAL_TIM_IC_CaptureCallback() (my sketch ino)

You can see the call stack in the middle right panel of the Ozone screen cap below (right click the image into a new browser tab to see it better).

I’m finding it difficult to know what combinations of HAL commands do what and how to verify that it is doing what I expect without staring at the HAL code plus the data sheets and reference manuals. This would seem to defeat the purpose of a hardware abstraction layer. I may explore LL functions as they appear to be more direct and verifiable.

George

Image

#define HAL_WAY

static TIM_HandleTypeDef htim3;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin = PA6;
const int TestOutputPin = D15;

volatile int32_t channel_1_start;
volatile int32_t channel_1;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)) {
// if (0b1 & GPIOA->IDR >> 6) { // If the receiver channel 1 input pulse on A6 is high.
channel_1_start = TIM3->CCR1; // Record the start time of the pulse.
TIM3->CCER |= TIM_CCER_CC1P; // Change the input capture mode to the falling edge of the pulse.
}
else { // If the receiver channel 1 input pulse on A0 is low.
channel_1 = TIM3->CCR1 - channel_1_start; // Calculate the total pulse time.
if (channel_1 < 0)channel_1 += 0xFFFF; // If the timer has rolled over a correction is needed.
TIM3->CCER &= ~TIM_CCER_CC1P; // Change the input capture mode to the rising edge of the pulse.
}
#ifdef HAL_WAY
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // the HAL way (also works)
#else
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
#endif
}

void setup() {

Serial.begin(115200);
Serial.print("Initializing...");
Serial.println(getTimerClkFreq(TIM1));
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM2_Regs();

// ------- Input Capture TIM3 Setup -------------------------

GPIO_InitTypeDef GPIO_InitStruct;

/*
// TIM3 GPIO Configuration
// PB4 -> TIM3_CH1
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

__HAL_AFIO_REMAP_TIM3_PARTIAL();
*/

// TIM3 interrupt Init
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;

htim3.Instance = TIM3;
htim3.Init.Prescaler = (64 - 1);
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

#ifdef HAL_WAY
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1 | TIM_IT_CC2);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_1);
#else
TIM3->CR1 = TIM_CR1_CEN;
TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
//TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P;
#endif

Print_TIM3_Regs();

}

void loop() {
float ch1_msec = ((float) channel_1 / 1000.0);
Serial.print("channel_1(mSec): "); Serial.println(ch1_msec);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
}

void Print_TIM2_Regs() {
Serial.print("TIM2->CR1: "); Serial.println(TIM2->CR1, HEX);
Serial.print("TIM2->CR2: "); Serial.println(TIM2->CR2, HEX);
Serial.print("TIM2->DIER: "); Serial.println(TIM2->DIER, HEX);
Serial.print("TIM2->CCER: "); Serial.println(TIM2->CCER, HEX);
Serial.print("TIM2->SR: "); Serial.println(TIM2->SR, HEX);
Serial.print("TIM2->CNT: "); Serial.println(TIM2->CNT, HEX);
Serial.print("TIM2->PSC: "); Serial.println(TIM2->PSC, HEX);
Serial.print("TIM2->ARR: "); Serial.println(TIM2->ARR, HEX);
Serial.println();
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->CCER: "); Serial.println(TIM3->CCER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


MGeo
Sat Oct 27, 2018 12:03 pm
Successful remap of TIM3 Channel 1 input from default PA6 to alternate PB4 using __HAL_AFIO_REMAP_TIM3_PARTIAL():

#define HAL_WAY

static TIM_HandleTypeDef htim3;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin = PB4;
const int TestOutputPin = D15;

volatile int32_t channel_1_start;
volatile int32_t channel_1;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
UNUSED(htim);
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)) {
// if (0b1 & GPIOA->IDR >> 6) { // If the receiver channel 1 input pulse on A6 is high.
channel_1_start = TIM3->CCR1; // Record the start time of the pulse.
TIM3->CCER |= TIM_CCER_CC1P; // Change the input capture mode to the falling edge of the pulse.
}
else { // If the receiver channel 1 input pulse on A0 is low.
channel_1 = TIM3->CCR1 - channel_1_start; // Calculate the total pulse time.
if (channel_1 < 0)channel_1 += 0xFFFF; // If the timer has rolled over a correction is needed.
TIM3->CCER &= ~TIM_CCER_CC1P; // Change the input capture mode to the rising edge of the pulse.
}
#ifdef HAL_WAY
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // the HAL way (also works)
#else
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
#endif
}

void setup() {

Serial.begin(115200);
Serial.print("Initializing...");
Serial.println(getTimerClkFreq(TIM1));
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pin
// ??? --- I'm not sure if this is enough, do I need to configure my input pin as an 'Input Capture' alternate function --- ???
pinMode(CaptureInputPin, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM2_Regs();

// ------- Input Capture TIM3 Setup -------------------------

GPIO_InitTypeDef GPIO_InitStruct;

/*
// TIM3 GPIO Configuration
// PB4 -> TIM3_CH1
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
*/
__HAL_AFIO_REMAP_TIM3_PARTIAL();

// TIM3 interrupt Init
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;

htim3.Instance = TIM3;
htim3.Init.Prescaler = (64 - 1);
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

#ifdef HAL_WAY
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1 | TIM_IT_CC2);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_1);
#else
TIM3->CR1 = TIM_CR1_CEN;
TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
//TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P;
#endif

Print_TIM3_Regs();

}

void loop() {
float ch1_msec = ((float) channel_1 / 1000.0);
Serial.print("channel_1(mSec): "); Serial.println(ch1_msec);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
}

void Print_TIM2_Regs() {
Serial.print("TIM2->CR1: "); Serial.println(TIM2->CR1, HEX);
Serial.print("TIM2->CR2: "); Serial.println(TIM2->CR2, HEX);
Serial.print("TIM2->DIER: "); Serial.println(TIM2->DIER, HEX);
Serial.print("TIM2->CCER: "); Serial.println(TIM2->CCER, HEX);
Serial.print("TIM2->SR: "); Serial.println(TIM2->SR, HEX);
Serial.print("TIM2->CNT: "); Serial.println(TIM2->CNT, HEX);
Serial.print("TIM2->PSC: "); Serial.println(TIM2->PSC, HEX);
Serial.print("TIM2->ARR: "); Serial.println(TIM2->ARR, HEX);
Serial.println();
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->CCER: "); Serial.println(TIM3->CCER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


MGeo
Sat Oct 27, 2018 8:39 pm
Next on my list is a four input channel example on a single TIM on an F103RB.

I dug out an old homebrew AVR ATTiny84 based RC pulse simulator, the AVR device is programmed to generate 6 separate sequential 1-2mSec pulses every 20 mSec (50 Hz) to simulate an old-school RC receiver. Similar to the Channel 1-6 Servo Signal pulses below. I hooked the 6 channels of the simulator up to my Nucleo-F103RB board on TIM3 channel 1-4 and TIM4 channel 1-2 pins per the datasheet.

Studying HAL_TIM_IRQHandler I can see that the handler uses __HAL_TIM_GET_IT_SOURCE() to determine the TIM interrupt source. In the routine htim->Channel is used to save the active TIM channel before calling HAL_TIM_IC_CaptureCallback(), so I can use it in my channel pulse width calculations.

Image

Image

//#define HAL_WAY

/*
I.) ATMEL ATTINY84/ARDUINO ppm generator test rig pinout-------------------*

+--\/--+
VCC 1 | | 14 GND
CH1 (D0) PB0 2 | | 13 AREF (D10)
CH2 (D1) PB1 3 | | 12 PA1 (D9) SoftSerial Tx
PB3 4 | | 11 PA2 (D8) SoftSerial Rx
CH3 (D2) PB2 5 | | 10 PA3 (D7)
CH4 (D3) PA7 6 | | 9 PA4 (D6) CH6
(*PPMout)--> ppm (D4) PA6 7 | | 8 PA5 (D5) CH5
+------+

II.) STM32 Nucleo-F103RB default peripheral pins---------------------------*

PA0 = WKUP / USART2_CTS / ADC12_IN0 / TIM2_CH1_ETR
PA1 = USART2_RTS / ADC12_IN1 / TIM2_CH2
PA2 = USART2_TX(9) / ADC12_IN2 / TIM2_CH3
PA3 = USART2_RX / ADC12_IN3 / TIM2_CH4

PA6 = SPI1_MISO / ADC12_IN6 / TIM3_CH1
PA7 = SPI1_MOSI / ADC12_IN7 / TIM3_CH2
PB0 = ADC12_IN8 / TIM3_CH3
PB1 = ADC12_IN9 / TIM3_CH4

PB6 = I2C1_SCL / TIM4_CH1
PB7 = I2C1_SDA / TIM4_CH2
PB8 = TIM4_CH3
PB9 = TIM4_CH4

---------------------------------------------------------------------------*/

static TIM_HandleTypeDef htim3;
static TIM_HandleTypeDef htim4;

const int PwmOutputPin = PA0; // PA_0,D46/A0 -- USES TIM2
const int CaptureInputPin1 = PA6;
const int CaptureInputPin2 = PA7;
const int CaptureInputPin3 = PB0;
const int CaptureInputPin4 = PB1;
const int CaptureInputPin5 = PB6;
const int CaptureInputPin6 = PB7;
const int TestOutputPin = D15;

volatile int32_t channel_1_start, channel_1;
volatile int32_t channel_2_start, channel_2;
volatile int32_t channel_3_start, channel_3;
volatile int32_t channel_4_start, channel_4;
volatile int32_t channel_5_start, channel_5;
volatile int32_t channel_6_start, channel_6;

extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {

long address;
address = (long) htim->Instance;

switch (address)
{
case TIM3_BASE :
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
break;
case TIM4_BASE :
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
break;
default :
break;
}

if (htim->Instance == TIM3) {
switch(htim->Channel)
{
case HAL_TIM_ACTIVE_CHANNEL_1 :
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)) { // if the receiver channel 1 input pulse on A6 is high
channel_1_start = TIM3->CCR1; // record the start time of the pulse
TIM3->CCER |= TIM_CCER_CC1P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 1 input pulse on A6 is low
channel_1 = TIM3->CCR1 - channel_1_start; // calculate the total pulse time
if (channel_1 < 0)channel_1 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM3->CCER &= ~TIM_CCER_CC1P; // change the input capture mode to the rising edge of the pulse
}
break;
case HAL_TIM_ACTIVE_CHANNEL_2 :
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7)) { // if the receiver channel 2 input pulse on A7 is high
channel_2_start = TIM3->CCR2; // record the start time of the pulse
TIM3->CCER |= TIM_CCER_CC2P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 2 input pulse on A7 is low
channel_2 = TIM3->CCR2 - channel_2_start; // calculate the total pulse time
if (channel_2 < 0)channel_2 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM3->CCER &= ~TIM_CCER_CC2P; // change the input capture mode to the rising edge of the pulse
}
break;
case HAL_TIM_ACTIVE_CHANNEL_3 :
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)) { // if the receiver channel 3 input pulse on B0 is high
channel_3_start = TIM3->CCR3; // record the start time of the pulse
TIM3->CCER |= TIM_CCER_CC3P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 3 input pulse on B0 is low
channel_3 = TIM3->CCR3 - channel_3_start; // calculate the total pulse time
if (channel_3 < 0)channel_3 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM3->CCER &= ~TIM_CCER_CC3P; // change the input capture mode to the rising edge of the pulse
}
break;
case HAL_TIM_ACTIVE_CHANNEL_4 :
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)) { // if the receiver channel 4 input pulse on B1 is high
channel_4_start = TIM3->CCR4; // record the start time of the pulse
TIM3->CCER |= TIM_CCER_CC4P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 4 input pulse on B4 is low
channel_4 = TIM3->CCR4 - channel_4_start; // calculate the total pulse time
if (channel_4 < 0)channel_4 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM3->CCER &= ~TIM_CCER_CC4P; // change the input capture mode to the rising edge of the pulse
}
break;
default :
break;
}
} else if (htim->Instance == TIM4) {
switch(htim->Channel)
{
case HAL_TIM_ACTIVE_CHANNEL_1 :
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)) { // if the receiver channel 5 input pulse on B6 is high
channel_5_start = TIM4->CCR1; // record the start time of the pulse
TIM4->CCER |= TIM_CCER_CC1P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 5 input pulse on B6 is low
channel_5 = TIM4->CCR1 - channel_5_start; // calculate the total pulse time
if (channel_5 < 0)channel_5 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM4->CCER &= ~TIM_CCER_CC1P; // change the input capture mode to the rising edge of the pulse
}
break;
case HAL_TIM_ACTIVE_CHANNEL_2 :
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)) { // if the receiver channel 6 input pulse on B7 is high
channel_6_start = TIM4->CCR2; // record the start time of the pulse
TIM4->CCER |= TIM_CCER_CC2P; // change the input capture mode to the falling edge of the pulse
} else { // if the receiver channel 6 input pulse on B7 is low
channel_6 = TIM4->CCR2 - channel_6_start; // calculate the total pulse time
if (channel_6 < 0)channel_6 += 0xFFFF; // if the timer has rolled over a correction is needed
TIM4->CCER &= ~TIM_CCER_CC2P; // change the input capture mode to the rising edge of the pulse
}
break;
default :
break;
}
}
#ifdef HAL_WAY
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8); // the HAL way (also works)
#else
digitalWrite(TestOutputPin, !digitalRead(TestOutputPin)); // the Arduino way (works)
#endif
}

void setup() {

Serial.begin(115200);
Serial.print("Initializing...");
Serial.println(getTimerClkFreq(TIM1));
Serial.println();

// initialize digital pins
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PwmOutputPin, OUTPUT);
pinMode(TestOutputPin, OUTPUT);

// initialize the input capture pins
pinMode(CaptureInputPin1, INPUT);
pinMode(CaptureInputPin2, INPUT);
pinMode(CaptureInputPin3, INPUT);
pinMode(CaptureInputPin4, INPUT);
pinMode(CaptureInputPin5, INPUT);
pinMode(CaptureInputPin6, INPUT);

// create a 1KHz 25% (256/4 - 1) duty PWM on testPwmOutputPin
// NOTE - uses TIM associated with output pin
analogWrite(PwmOutputPin, (64 - 1));

Print_TIM2_Regs();

// ------- Input Capture TIM3 Setup -------------------------

GPIO_InitTypeDef GPIO_InitStruct;

// TIM3 interrupt Init
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;

htim3.Instance = TIM3;
htim3.Init.Prescaler = (64 - 1);
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

#ifdef HAL_WAY
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_2);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_3);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_4, TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_IC_Start (&htim3, TIM_CHANNEL_4);
#else
TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE;
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC2E | TIM_CCER_CC2P | TIM_CCER_CC3E | TIM_CCER_CC3P | TIM_CCER_CC4E | TIM_CCER_CC4P;
TIM3->CR1 = TIM_CR1_CEN;
#endif

Print_TIM3_Regs();

// ------- Input Capture TIM3 Setup -------------------------

// TIM3 interrupt Init
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM4_IRQn);

htim4.Instance = TIM4;
htim4.Init.Prescaler = (64 - 1);
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 0xFFFF;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

TIM4->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE;
TIM4->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC2E | TIM_CCER_CC2P;
TIM4->CR1 = TIM_CR1_CEN;

Print_TIM4_Regs();

}

void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
float ch1_msec = ((float) channel_1 / 1000.0);
float ch2_msec = ((float) channel_2 / 1000.0);
float ch3_msec = ((float) channel_3 / 1000.0);
float ch4_msec = ((float) channel_4 / 1000.0);
float ch5_msec = ((float) channel_5 / 1000.0);
float ch6_msec = ((float) channel_6 / 1000.0);
Serial.print("channel_1(mSec): "); Serial.print(ch1_msec);
Serial.print(" | channel_2(mSec): "); Serial.print(ch2_msec);
Serial.print(" | channel_3(mSec): "); Serial.print(ch3_msec);
Serial.print(" | channel_4(mSec): "); Serial.print(ch4_msec);
Serial.print(" | channel_5(mSec): "); Serial.print(ch5_msec);
Serial.print(" | channel_6(mSec): "); Serial.println(ch6_msec);
}

void Print_TIM2_Regs() {
Serial.print("TIM2->CR1: "); Serial.println(TIM2->CR1, HEX);
Serial.print("TIM2->CR2: "); Serial.println(TIM2->CR2, HEX);
Serial.print("TIM2->DIER: "); Serial.println(TIM2->DIER, HEX);
Serial.print("TIM2->CCER: "); Serial.println(TIM2->CCER, HEX);
Serial.print("TIM2->SR: "); Serial.println(TIM2->SR, HEX);
Serial.print("TIM2->CNT: "); Serial.println(TIM2->CNT, HEX);
Serial.print("TIM2->PSC: "); Serial.println(TIM2->PSC, HEX);
Serial.print("TIM2->ARR: "); Serial.println(TIM2->ARR, HEX);
Serial.println();
}

void Print_TIM3_Regs() {
Serial.print("TIM3->CR1: "); Serial.println(TIM3->CR1, HEX);
Serial.print("TIM3->CR2: "); Serial.println(TIM3->CR2, HEX);
Serial.print("TIM3->DIER: "); Serial.println(TIM3->DIER, HEX);
Serial.print("TIM3->CCER: "); Serial.println(TIM3->CCER, HEX);
Serial.print("TIM3->SR: "); Serial.println(TIM3->SR, HEX);
Serial.print("TIM3->CNT: "); Serial.println(TIM3->CNT, HEX);
Serial.print("TIM3->PSC: "); Serial.println(TIM3->PSC, HEX);
Serial.print("TIM3->ARR: "); Serial.println(TIM3->ARR, HEX);
Serial.println();
}

void Print_TIM4_Regs() {
Serial.print("TIM4->CR1: "); Serial.println(TIM4->CR1, HEX);
Serial.print("TIM4->CR2: "); Serial.println(TIM4->CR2, HEX);
Serial.print("TIM4->DIER: "); Serial.println(TIM4->DIER, HEX);
Serial.print("TIM4->CCER: "); Serial.println(TIM4->CCER, HEX);
Serial.print("TIM4->SR: "); Serial.println(TIM4->SR, HEX);
Serial.print("TIM4->CNT: "); Serial.println(TIM4->CNT, HEX);
Serial.print("TIM4->PSC: "); Serial.println(TIM4->PSC, HEX);
Serial.print("TIM4->ARR: "); Serial.println(TIM4->ARR, HEX);
Serial.println();
}

void _Error_Handler(const char * msg, int val) {
Serial.print("Error: ");
Serial.print(msg);
Serial.print(" Err: ");
Serial.println(val);
while(1) {}
}


Leave a Reply

Your email address will not be published. Required fields are marked *