Rotary encoder with blue pill
Re: Rotary encoder with blue pill
ok thanks... well I do my own rotary lib long ago...
https://drive.google.com/file/d/1iWFBES ... share_link
... and this is the way u setup it...
#include <RotaryEncoder.h>
float maxMark = 12.0;
float minMark = 0.0;
float incMark = 0.25;
#define Enc_CLK PB8
#define Enc_DT PB9
RotaryEncoder EncoderPulse (Enc_CLK,Enc_DT,incMark,minMark,maxMark,4);
(4 is for the number of latches the encoder is using... cuadrature etc)
thats all I think anybody needs to do using that library, the min, the max and the specific increment u want to use... all in a easy to use 1 line setup
this soft version works good for me, I'm just looking for a good hardware based alternative with the same easy of use... so Im looking for a solution that does not need to use digitalRead in order to work.
https://drive.google.com/file/d/1iWFBES ... share_link
... and this is the way u setup it...
#include <RotaryEncoder.h>
float maxMark = 12.0;
float minMark = 0.0;
float incMark = 0.25;
#define Enc_CLK PB8
#define Enc_DT PB9
RotaryEncoder EncoderPulse (Enc_CLK,Enc_DT,incMark,minMark,maxMark,4);
(4 is for the number of latches the encoder is using... cuadrature etc)
thats all I think anybody needs to do using that library, the min, the max and the specific increment u want to use... all in a easy to use 1 line setup
this soft version works good for me, I'm just looking for a good hardware based alternative with the same easy of use... so Im looking for a solution that does not need to use digitalRead in order to work.
Re: Rotary encoder with blue pill
playing with the code I solved the initial problem... it just need a Refresh()...
not A simple code like this works very good...
now I just need to know how to specify the pins used... as showed here by default the first 2 channels are used... so u do not need to declare them as inputs.
not A simple code like this works very good...
Code: Select all
#include "HardwareTimer.h"
HardwareTimer timer(4);//PB6 and PB7
float units=0.0;
float incre=0.1;
void func(){
if (timer.getDirection())
{units-=incre;}
else
{units+=incre;}
}
void setup() {
//define the Timer channels as inputs.
// pinMode(D2, INPUT_PULLUP); //channel A
// pinMode(D3, INPUT_PULLUP); //channel B
timer.pause();
//timer.setMode(0, TIMER_ENCODER);
//timer.setCompare(TIMER_CH1, 1);
timer.setPrescaleFactor(1);
timer.setOverflow(3);
timer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
//timer.setCount(0);
timer.refresh(); // <<<<<<<<<< FIX!!!
timer.resume();
timer.attachInterrupt(0, func);
}
void loop() {
Serial.print("Units: ");
Serial.println(units);
}
Re: Rotary encoder with blue pill
playing more u can even work without attachedinterrupts...
it seems there is no way to disable or decide which channel u can use,,, low level will be needed.
Code: Select all
#include "HardwareTimer.h"
HardwareTimer MyTimer(1);
float units=0.0;
float incre=0.5;
float min = -4;
float max = 3.5;
float start =-2.0;
int range =4*(max-min)/incre;
void setup() {
//timer 1 pins
pinMode(PA8 , INPUT_PULLUP);
pinMode(PA9 , INPUT_PULLUP);
pinMode(PA10, INPUT_PULLUP);
pinMode(PA11, INPUT_PULLUP);
MyTimer.pause();
MyTimer.setPrescaleFactor(1);
MyTimer.setOverflow(range);
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
MyTimer.setMode(TIMER_CH1, TIMER_DISABLED);
MyTimer.setMode(TIMER_CH2, TIMER_DISABLED);
MyTimer.setMode(TIMER_CH3, TIMER_DISABLED);
MyTimer.setMode(TIMER_CH4, TIMER_DISABLED);
MyTimer.refresh();
MyTimer.setCount(4*abs(min-start)/incre);
MyTimer.resume();
}
void loop() {
units = ((MyTimer.getCount()/4)*incre)+min;
Serial.print("Units: ");
Serial.println(units);
}
-
- Posts: 9
- Joined: Fri Dec 16, 2022 4:28 pm
Re: Rotary encoder with blue pill
I have been struggling to get the hardware timers to work in encoder mode. I'm using a BluePill (STM32F103C8) and Roger Clarks core for Arduino.
I'm setting it up in ENCODER3 mode (TIMER4_BASE->SMCR = TIMER_SMCR_SMS_ENCODER3; //SMS = 011) which according to the reference manual should trigger counts on the rise AND fall of both inputs TI1 and TI2. I am getting counts on half of those transitions, which ALMOST works, except it doesn't handle jitter. If input TI1 toggles true/false, my counter doesn't count UP, then DOWN. It only counts up. I'm using this for position control of a linear actuator, and eventually these small errors caused by jitter accumulate and become significant.
I "think" I'm setting up the timer according to the reference manual, but maybe there is some secret switch I need to set for the configuration to be accepted by the board. Here is my code.
I'm setting it up in ENCODER3 mode (TIMER4_BASE->SMCR = TIMER_SMCR_SMS_ENCODER3; //SMS = 011) which according to the reference manual should trigger counts on the rise AND fall of both inputs TI1 and TI2. I am getting counts on half of those transitions, which ALMOST works, except it doesn't handle jitter. If input TI1 toggles true/false, my counter doesn't count UP, then DOWN. It only counts up. I'm using this for position control of a linear actuator, and eventually these small errors caused by jitter accumulate and become significant.
I "think" I'm setting up the timer according to the reference manual, but maybe there is some secret switch I need to set for the configuration to be accepted by the board. Here is my code.
Code: Select all
/*
* STM32F102C8 - Blue Pill
* Arduino IDE
* Roger Clark STM32 for Arduino Library
* Test using Timer 4 as an encoder input.
* T4C1 is pin PB6
* T4C2 is pin PB7
*/
void setup() {
RCC_BASE->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC_BASE->APB1ENR |= RCC_APB1ENR_TIM4EN;
pinMode(PB6, INPUT_PULLUP);//So I can easily toggle the input by shorting to ground
pinMode(PB7, INPUT_PULLUP);//So I can easily toggle the input by shorting to ground
//Setup timer 4 inputs 1 and 2 as encoder inputs
TIMER4_BASE->ARR = 65535; //set the rollover value to maximum
//configure channels 1 and 2 as inputs from their respective pins PB6 and PB7
TIMER4_BASE->CCMR1 |= (TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2);
TIMER4_BASE->SMCR = TIMER_SMCR_SMS_ENCODER3; //SMS = 011
TIMER4_BASE->CR1 |= TIMER_CR1_CEN; //Enable counter
Serial.begin(57600);
}
void loop() {
Serial.println(TIMER4_BASE->CNT);
delay(1000);
}
Re: Rotary encoder with blue pill
Hi
this is my actual code about this...
test it MAYBE it can give u some info...
this is my actual code about this...
test it MAYBE it can give u some info...
- Attachments
-
- HardTimer Encoder.rar
- (9.03 KiB) Downloaded 1026 times
-
- Posts: 9
- Joined: Fri Dec 16, 2022 4:28 pm
Re: Rotary encoder with blue pill
Thanks,
I'll give this a try. It looks like you're only triggering the counter on transitions of input channel1.
MyTimer.setMode(1, TIMER_ENCODER );
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1);
I'm curious how this .setMode function works. I've traced it to.
C:\Program Files (x86)\Arduino\hardware\Arduino_STM32-master\STM32F1\cores\maple\HardwareTimer.cpp
--------------------------------------------------------------------------
which calls
C:\Program Files (x86)\Arduino\hardware\Arduino_STM32-master\STM32F1\cores\maple\libmaple\timer.c
Notice the comment "//find a way to pass all the needed stuff on the 8bit var"
Seems like this case isn't finished.
Author didn't know how to pass all of the info needed.
----------------------------------------------------------------------
which calls this (from same file timer.c)
Notice how the channel__attribute parameter is unused?
Also note how this routine by default sets the timer to encoder3 mode (which is supposed to trigger a count on each rise AND fall of BOTH channel 1 and channe 2 inputs (See STM32F1 reference manual).
It also applies the maximum debounce filtering (TIMER_CCMR1_IC1F)
Can you tell me how your program performs? Does count on both rising and falling edges of both inputs, or only channel 1 input?
I'll give this a try. It looks like you're only triggering the counter on transitions of input channel1.
MyTimer.setMode(1, TIMER_ENCODER );
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1);
I'm curious how this .setMode function works. I've traced it to.
C:\Program Files (x86)\Arduino\hardware\Arduino_STM32-master\STM32F1\cores\maple\HardwareTimer.cpp
Code: Select all
void HardwareTimer::setMode(int channel, timer_mode mode) {
timer_set_mode(this->dev, (uint8)channel, (timer_mode)mode);
}
which calls
C:\Program Files (x86)\Arduino\hardware\Arduino_STM32-master\STM32F1\cores\maple\libmaple\timer.c
Notice the comment "//find a way to pass all the needed stuff on the 8bit var"
Seems like this case isn't finished.
Author didn't know how to pass all of the info needed.
Code: Select all
void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) {
ASSERT_FAULT(channel > 0 && channel <= 4);
/* TODO decide about the basic timers */
ASSERT(dev->type != TIMER_BASIC);
if (dev->type == TIMER_BASIC)
return;
switch (mode) {
case TIMER_DISABLED:
disable_channel(dev, channel);
break;
case TIMER_PWM:
pwm_mode(dev, channel);
break;
case TIMER_OUTPUT_COMPARE:
output_compare_mode(dev, channel);
break;
//added by CARLOS.
case TIMER_ENCODER:
encoder_mode(dev, channel); //find a way to pass all the needed stuff on the 8bit var
break;
case TIMER_INPUT_CAPTURE:// code from @Cesco
input_capture_mode(dev, channel, TIMER_IC_INPUT_DEFAULT);
break;
}
}
which calls this (from same file timer.c)
Notice how the channel__attribute parameter is unused?
Also note how this routine by default sets the timer to encoder3 mode (which is supposed to trigger a count on each rise AND fall of BOTH channel 1 and channe 2 inputs (See STM32F1 reference manual).
It also applies the maximum debounce filtering (TIMER_CCMR1_IC1F)
Code: Select all
static void encoder_mode(timer_dev *dev, uint8 channel __attribute__((unused))) {
//prescaler.
//(dev->regs).gen->PSC = 1;
//map inputs.
(dev->regs).gen->CCMR1 = TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2 | TIMER_CCMR1_IC2F | TIMER_CCMR1_IC1F ;
(dev->regs).gen->SMCR = TIMER_SMCR_SMS_ENCODER3; //choose encoder 3, counting on both edges.
//polarity
//(dev->regs).gen->CCER = TIMER_CCER_CC1P; //to invert the counting, only one of the inputs should be inverted.
//set the interval used by the encoder.
//timer_set_reload(dev, 1000);
// (dev->regs).gen->CR1 |=TIMER_CR1_UDIS_BIT;
//run timer
timer_resume(dev);
}
Re: Rotary encoder with blue pill
look that code works well for me , of course it tracks the encoder movements 100% accurately... no missed ticks at all... no matter what u do in normal sketch code... maybe u miss to show a change but the changes are always tracked... just how it must be.
THE PROBLEM: the internal counter goes from 0 to any number u decide with the overflow... after that it will return to zero or to the max number on the counter memory object, that's makes necessary use of code to control the limits. That s why I include 2 examples to see the difference.
That means u can not avoid the ticks bellow zero or above the overflow limits.
A low level way to NOT count when u reach that limits is what I want to find.
For me an encoder with no control of the limits for the user is useless...
SO by now I continue to use my old software based library... included here.
THE PROBLEM: the internal counter goes from 0 to any number u decide with the overflow... after that it will return to zero or to the max number on the counter memory object, that's makes necessary use of code to control the limits. That s why I include 2 examples to see the difference.
That means u can not avoid the ticks bellow zero or above the overflow limits.
A low level way to NOT count when u reach that limits is what I want to find.
For me an encoder with no control of the limits for the user is useless...
SO by now I continue to use my old software based library... included here.
- Attachments
-
- RotaryEncoder.rar
- (42.07 KiB) Downloaded 842 times
-
- Posts: 9
- Joined: Fri Dec 16, 2022 4:28 pm
Re: Rotary encoder with blue pill
Sigi,
Thanks for the example code. I ran it on my Blue Pill and it worked correctly in ENCODER1 mode.
Triggered on rise and fall of input channel 1.
I changed
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1);
to
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
and it triggered on rise AND fall of BOTH input channels 1 AND 2 which is what I've been struggling to make happen. YEAH!!!!
I'm going to drill into that HardwareTimer code and see if I can find a difference between what your code is doing vs my attempt to manipulate the timer registers directly. There must be something different in the sequence of commands that is making my code not work.
Maybe it's in the setEdgeCounting function. Anyway, thanks again for your response which help me solve my problem.
Thanks for the example code. I ran it on my Blue Pill and it worked correctly in ENCODER1 mode.
Triggered on rise and fall of input channel 1.
I changed
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1);
to
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
and it triggered on rise AND fall of BOTH input channels 1 AND 2 which is what I've been struggling to make happen. YEAH!!!!
I'm going to drill into that HardwareTimer code and see if I can find a difference between what your code is doing vs my attempt to manipulate the timer registers directly. There must be something different in the sequence of commands that is making my code not work.
Maybe it's in the setEdgeCounting function. Anyway, thanks again for your response which help me solve my problem.
-
- Posts: 9
- Joined: Fri Dec 16, 2022 4:28 pm
Re: Rotary encoder with blue pill
Here is the result of my tracing the functions in HardwareTimer
Code: Select all
#include "HardwareTimer.h"
HardwareTimer MyTimer(4);
MyTimer.pause(); //Disable timer CEN bit.
MyTimer.setMode(1, TIMER_ENCODER );
//setMode(int channel, timer_mode mode) located in HardwareTimer.cpp
//calls timer_set_mode(this->dev, (uint8)channel, (timer_mode)mode) in timer.c routine.
//void timer_set_mode(dev,channel, mode)
//switch(mode)
//case TIMER_ENCODER:
//encoder_mode(dev,channel) - calls encoder_mode in timer.c routine.
//void encoder_mode(timer_dev *dev, uint8 channel __attribute__((unused))) {
// (dev->regs).gen->CCMR1 = TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2 | TIMER_CCMR1_IC2F | TIMER_CCMR1_IC1F ;
//
// (dev->regs).gen->SMCR = TIMER_SMCR_SMS_ENCODER3; //choose encoder 3, counting on both edges.
MyTimer.setPrescaleFactor(1); //sets the prescaler to 0 (parameter - 1)
MyTimer.setOverflow(range+1); //sets the ARR to parameter
MyTimer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3);
//.setEdgeCounting(uint32 counting) (located in HardwareTimer.h)
//this calls setSlaveFlags(counting) in HardwareTimer.h
// void setSlaveFlags(uint32 flags) {
// ((this->dev)->regs).gen->SMCR = flags; Overwrites the contents of the SMCR (slave mode control register)
// }
MyTimer.refresh();
//.refresh() located in HardwareTimer.cpp
//calls timer_generate_update(this->dev) in timer.h
//static inline void timer_generate_update(timer_dev *dev) {
// *bb_perip(&(dev->regs).bas->EGR, TIMER_EGR_UG_BIT) = 1; Sets the UG bit in the EGR register
//}
//NOTE: Value of TIMER_EGR_UG_BIT = 0.
MyTimer.resume();
//This simply sets the CEN bit in the CR1 register enabling the timer.
Re: Rotary encoder with blue pill
a simpler approach may be to poll the pins periodically (driven by a timer) and use a state machine to read the encoder.
should be fairly simple.
should be fairly simple.