Page 1 of 1

[Solved] Set alternate function for quadrature encoder pins.

Posted: Tue Dec 22, 2020 2:05 pm
by jsuijs
Hi,

I'm using timers to decode a quadrature signal but have trouble getting this running on an stmf4 with stm32 core.

In detail: PB6/PB7 on timer4 and PA0/PA1 on timer2, on a 'Black F407VE'.

I do have this running on the same hardware, using CMSIS files, mostly dated 2011. However, I'd like to switch to the stm32-core to avoid all the low-level stuff on each project.

Afaik there is no support for TIM_ENCODERMODE_TI12 in the core, so what I did on an STM32F103C8 was setup the timers HAL_TIM_Encoder_Init & HAL_TIM_Encoder_Start. And initialize the gpio using stm32-core with pinMode(MAQUEENPLUS_PIN_ENCODER_L_A, INPUT_PULLUP);

And this works as expected.

But, moving this to the stm23f407 doesn't produce any result. I suspect this may have to do with the alternate function setting, which is required. But I don't see how to set / change that.

So my basic question is how to setup these timers using stm32-core functions as much as possible, avoiding cmsis-call when possible.

Any help will be appreciated!

Joep

Re: Set alternate function.

Posted: Tue Dec 22, 2020 2:31 pm
by fpiSTM
Right encoder is not supported.
It is working on F1 because the AF is not the same on this series compare to other.
You have to manually set the correct AF for each GPIO pins used using LL or HAL which is not managed by pinMode as it stands for pure GPIO config Input/output, PU/PD.

Code: Select all

   LL_GPIO_SetAFPin_8_15(port, ll_pin, afnum);
   LL_GPIO_SetAFPin_0_7(port, ll_pin, afnum);

Re: Set alternate function.

Posted: Tue Dec 22, 2020 5:53 pm
by jsuijs
Hi,

Thank you for your reply. Unfortunately this hasn't solved it yet. Spent the whole day on this and running out of ideas :( .
Below is the current program, have tried many variants but timers stay at zero.

Any suggestions?

Joep

Code: Select all

HardwareSerial Serial(PA3, PA2);

void setup() {
   Serial.begin(115200);
   Serial.println("Hello Stm32Duino world!");
   EncoderInit();
}

void loop() {
   Serial.print(LL_TIM_GetCounter(TIM4));
   Serial.print(' ');
   Serial.println(LL_TIM_GetCounter(TIM2));
   delay(1000);
}

void EncoderInit()
{
   pinMode(PD12, INPUT_PULLUP);   // TIM4
   pinMode(PD13, INPUT_PULLUP);   // TIM4
   pinMode(PA15, INPUT_PULLUP);   // TIM2
   pinMode(PB3,  INPUT_PULLUP);   // TIM2

   LL_GPIO_SetAFPin_8_15(GPIOD, GPIO_PIN_12, GPIO_AF2_TIM4);
   LL_GPIO_SetAFPin_8_15(GPIOD, GPIO_PIN_13, GPIO_AF2_TIM4);
   LL_GPIO_SetAFPin_8_15(GPIOA, GPIO_PIN_15, GPIO_AF1_TIM2);
   LL_GPIO_SetAFPin_0_7(GPIOB,  GPIO_PIN_3,  GPIO_AF1_TIM2);

//   RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM4, ENABLE);
//   RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2, ENABLE);

   TIM_HandleTypeDef       Encoder_Handle;
   TIM_Encoder_InitTypeDef sEncoderConfig;

   Encoder_Handle.Init.Period             = 65535;
   Encoder_Handle.Init.Prescaler          = 0;
   Encoder_Handle.Init.ClockDivision      = 0;
   Encoder_Handle.Init.CounterMode        = TIM_COUNTERMODE_UP;
   Encoder_Handle.Init.RepetitionCounter  = 0;
   Encoder_Handle.Init.AutoReloadPreload  = TIM_AUTORELOAD_PRELOAD_DISABLE;

   sEncoderConfig.EncoderMode             = TIM_ENCODERMODE_TI12;

   sEncoderConfig.IC1Polarity             = TIM_ICPOLARITY_RISING;
   sEncoderConfig.IC1Selection            = TIM_ICSELECTION_DIRECTTI;
   sEncoderConfig.IC1Prescaler            = TIM_ICPSC_DIV1;
   sEncoderConfig.IC1Filter               = 0;

   sEncoderConfig.IC2Polarity             = TIM_ICPOLARITY_RISING;
   sEncoderConfig.IC2Selection            = TIM_ICSELECTION_DIRECTTI;
   sEncoderConfig.IC2Prescaler            = TIM_ICPSC_DIV1;
   sEncoderConfig.IC2Filter               = 0;

   Encoder_Handle.Instance = TIM4;
   if(HAL_TIM_Encoder_Init(&Encoder_Handle, &sEncoderConfig) != HAL_OK) Serial.println("Init error of TIM4\n");
   HAL_TIM_Encoder_Start(&Encoder_Handle, TIM_CHANNEL_ALL);

   Encoder_Handle.Instance = TIM2;
   if(HAL_TIM_Encoder_Init(&Encoder_Handle, &sEncoderConfig) != HAL_OK) Serial.println("Init error of TIM2\n");
   HAL_TIM_Encoder_Start(&Encoder_Handle, TIM_CHANNEL_ALL);
}

