cancel
Showing results for 
Search instead for 
Did you mean: 

Why voltage drops while using GPIO as a timer input

Nikita.Mikhailovskiy
Associate II

Greetings.

I've entirely stuck with this problem.

MCU: STM32F407VGT6

Realization: custom PCB and F4 Discovery board.

Problem: Microcontroller can't gather information from encoders.

Description: 

  There are three motors with built-in encoders that I want to control. These motors have a controller that power encoder and drives motor by a PWM signal. It works with the TTL voltage level(5V). I used a discovery board to make a prototype where encoders were connected directly to the STM32. Everything works just fine. I made a custom PCB where all inputs are isolated. 

  For motor controllers, I've used ADuM1402 isolators (Quad-channel digital isolator), where two channels are output signals (PWM and direction), and two channels are inputs from the encoder (CHA and CHB). After some tests, I realized that only one of three encoders is processing right, and I get only zeroes from the others.

  I checked the voltage and signal on the pin and saw that maximal amplitude is equaled to 0.7 V, which is not enough to trigger a counter.

  I disabled an MCU by loading it in the bootloader and tested the voltage on that pin again. It equals to a Vdd voltage that powers the isolator and MCU(3.3V). After editing the pin mode from "alternate" to "input", an amplitude was equal to 3.3V too.

  

The working encoder is connected to Timer 1 through PA8 and PA9 (AF 1).

Non-processing encoders are connected to Timer 2 through PA0, PA1 (AF 1), and Timer 3 through PA6 and PA7 (AF 2).  

All pins are configured with pull-up resistors and push-pull output mode.

Timers are configured in encoder mode (LL_TIM_ENCODERMODE_X4_TI12) with active inputs.

Defines for examples:

/*

* Odometry configuration

* Encoder times configuration

*/

#define ENCODER_TIM_CNT_INITIAL_VALUE (uint16_t) 0x7530

#define ENCODER_TIM_ARR 0xffff

#define ENCODER_1_TIM_MODULE TIM2

#define ENCODER_1_CNT ((uint16_t *)&(ENCODER_1_TIM_MODULE->CNT))

#define ENCODER_2_TIM_MODULE TIM3

#define ENCODER_2_CNT ((uint16_t *)&(ENCODER_2_TIM_MODULE->CNT))

#define ENCODER_3_TIM_MODULE TIM1

#define ENCODER_3_CNT ((uint16_t *)&(ENCODER_3_TIM_MODULE->CNT))

#define ENCODER_2_CHA_PORT GPIOA

#define ENCODER_2_CHA_PIN LL_GPIO_PIN_7

#define ENCODER_2_CHB_PORT GPIOA

#define ENCODER_2_CHB_PIN LL_GPIO_PIN_6

#define ENCODER_2_PIN_AF LL_GPIO_AF_2

GPIO configuration example:

/*

* Initialization second encoder gpios

*/

LL_GPIO_SetPinMode(ENCODER_2_CHA_PORT, ENCODER_2_CHA_PIN,

LL_GPIO_MODE_ALTERNATE);

LL_GPIO_SetAFPin_0_7(ENCODER_2_CHA_PORT, ENCODER_2_CHA_PIN,

ENCODER_2_PIN_AF);

LL_GPIO_SetPinPull(ENCODER_2_CHA_PORT, ENCODER_2_CHA_PIN,

LL_GPIO_PULL_UP);

LL_GPIO_SetPinOutputType(ENCODER_2_CHA_PORT, ENCODER_2_CHA_PIN,

LL_GPIO_OUTPUT_PUSHPULL)

LL_GPIO_SetPinMode(ENCODER_2_CHB_PORT, ENCODER_2_CHB_PIN,

LL_GPIO_MODE_ALTERNATE);

LL_GPIO_SetAFPin_0_7(ENCODER_2_CHB_PORT, ENCODER_2_CHB_PIN,

ENCODER_2_PIN_AF);

LL_GPIO_SetPinPull(ENCODER_2_CHB_PORT, ENCODER_2_CHB_PIN,

LL_GPIO_PULL_UP);

