158 lines
4.8 KiB
C++
158 lines
4.8 KiB
C++
/*
|
|
* Hardware Timer as an Encoder interface.
|
|
*
|
|
* The STM32 Timers have the possibility of being used as an encoder interface.
|
|
* This can be both a quadrature encoder (mode 3) or pulse encoder with a signal to give direction (modes 1 and 2).
|
|
* The default behavior is for quadrature encoder.
|
|
*
|
|
* To avoid overflowing the encoder (which may or may not happen (although with 16 bits, it's likely), the following code
|
|
* will interrupt every time the number of pulses that each revolution gives to increment/decrement a variable (ints).
|
|
*
|
|
* This means that the total number of pulses given by the encoder will be (ints * PPR) + timer.getCount()
|
|
*
|
|
* Attached is also a bit of code to simulate a quadrature encoder.
|
|
* To test this library, make the connections as below:
|
|
*
|
|
* TIMER2 inputs -> Digital Pins used to simulate.
|
|
* D2 -> D4
|
|
* D3 -> D5
|
|
*
|
|
* COUNTING DIRECTION:
|
|
* 0 means that it is upcounting, meaning that Channel A is leading Channel B
|
|
*
|
|
* EDGE COUNTING:
|
|
*
|
|
* mode 1 - only counts pulses on channel B
|
|
* mode 2 - only counts pulses on Channel A
|
|
* mode 3 - counts on both channels.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "HardwareTimer.h"
|
|
|
|
//Encoder simulation stuff
|
|
//NOT NEEDED WHEN CONNECTING A REAL ENCODER
|
|
unsigned char mode = 0; //to issue steps...
|
|
unsigned char dir = 'F'; // direction of movement of the encoder.
|
|
|
|
unsigned int freq = 100; //update frequency.
|
|
unsigned long time = 0; //time variable for millis()
|
|
unsigned char states[4]; //because I'm lazy...
|
|
|
|
|
|
//Encoder stuff
|
|
|
|
//Pulses per revolution
|
|
#define PPR 1024
|
|
|
|
HardwareTimer timer(2);
|
|
|
|
unsigned long ints = 0;
|
|
|
|
void func(){
|
|
if (timer.getDirection()){
|
|
ints--;
|
|
} else{
|
|
ints++;
|
|
}
|
|
}
|
|
|
|
|
|
void setup() {
|
|
//define the Timer channels as inputs.
|
|
pinMode(D2, INPUT_PULLUP); //channel A
|
|
pinMode(D3, INPUT_PULLUP); //channel B
|
|
|
|
//configure timer as encoder
|
|
timer.setMode(0, TIMER_ENCODER); //set mode, the channel is not used when in this mode.
|
|
timer.pause(); //stop...
|
|
timer.setPrescaleFactor(1); //normal for encoder to have the lowest or no prescaler.
|
|
timer.setOverflow(PPR); //use this to match the number of pulse per revolution of the encoder. Most industrial use 1024 single channel steps.
|
|
timer.setCount(0); //reset the counter.
|
|
timer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3); //or TIMER_SMCR_SMS_ENCODER1 or TIMER_SMCR_SMS_ENCODER2. This uses both channels to count and ascertain direction.
|
|
timer.attachInterrupt(0, func); //channel doesn't mean much here either.
|
|
timer.resume(); //start the encoder...
|
|
|
|
//Setup encoder simulator
|
|
pinMode(5, OUTPUT);
|
|
pinMode(4, OUTPUT);
|
|
|
|
digitalWrite(5, HIGH);
|
|
digitalWrite(4, LOW);
|
|
states[0] = 0; //00
|
|
states[1] = 1; //01
|
|
states[2] = 3; //11
|
|
states[3] = 2; //10
|
|
|
|
|
|
}
|
|
|
|
//Support variables.
|
|
unsigned long interval=0; //variable for status updates...
|
|
char received = 0;
|
|
|
|
void loop() {
|
|
//encoder code
|
|
if (millis() - interval >= 1000) {
|
|
Serial.print(timer.getCount());
|
|
Serial.println(" counts");
|
|
Serial.print("direction ");
|
|
Serial.println(timer.getDirection());
|
|
Serial.print("Full Revs: ");
|
|
Serial.println(ints);
|
|
interval = millis(); //update interval for user.
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
EENCODER SIMULATION PART.
|
|
|
|
|
|
*/
|
|
/*
|
|
*
|
|
* Protocol...
|
|
*
|
|
* if received F - Move forward.
|
|
* if received B - Move BackWard
|
|
* if received 1 - Mode 1 (Channel B counts)
|
|
* if received 2 - Mode 2 (Channel A counts)
|
|
* if received 3 - Mode 3 (Counts on both channels)
|
|
* if received 4 - Change prescaler to 4
|
|
* if received 0 - change prescaler to 1
|
|
* if received - - Increase Speed
|
|
* if received + - Decrease Speed
|
|
*/
|
|
|
|
//take care of comms...
|
|
if (Serial.available() > 0) {
|
|
received = Serial.read();
|
|
if (received == 'F' || received == 'R') dir = received; //direction. Forward or Reverse.
|
|
if (received == '1') timer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER1); //count only the pulses from input 1
|
|
if (received == '2') timer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER2); //count only the pulses from input 2
|
|
if (received == '3') timer.setEdgeCounting(TIMER_SMCR_SMS_ENCODER3); //count on both channels (default of the lib).
|
|
if (received == '4') timer.setPrescaleFactor(4); //only updates on overflow, so you need to wait for an overflow. Not really used...
|
|
if (received == '0') timer.setPrescaleFactor(1); //only updates on overflow, so you need to wait for an overflow.
|
|
if (received == '-') freq+=50; //increase speed.
|
|
if (received == '+') {
|
|
freq-=50;
|
|
if (freq <= 0) freq = 100; //smallest is 10 ms.
|
|
}
|
|
}
|
|
|
|
//simulate encoder pulses.
|
|
if ( millis() - time >= freq) {
|
|
time = millis(); //prepare next
|
|
|
|
if (dir == 'F') mode++;
|
|
if (dir == 'R') mode --;
|
|
|
|
digitalWrite(4, (states[mode%4] & 0x01));
|
|
digitalWrite(5, (states[mode%4] >> 1));
|
|
}
|
|
|
|
}
|