Re: Set alternate function.

Posted: Wed Dec 23, 2020 6:37 pm
by jsuijs
Hi,

I'm almost there, it works but it needs quite some cleanup.

On of the issues was LL_GPIO_SetAFPin_8_15() and LL_GPIO_SetAFPin_0_7() do set the AFR-register, but do not activate the AF-mode in the MODER register. Is there a function or macro to set these bits, except HAL_GPIO_Init()?

Regards,
Joep

Re: Set alternate function.

Posted: Thu Dec 24, 2020 6:49 am
by fpiSTM
Yes you can use LL_GPIO_SetPinMode.
You can refers to this function to see which LL are used: https://github.com/stm32duino/Arduino_C ... nmap.c#L62

Re: Set alternate function.

Posted: Thu Dec 24, 2020 7:55 am
by jsuijs
Hi,

Yes, that's what I needed :)

I combined your two answers and some research into arduino->stm pins into a function to switch a pin to an Alternate function.

It works as a charm now, thanks again.

Joep

Code: Select all


//-----------------------------------------------------------------------------
// pinModeAF - configure Arduino pin for given alternate-function
//-----------------------------------------------------------------------------
// Alternate values: see LL_GPIO_SetAFPin_8_15()
// example call: pinModeAF(PD12, GPIO_AF2_TIM4);
//-----------------------------------------------------------------------------
void pinModeAF(int ulPin, uint32_t Alternate)
{
   int pn = digitalPinToPinName(ulPin);

   if (STM_PIN(pn) < 8) {
      LL_GPIO_SetAFPin_0_7( get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   } else {
      LL_GPIO_SetAFPin_8_15(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   }

   LL_GPIO_SetPinMode(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), LL_GPIO_MODE_ALTERNATE);
}

Re: Set alternate function.

Posted: Thu Dec 24, 2020 8:26 am
by jsuijs
Hi,

To finalize this, below the working version of the quadrature encoder setup on TIM4 and TIM2. Difference is TimerClock and AlternateFucnction are enabled.

Hope one day this will be useful for someone.

Joep

Code: Select all

HardwareSerial Serial(PA3, PA2);

void setup() {
   Serial.begin(115200);
   Serial.println("Hello Stm32Duino world!");
   EncoderInit();
}

void loop() {
   Serial.print(LL_TIM_GetCounter(TIM4));
   Serial.print(' ');
   Serial.println(LL_TIM_GetCounter(TIM2));
   delay(1000);
}

//-----------------------------------------------------------------------------
// pinModeAF - configure Arduino pin for given alternate-function
//-----------------------------------------------------------------------------
// Alternate values: see LL_GPIO_SetAFPin_8_15()
// example call: pinModeAF(PD12, GPIO_AF2_TIM4);
//-----------------------------------------------------------------------------
void pinModeAF(int ulPin, uint32_t Alternate)
{
   int pn = digitalPinToPinName(ulPin);

   if (STM_PIN(pn) < 8) {
      LL_GPIO_SetAFPin_0_7( get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   } else {
      LL_GPIO_SetAFPin_8_15(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   }

   LL_GPIO_SetPinMode(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), LL_GPIO_MODE_ALTERNATE);
}

void EncoderInit(void)
{
   // use standaard Arduino function for basic setup
   pinMode(PD12, INPUT_PULLUP);  // TIM4
   pinMode(PD13, INPUT_PULLUP);  // TIM4
   pinMode(PA15, INPUT_PULLUP);  // TIM2
   pinMode(PB3 , INPUT_PULLUP);  // TIM2

   // use custom function to switch to AlternateFuction.
   pinModeAF(PD12, GPIO_AF2_TIM4);
   pinModeAF(PD13, GPIO_AF2_TIM4);
   pinModeAF(PA15, GPIO_AF1_TIM2);
   pinModeAF(PB3 , GPIO_AF1_TIM2);

   TIM_HandleTypeDef       Encoder_Handle;
   TIM_Encoder_InitTypeDef sEncoderConfig;

   Encoder_Handle.Init.Period             = 65535;
   Encoder_Handle.Init.Prescaler          = 0;
   Encoder_Handle.Init.ClockDivision      = 0;
   Encoder_Handle.Init.CounterMode        = TIM_COUNTERMODE_UP;
   Encoder_Handle.Init.RepetitionCounter  = 0;
   Encoder_Handle.Init.AutoReloadPreload  = TIM_AUTORELOAD_PRELOAD_DISABLE;

   sEncoderConfig.EncoderMode             = TIM_ENCODERMODE_TI12;

   sEncoderConfig.IC1Polarity             = TIM_ICPOLARITY_RISING;
   sEncoderConfig.IC1Selection            = TIM_ICSELECTION_DIRECTTI;
   sEncoderConfig.IC1Prescaler            = TIM_ICPSC_DIV1;
   sEncoderConfig.IC1Filter               = 0;

   sEncoderConfig.IC2Polarity             = TIM_ICPOLARITY_RISING;
   sEncoderConfig.IC2Selection            = TIM_ICSELECTION_DIRECTTI;
   sEncoderConfig.IC2Prescaler            = TIM_ICPSC_DIV1;
   sEncoderConfig.IC2Filter               = 0;

   Encoder_Handle.Instance = TIM4;
   enableTimerClock(&Encoder_Handle);
   if(HAL_TIM_Encoder_Init(&Encoder_Handle, &sEncoderConfig) != HAL_OK) Serial.println("Init error of TIM4");
   HAL_TIM_Encoder_Start(&Encoder_Handle, TIM_CHANNEL_ALL);

   Encoder_Handle.Instance = TIM2;
   enableTimerClock(&Encoder_Handle);
   if(HAL_TIM_Encoder_Init(&Encoder_Handle, &sEncoderConfig) != HAL_OK) Serial.println("Init error of TIM2");
   HAL_TIM_Encoder_Start(&Encoder_Handle, TIM_CHANNEL_ALL);
}

Re: [Solved] Set alternate function for quadrature encoder pins.

Posted: Wed Apr 28, 2021 8:14 am
by AndrewBCN
Thank you very much for posting your code. I used the elegant function you wrote to setup pins in alternate function mode in my project, as I posted here:
viewtopic.php?f=41&t=1030

Re: Set alternate function.

Posted: Wed Mar 16, 2022 12:25 pm
by razvitm
jsuijs wrote: Thu Dec 24, 2020 7:55 am Hi,

Yes, that's what I needed :)

I combined your two answers and some research into arduino->stm pins into a function to switch a pin to an Alternate function.

It works as a charm now, thanks again.

Joep

Code: Select all


//-----------------------------------------------------------------------------
// pinModeAF - configure Arduino pin for given alternate-function
//-----------------------------------------------------------------------------
// Alternate values: see LL_GPIO_SetAFPin_8_15()
// example call: pinModeAF(PD12, GPIO_AF2_TIM4);
//-----------------------------------------------------------------------------
void pinModeAF(int ulPin, uint32_t Alternate)
{
   int pn = digitalPinToPinName(ulPin);

   if (STM_PIN(pn) < 8) {
      LL_GPIO_SetAFPin_0_7( get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   } else {
      LL_GPIO_SetAFPin_8_15(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
   }

   LL_GPIO_SetPinMode(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), LL_GPIO_MODE_ALTERNATE);
}
Does not compile on latest core, did the new core version break compatibility i wonder:
src\main.cpp: In function 'void pinModeAF(int, uint32_t)':
src\main.cpp:48:7: error: 'LL_GPIO_SetAFPin_0_7' was not declared in this scope; did you mean 'LL_GPIO_SetPinPull'?
48 | LL_GPIO_SetAFPin_0_7( get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
| ^~~~~~~~~~~~~~~~~~~~
| LL_GPIO_SetPinPull
src\main.cpp:50:7: error: 'LL_GPIO_SetAFPin_8_15' was not declared in this scope; did you mean 'LL_GPIO_SetPinPull'?
50 | LL_GPIO_SetAFPin_8_15(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
| ^~~~~~~~~~~~~~~~~~~~~
| LL_GPIO_SetPinPull

Re: Set alternate function.

Posted: Thu Mar 17, 2022 8:38 am
by fpiSTM
razvitm wrote: Wed Mar 16, 2022 12:25 pm Does not compile on latest core, did the new core version break compatibility i wonder:
src\main.cpp: In function 'void pinModeAF(int, uint32_t)':
src\main.cpp:48:7: error: 'LL_GPIO_SetAFPin_0_7' was not declared in this scope; did you mean 'LL_GPIO_SetPinPull'?
48 | LL_GPIO_SetAFPin_0_7( get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
| ^~~~~~~~~~~~~~~~~~~~
| LL_GPIO_SetPinPull
src\main.cpp:50:7: error: 'LL_GPIO_SetAFPin_8_15' was not declared in this scope; did you mean 'LL_GPIO_SetPinPull'?
50 | LL_GPIO_SetAFPin_8_15(get_GPIO_Port(STM_PORT(pn)), STM_LL_GPIO_PIN(pn), Alternate);
| ^~~~~~~~~~~~~~~~~~~~~
| LL_GPIO_SetPinPull
See my answer here: viewtopic.php?p=9640#p9640