LL_GPIO_SetPinOutputType(ENCODER_2_CHB_PORT, ENCODER_2_CHB_PIN,

LL_GPIO_OUTPUT_PUSHPULL)

Timer configuration example:

/*

    * Second encoder mode timer

     */

    LL_TIM_CC_EnableChannel(ENCODER_2_TIM_MODULE,

     LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH2);

    LL_TIM_IC_Config(ENCODER_2_TIM_MODULE, LL_TIM_CHANNEL_CH1,

             LL_TIM_ACTIVEINPUT_DIRECTTI);

    LL_TIM_IC_Config(ENCODER_2_TIM_MODULE, LL_TIM_CHANNEL_CH2,

             LL_TIM_ACTIVEINPUT_DIRECTTI |

             LL_TIM_IC_POLARITY_RISING);

    LL_TIM_SetAutoReload(ENCODER_2_TIM_MODULE, ENCODER_TIM_ARR);

    LL_TIM_SetEncoderMode(ENCODER_2_TIM_MODULE,

               LL_TIM_ENCODERMODE_X4_TI12);

    *(odom_ctrl->p_enc_ticks[1]) = ENCODER_TIM_CNT_INITIAL_VALUE;

1 ACCEPTED SOLUTION

Accepted Solutions

After a little research. I figured out that there was incorrect usage of LL_TIM_IC_Config() function. For this function it's necessary to use full LL lib with timer and GPIO structures. CubeIDE generated the code with dedicated commands for configuring input config.

Generated code snippet:

