millwood
Advanced Member level 3
I was asked if I know of a way to read a rotary encoder.
so I dug this out of my old code and simplified it to make it easier to understand.
The code was originally written for a LPC21xx class device.
the key routine is the encoder_read(port, keyab) routine, that reads off keya and keyb on port. It can return two types of values:
1) it can return the absolutely value of the encoder position, as it is written now;
2) or it can return the relative value of the encoder, if you uncomment the return line that is commented out. those values would be -1 (indicating counter clock wise turn of the encoder), 0 (indicating no turn of the encoder, or an invalid turn of the encoder), or +1 (indicating a clock-wise turn of the encoder).
---------- Post added at 07:50 PM ---------- Previous post was at 07:47 PM ----------
the principle is actually very simple: the code stores the previous read of Key_A and Key_B, as a two bit number, and reads the current state of Key_A and Key_B, and form a four bit index where by Key_A's previous state is bit 3, Key_B's previous state is bit 2, Key_A's current state is bit 1, and Key_B's current state is bit 0. That four bit index is used to return the relative movement of the encoder from KEY_AB_states, which itself is based on a state machine.
The encoder's initial position is reset to zero but in your code, you can read it off eeprom if you wish to give the routine some "memory" of its previous states before power-off.
here is a quick simulation where the routine is used to send its absolute position to a mcu to be displayed on an LCD.
so I dug this out of my old code and simplified it to make it easier to understand.
The code was originally written for a LPC21xx class device.
Code:
#include <REGX51.H>
#include <string.h>
#include "gpio.h"
#include "lcd_4bit.h"
//hardware configuration
#define OUT_PORT P3 //leds on p3
#define OUT_DDR P3
#define OUTs 0xff //output on pin 8 - 15
#define KEY_PORT P2 //inputs on p2
#define KEY_DDR P2
#define KEY1 (1<<0) //p2.0
#define KEY2 (1<<6) //p2.6
#define KEYs (KEY1 | KEY2)
#define KEY_A KEY1 //encoder's output A to KEY1
#define KEY_B KEY2 //encoder's output B to KEY2
//end of hardware configuration
//port related macros
//#define IO_FLP(port, bits) port ^= (bits) //flip bits on port
//#define IO_SET(port, bits) port |= (bits) //set bits on port
//#define IO_CLR(port, bits) port &=~(bits) //clear bits on port
//#define IO_GET(port, bits) (port & (bits)) //get bits on port
//#define IO_IN(ddr, bits) ddr &=~(bits) //bits as input
//#define IO_OUT(ddr, bits) ddr |= (bits) //bits as output
//define port type:
//"unsigned int" (a 32-bit type) for 32-bit ports
//"unsigned char" for 8-bit ports
//change PORT_TYPE if you are compiling for a differrent platform
//#define PORT_TYPE unsigned long //works for 32-bit ports
#define PORT_TYPE unsigned char //works for 8-bit ports
const unsigned char str0[]="89C51RotarySwitch";
const unsigned char str1[]="Encoder= ";
unsigned char vRAM[17]; //display buffer
unsigned char pos; //encoder position
unsigned char pos_prev; //encoder previous position
//determine increment / decrement of the encoder
unsigned char encoder_read(PORT_TYPE port, PORT_TYPE pins) {
const signed char KEY_AB_states[]={0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
static unsigned char encoder_output=0x00;
static unsigned char KEY_AB=0x00; //AB key read out, Previous in the high 2 bits and current in the low two bits;
unsigned char tmp;
KEY_AB <<=2; //bit 2..3 now contain the previous AB key read-out;
tmp=IO_GET(port, pins); //read ab pins
if (tmp & KEY_A) KEY_AB |= 0x02; //set the 1st bit if A is high now;
if (tmp & KEY_B) KEY_AB |= 0x01; //set the 0th bit if B is high;
KEY_AB &= 0x0f; //only retain KEY_AB' last 4 bits (A_previous, B_previous, A_current, B_current)
encoder_output += KEY_AB_states[KEY_AB];
return encoder_output; //if you want to return absolute steps.
//return KEY_AB_states[KEY_AB]; //if you want to return relative steps (+1, 0, -1).
}
void ultoa(unsigned char * str, unsigned long ul, unsigned char length) {
do {
str[length--]=(ul % 10) + '0'; //conver the lowest digit
ul = ul / 10;
} while (ul);
}
//initialize the mcu
void mcu_init(void) {
IO_CLR(OUT_PORT, OUTs); //clear LED
IO_OUT(OUT_DDR, OUTs); //leds as output
IO_IN(KEY_DDR, KEYs); //keys as input
}
//demo program
int main(void) { //main
mcu_init(); //initiate the mcu
lcd_init(); //initiate the lcd
lcd_display(LCD_Line0, str0); //display str0 on lcd_line0
pos_prev=encoder_read(KEY_PORT, KEY_A | KEY_B); //read the encoder and reset pos_prev
while (1) {
//OUT_PORT = encoder_read(KEY_PORT, KEY_A | KEY_B); //read KEY_A and KEY_B and output encoder read-out on OUT_PORT
pos=encoder_read(KEY_PORT, KEY_A | KEY_B); //read the encoder
if (pos ^ pos_prev) { //position has changed
pos_prev=pos; //update pos_prev
strcpy(vRAM, str1); //copy str1 to vRAM
ultoa(&vRAM[8], pos, 7); //convert pos to vRAM
lcd_display(LCD_Line1, vRAM); //display
}
//do something else
}
}
the key routine is the encoder_read(port, keyab) routine, that reads off keya and keyb on port. It can return two types of values:
1) it can return the absolutely value of the encoder position, as it is written now;
2) or it can return the relative value of the encoder, if you uncomment the return line that is commented out. those values would be -1 (indicating counter clock wise turn of the encoder), 0 (indicating no turn of the encoder, or an invalid turn of the encoder), or +1 (indicating a clock-wise turn of the encoder).
---------- Post added at 07:50 PM ---------- Previous post was at 07:47 PM ----------
the principle is actually very simple: the code stores the previous read of Key_A and Key_B, as a two bit number, and reads the current state of Key_A and Key_B, and form a four bit index where by Key_A's previous state is bit 3, Key_B's previous state is bit 2, Key_A's current state is bit 1, and Key_B's current state is bit 0. That four bit index is used to return the relative movement of the encoder from KEY_AB_states, which itself is based on a state machine.
The encoder's initial position is reset to zero but in your code, you can read it off eeprom if you wish to give the routine some "memory" of its previous states before power-off.
here is a quick simulation where the routine is used to send its absolute position to a mcu to be displayed on an LCD.