2020-12-28 12:51 AM
Hi,
Greetings!
I have a Bourns Rotary encoder
https://www.bourns.com/docs/product-datasheets/pec11r.pdf
connected to PA11(ENCA)/TIM4_CH1 and PA12(ENCB)/TIM4_CH2.
Trying to use the encoder as a input control for user input.
This is what I am doing:
static void encoder_init(void)
{
LL_GPIO_InitTypeDef gpio;
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);
gpio.Pin = LL_GPIO_PIN_11 | LL_GPIO_PIN_12; /* Enc A, Enc B */
gpio.Mode = LL_GPIO_MODE_ALTERNATE; /* Alternate function */
gpio.Speed = LL_GPIO_SPEED_FREQ_LOW; /* Low Speed */
gpio.Alternate = LL_GPIO_AF_10; /* AF10=TIM4 */
LL_GPIO_Init(GPIOA, &gpio);
LL_TIM_SetPrescaler(TIM4, 0);
LL_TIM_SetAutoReload(TIM4, 100);
LL_TIM_SetCounterMode(TIM4, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM4, LL_TIM_CLOCKDIVISION_DIV4);
LL_TIM_DisableARRPreload(TIM4);
// LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_X4_TI12);
LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_DIRECTIONALCLOCK_X1_TI12);
LL_TIM_IC_SetActiveInput(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV2_N8);
LL_TIM_IC_SetPolarity(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
LL_TIM_IC_SetActiveInput(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV2_N8);
LL_TIM_IC_SetPolarity(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
LL_TIM_EnableCounter(TIM4);
LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH2);
}
int main(void)
{
uint32_t enc_cur = 0, enc_prev = 0;
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
SystemClock_Config();
gpio_init();
uart_init();
encoder_init();
printf("\tSTM32G474 Encoder Test\n");
while (1) {
enc_cur = LL_TIM_GetCounter(TIM4);
if (enc_cur != enc_prev) {
printf(" ENC: %d DIR: %d\n", enc_cur, LL_TIM_GetDirection(TIM4) >> 4);
enc_prev = enc_cur;
}
}
}
On RST, I get the following results:
STM32G474 Encoder Test
ENC: 1 DIR: 0
ENC: 2 DIR: 0
ENC: 3 DIR: 0
ENC: 4 DIR: 0
When I turn the encoder CW, I get:
ENC: 3 DIR: 1
ENC: 2 DIR: 1
ENC: 1 DIR: 1
ENC: 0 DIR: 1
ENC: 100 DIR: 1
ENC: 99 DIR: 1
ENC: 98 DIR: 1
ENC: 97 DIR: 1
ENC: 96 DIR: 1
When I turn the encoder CCW, I get:
ENC: 95 DIR: 1
ENC: 94 DIR: 1
ENC: 93 DIR: 1
ENC: 92 DIR: 1
ENC: 91 DIR: 1
ENC: 90 DIR: 1
ENC: 89 DIR: 1
ENC: 88 DIR: 1
ENC: 87 DIR: 1
Attached screenshot:
Despite what I do, the direction, does not seem to change.
Any thoughts, suggestions what I am doing wrong in here ?
Thanks,
Manu
Solved! Go to Solution.
2020-12-28 04:55 AM
2020-12-28 01:30 AM
I don't understand the Cube/LL gibberish, but
> LL_TIM_ENCODERMODE_DIRECTIONALCLOCK_X1_TI12
is probably Directional Clock encoder mode, which, as the manual says:
In the “directional clock�? mode on Figure 409, the clocks are provided on two lines, with a
single one at once, depending on the direction, so as to have one up-counting clock line and
one down-counting clock line.
which is not case with the usual manually turned encoder.
JW
2020-12-28 02:45 AM
Hi JW,
Thanks for the input. Which mode do you suggest ?
I tried both SMS=b'0011' and b'1110' both appeared to have weird results:
ie,
static void encoder_init_r2(void)
{
LL_GPIO_InitTypeDef gpio;
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);
gpio.Pin = LL_GPIO_PIN_11 | LL_GPIO_PIN_12; /* Enc A, Enc B */
gpio.Mode = LL_GPIO_MODE_ALTERNATE; /* Alternate function */
gpio.Speed = LL_GPIO_SPEED_FREQ_LOW; /* Low Speed */
gpio.Alternate = LL_GPIO_AF_10; /* AF10=TIM4 */
LL_GPIO_Init(GPIOA, &gpio);
LL_TIM_SetPrescaler(TIM4, 0);
LL_TIM_SetAutoReload(TIM4, 100);
LL_TIM_SetCounterMode(TIM4, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM4, LL_TIM_CLOCKDIVISION_DIV4);
LL_TIM_DisableARRPreload(TIM4);
/**
* Slave Mode Selection (SMS)
* 0000: Slave mode disabled ifCEN = ‘1 prescaler fed from internal clock.
* 0001: Encoder mode 1 - tim_ti1fp1 edge up/down counter depending on tim_ti2fp2 level.
* 0010: Encoder mode 2 - tim_ti2fp2 edge up/down counter depending on tim_ti1fp1 level.
* 0011: Encoder mode 3 - tim_ti1fp1, tim_ti2fp2 edge up/down counter depending on the level of the other input.
* 0100: Reset Mode - selected trigger input rising edge (tim_trgi) reinitializes counter and updates registers.
* 0101: Gated Mode - The counter clock is enabled when the trigger input (tim_trgi) is high.
* 0110: Trigger Mode - The counter starts at a rising edge of the trigger tim_trgi (but it is not reset). Only the start of the counter is controlled.
* 0111: External Clock Mode 1 - Rising edges of the selected trigger (tim_trgi) clock the counter.
* 1000: Combined reset + trigger mode - Rising edge of the selected trigger input (tim_trgi) reinitializes the counter, generates an update of the registers and starts the counter.
* 1001: Combined gated + reset mode - The counter clock is enabled when the trigger input (tim_trgi) is high. The counter stops and is reset) as soon as the trigger becomes low.
* 1010: Encoder mode: Clock plus direction, x2 mode.
* 1011: Encoder mode: Clock plus direction, x1 mode, tim_ti2fp2 edge sensitivity is set by CC2P.
* 1100: Encoder mode: Directional Clock, x2 mode.
* 1101: Encoder mode: Directional Clock, x1 mode, tim_ti1fp1 and tim_ti2fp2 edge sensitivity is set by CC1P and CC2P.
* 1110: Quadrature encoder mode: x1 mode, counting on tim_ti1fp1 edges only, edge sensitivity is set by CC1P.
* 1111: Quadrature encoder mode: x1
*
* #define TIM_SMCR_SMS_0 (0x00001UL << TIM_SMCR_SMS_Pos) // 0x00000001
* #define TIM_SMCR_SMS_1 (0x00002UL << TIM_SMCR_SMS_Pos) // 0x00000002
* #define TIM_SMCR_SMS_2 (0x00004UL << TIM_SMCR_SMS_Pos) // 0x00000004
* #define TIM_SMCR_SMS_3 (0x10000UL << TIM_SMCR_SMS_Pos) // 0x00010000
*
* #define LL_TIM_ENCODERMODE_X2_TI1 TIM_SMCR_SMS_0 // Quadrature encoder mode 1, x2 mode - Counter counts up/down on TI1FP1 edge depending on TI2FP2 level
* #define LL_TIM_ENCODERMODE_X2_TI2 TIM_SMCR_SMS_1 // Quadrature encoder mode 2, x2 mode - Counter counts up/down on TI2FP2 edge depending on TI1FP1 level
* #define LL_TIM_ENCODERMODE_X4_TI12 (TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0) // Quadrature encoder mode 3, x4 mode - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input
* #define LL_TIM_ENCODERMODE_CLOCKPLUSDIRECTION_X2 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_1) // Encoder mode: Clock plus direction - x2 mode
* #define LL_TIM_ENCODERMODE_CLOCKPLUSDIRECTION_X1 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0) // Encoder mode: Clock plus direction, x1 mode, TI2FP2 edge sensitivity is set by CC2P
* #define LL_TIM_ENCODERMODE_DIRECTIONALCLOCK_X2 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_2) // Encoder mode: Directional Clock, x2 mode
* #define LL_TIM_ENCODERMODE_DIRECTIONALCLOCK_X1_TI12 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_2 | TIM_SMCR_SMS_0) // Encoder mode: Directional Clock, x1 mode, TI1FP1 and TI2FP2 edge sensitivity is set by CC1P and CC2P
* #define LL_TIM_ENCODERMODE_X1_TI1 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1) // Quadrature encoder mode: x1 mode, counting on TI1FP1 edges only, edge sensitivity is set by CC1P
* #define LL_TIM_ENCODERMODE_X1_TI2 (TIM_SMCR_SMS_3 | TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0) // Quadrature encoder mode: x1 mode, counting on TI2FP2 edges only, edge sensitivity is set by CC1P
*/
// LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_X4_TI12); /* Enc MODE3: Up/Down on TI1FP1, TI2FP2 edges */
LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_X1_TI1); /* Enc x1 MODE: Up/Down on TI1FP1 edge */
LL_TIM_IC_SetActiveInput(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV2_N8);
LL_TIM_IC_SetPolarity(TIM4, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
LL_TIM_IC_SetActiveInput(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV2_N8);
LL_TIM_IC_SetPolarity(TIM4, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
LL_TIM_EnableCounter(TIM4);
LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH2);
}
which results in
#1. With
LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_X4_TI12); /* Enc MODE3: Up/Down on TI1FP1, TI2FP2 edges */
On RST
STM32G474 Encoder Test
ENC: 1 DIR: 0
ENC: 2 DIR: 0
ENC: 1 DIR: 1
ENC: 2 DIR: 0
ENC: 1 DIR: 1
ENC: 2 DIR: 0
ENC: 1 DIR: 1
ENC: 2 DIR: 0
ENC: 1 DIR: 1
ENC: 2 DIR: 0
On Single CW rotation:
ENC: 3 DIR: 0
ENC: 2 DIR: 1
On Single CCW rotation
ENC: 3 DIR: 0
ENC: 2 DIR: 1
4x CW rotations
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
4x CCW rotations
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
ENC: 3 DIR: 0
ENC: 2 DIR: 1
#2. With
LL_TIM_SetEncoderMode(TIM4, LL_TIM_ENCODERMODE_X1_TI1); /* Enc x1 MODE: Up/Down on TI1FP1 edge */
On RST
STM32G474 Encoder Test
On Single CW rotation:
ENC: 1 DIR: 0
ENC: 0 DIR: 1
On Single CCW rotation:
ENC: 1 DIR: 0
ENC: 0 DIR: 1
Both dont look quite sane. Or am I still doing something wrong ?
Thanks,
Manu
2020-12-28 04:55 AM
This looks much like there's no input on CH2.
JW
2020-12-28 06:50 AM
Hi JW,
I was getting pretty frustrated -- Connected a scope to ENC_A and _B, things looked very well indeed.
Pulled a few hairs not knowing what to do.
Checked CH1 , CH2 waveforms on PA11, 12.
Well, as you said: one channel was dead .. The connector on CH2 was not making contact.
Changed the connector, things look quite good indeed.
Thanks a bunch! I would have lost quite a lot of hair, if not for your observation. ;)
Something really bad happened over here. After I did my last post; We had an electrical issue here, complete blackout, UPS tripped. :\
Resuming after fixing the problem, the filesystem has a previous version of main.c, ie, it was missing the newer
encoder_init_r2() function.
Had to copy back the function from the forum. :)
It looks quite well.
One issue though, it starts with the set auto reload value (ie at max, in this case 100)
Any thoughts to get it start with, say 0 ?
STM32G474 Encoder Test
ENC: 100 DIR: 1
ENC: 0 DIR: 0
ENC: 1 DIR: 0
ENC: 2 DIR: 0
ENC: 3 DIR: 0
ENC: 4 DIR: 0
ENC: 5 DIR: 0
ENC: 6 DIR: 0
ENC: 7 DIR: 0
ENC: 6 DIR: 1
ENC: 5 DIR: 1
ENC: 4 DIR: 1
ENC: 3 DIR: 1
ENC: 2 DIR: 1
ENC: 1 DIR: 1
ENC: 0 DIR: 1
ENC: 100 DIR: 1
ENC: 99 DIR: 1
ENC: 98 DIR: 1
ENC: 97 DIR: 1
Thanks again,
Manu
2020-12-28 07:09 AM
The quick hack, that I did was to set the counter to 1.
Wonder, whether that's a good thing to do ..
Thanks,
Manu