LL_TIM_SetEncoderMode(ENCODER_1_TIM_MODULE, LL_TIM_ENCODERMODE_X4_TI12);
LL_TIM_IC_SetActiveInput(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
LL_TIM_IC_SetActiveInput(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(ENCODER_1_TIM_MODULE, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
LL_TIM_SetCounterMode(ENCODER_1_TIM_MODULE, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetAutoReload(ENCODER_1_TIM_MODULE, ENCODER_TIM_ARR);
LL_TIM_SetClockDivision(ENCODER_1_TIM_MODULE, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_DisableARRPreload(ENCODER_1_TIM_MODULE);
LL_TIM_SetTriggerOutput(ENCODER_1_TIM_MODULE, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(ENCODER_1_TIM_MODULE);

Best regards,

Nikita Mikhailovskiy

View solution in original post

12 REPLIES 12

Read out and check/post content of TIM and relevant GPIO registers content.

JW

Thanks for reply!

I've checked all TIM2 register with openocd.

There are values for timer:

(TIM2->CCMR1) = 0 //Strange behavior

(TIM2->CCMR2) = 0 //Strange behavior

(TIM2->CR1) = 1

(TIM2->CR2) = 0

(TIM2->SR) = 0

(TIM2->EGR) = 0

(TIM2->CCER) = 17 // (10001) CC1E enabled, non-inverting, rising edge, CC2E enabled

(TIM2->CNT) = 1966110000

(TIM2->PSC) = 0

(TIM2->ARR) = 65535

(TIM2->CCR1) = 0

(TIM2->CCR2) = 0

(TIM2->DCR) = 0

(TIM2->OR) = 0

0693W000003R69PQAS.png

For GPIOA port:

(GPIOA->MODER) = 2820318474 // (10101000000110101010010100001010)

(GPIOA->OTYPER) = 0 // All push-pulls

(GPIOA->AFR) = {570425361, 17} // L(100010000000000000000000010001), H(10001) AF1

(GPIOA->BSRR) = 0

(GPIOA->IDR) = 51004 // (1100011100111100)

(GPIOA->PUPDR) = 1677742085 // (1100100000000000101000000000101) pull-up for PA0 and PA1

(GPIOA->OSPEEDR) = 201367562 // 1100000000001010000000001010

0693W000003R6CYQA0.png

Result: some problem with setting capture/compare mode registers... Where the problem could be?

if CCMR1=0, it means, both CH1 and CH2 are output compare, i.e. the pins are turned to output.

You want them to be in input capture mode.

Read the TIM chapter and set the registers according to your requirements.

I don't use and don't understand Cube/LL.

JW

PS. In GDB, to print hexadecimal values, use the /x switch, e.g.

p /x TIM2->SMCR

Thanks for advice. So, I disabled all tasks except odometry. I tried to directly set capture mod by writnig:

TIM2->CCMR1 |= 0x101U;

    TIM3->CCMR1 |= 0x101U;

But this register still holding 0x0 value during debugging session. Should I consider to change chip, because timer2 and timer3 circuitry burned?

MCU consumes around 170 mA with initialized timer and pins with alternate function and 30 mA with pins set to the input type.

Also, I've checked the same code on several F4 Discovery boards. Registers CCMR1 on all Timers (1,2,3) are staying the same with value 0x0.

On discovery boards odometry signals going directly in a chip (5V). Voltage drops to 2.4 V on input pins (higher than Voh) and algorithms working fine, except power consumption.

> Registers CCMR1 on all Timers (1,2,3) are staying the same with value 0x0.

Then you do something wrong.

Write a minimal code, with nothing else but enabling timer clock, setting this register, and then infinite loop. Observe in debugger.

JW

LL library (only main.c):

#include "stm32f407xx.h"
#include "stm32f4xx_ll_rcc.h"
#include "stm32f4xx_ll_system.h"
#include "stm32f4xx_ll_bus.h"
#include "stm32f4xx_ll_gpio.h"
#include "stm32f4xx_ll_tim.h"
 
static void rcc_config()
{
        /* Enable HSE oscillator */
        LL_RCC_HSE_Enable();
        while(LL_RCC_HSE_IsReady() != 1);
 
        /* Set FLASH latency */
        LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);
 
        /* Main PLL configuration and activation */
        LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_8,
                                    336, LL_RCC_PLLP_DIV_2);
        LL_RCC_PLL_Enable();
        while(LL_RCC_PLL_IsReady() != 1);
 
        /* Sysclk activation on the main PLL */
        LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
        LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
        while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL);
 
        /* Set APB1 & APB2 prescaler */
        LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_4);
        LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
 
        /* Set systick to 1ms */
        SysTick_Config(168000000/1000);
 
        /* Update CMSIS variable (which can be updated also
         * through SystemCoreClockUpdate function) */
        SystemCoreClock = 168000000;
}
 
static void periph_config(void)
{
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
		LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
		/*
         * Initialization first encoder gpios
         */
		LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_ALTERNATE);
        LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_0, LL_GPIO_AF_1);		
        LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_UP);
		
        LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_1, LL_GPIO_MODE_ALTERNATE);  
        LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_1, LL_GPIO_AF_1);		
        LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_1, LL_GPIO_PULL_UP);
        
        /*
         * First encoder mode timer
         */
        LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH2);
        LL_TIM_IC_Config(TIM2, LL_TIM_CHANNEL_CH1,
                         LL_TIM_ACTIVEINPUT_DIRECTTI |
						 LL_TIM_IC_POLARITY_RISING);
        LL_TIM_IC_Config(TIM2, LL_TIM_CHANNEL_CH2,
                         LL_TIM_ACTIVEINPUT_DIRECTTI |
                         LL_TIM_IC_POLARITY_RISING);
        LL_TIM_SetAutoReload(TIM2, 0xFFFF);
        LL_TIM_SetEncoderMode(TIM2, LL_TIM_ENCODERMODE_X4_TI12);        
		LL_TIM_EnableCounter(TIM2);
 
        return;
}
 
int main() {
        rcc_config();
        periph_config();
 
		for(;;) {}
        return 0;
}

The same result here:

0693W000003RCRUQA4.png

I will try to install CubeIDE and make the same on HAL

With HAL and STM32CubeIDE Timer 2 works fine. Though, the time of a foreign code refactoring coming... :worried_face: 0693W000003RChhQAG.png

Thanks for the significant support, and have a nice day!

Thank you for your support. I've generated code in CubeIDE for the LL library and copypasted it in my project. Now everything works fine.😀