/*
Rotary_Encoder_LED_Ring_Example
www.mayhewlabs.com/products/rotary-encoder-led-ring
Copyright (c) 2011 Mayhew Labs.
Written by Mark Mayhew
This example shows 3 sequences that are possible on the ring of LEDs around the encoder based on rotation of an encoder. The 3 sequences are
selected by entering 1, 2, or 3 in the serial command prompt. The serial window shows the current rotary encoder count. As the encoder is turned,
the serial display shows a raw count of the rotary encoder's value and the LEDs show a scaled version of the value. If the button on the rotary
encoder is pushed, the bottom LED will come on. Each section of code below discusses the process required to do this.
A note on setting the output sequence:
Think of each LED as a single bit in a 16-bit long binary string. If a bit is 1, the LED is on, if a bit is 0, the LED is off.
By making a string of ones and zeros, we choose which LEDs to have on and off, and then send this string to the shift register to display it.
For example 1000000000000001 binary (0x8001 in hex) will have the fist and last LEDs on, the rest off.
CREDIT:
Reading the rotary encoder is performed with Oleg's example code:a
http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino
*/
//These are the pins that will talk to the shift register through SPI
#define SDI 2 //Data
#define CLK 3 //Clock
#define LE 4 //Latch
//These are the rotary encoder pins A, B, and switch
#define ENC_A 8
#define ENC_B 9
#define ENC_SW 10
#define ENC_PORT PINB //The port that the rotary encoder is on (see http://www.arduino.cc/en/Reference/PortManipulation)
// Global variables
int scaledCounter = 0; //The LED output is based on a scaled veryson of the rotary encoder counter
int sequenceNumber=0; //The output sequence for the LEDs
int incomingByte = 0; //Serial input to select LED output sequence
/*This is a 2 dimensional array with 3 LED sequences. The outer array is the sequence;
the inner arrays are the values to output at each step of each sequence. The output values
are 16 bit hex values (hex math is actually easier here!). An LED will be on if its
corresponding binary bit is a one, for example: 0x7 = 0000000000000111 and the first 3 LEDs
will be on.
The data type must be 'unsigned int' if the sequence uses the bottom LED since it's value is 0x8000 (out of range for signed int).
*/
unsigned int sequence[3][16] = {{0x0,0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000},
{0x0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff},
{0x0,0x7fff,0x3ffe,0x1ffc,0xff8,0x7f0,0x3e0,0x1c0,0x80,0x1c0,0x3e0,0x7f0,0xff8,0x1ffC,0x3ffe,0x7fff},
};
void setup()
{
//Set SPI pins to output
pinMode(SDI, OUTPUT);
pinMode(CLK, OUTPUT);
pinMode(LE,OUTPUT);
//Set encoder pins to input, turn internal pull-ups on
pinMode(ENC_A, INPUT);
digitalWrite(ENC_A, HIGH);
pinMode(ENC_B, INPUT);
digitalWrite(ENC_B, HIGH);
pinMode(ENC_SW, INPUT);
digitalWrite(ENC_SW, HIGH);
//Set serial rate, prompt for desired sequence
Serial.begin(115200);
Serial.println("Enter 1, 2, or 3 to change the LED sequence");
}
void loop()
{
//Local Variables
static uint8_t counter = 0; //this variable will be changed by encoder input
int8_t tmpdata;
//Get any serial input
if (Serial.available() > 0)
{
incomingByte = Serial.read();
}
//if the serial input is valid, set the LED output sequence appropriately
if (incomingByte == '1')
sequenceNumber=0;
if (incomingByte == '2')
sequenceNumber=1;
if (incomingByte == '3')
sequenceNumber=2;
//Call read_encoder() function
tmpdata = read_encoder();
//if the encoder has moved
if(tmpdata)
{
//Print out the counter value
Serial.print("Counter value: ");
Serial.println(counter, DEC);
//Set the new counter value of the encoder
counter += tmpdata;
//Scale the counter value for referencing the LED sequence
//***NOTE: Change the map() function to suit the limits of your rotary encoder application.
// The first two numbers are the lower, upper limit of the rotary encoder, the
// second two numbers 0 and 14 are limits of LED sequence arrays. This is done
// so that the LEDs can use a different scaling than the encoder value.
scaledCounter = map(counter,0,100,0,15);
//Send the LED output to the shift register
digitalWrite(LE,LOW);
shiftOut(SDI,CLK,MSBFIRST,(sequence[sequenceNumber][scaledCounter] >> 8)); //High byte first
shiftOut(SDI,CLK,MSBFIRST,sequence[sequenceNumber][scaledCounter]); //Low byte second
digitalWrite(LE,HIGH);
}
//If the encoder switch is pushed, this will turn on the bottom LED. The bottom LED is turned
//on by 'OR-ing' the current display with 0x8000 (1000000000000000 in binary)
if (!digitalRead(ENC_SW))
{
digitalWrite(LE,LOW);
shiftOut(SDI,CLK,MSBFIRST,((sequence[sequenceNumber][scaledCounter]|0x8000) >> 8));
shiftOut(SDI,CLK,MSBFIRST,sequence[sequenceNumber][scaledCounter]);
digitalWrite(LE,HIGH);
}
}
/*************************************************************************
* read_encoder() function as provided by Oleg: *
* http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino *
* *
* Returns change in encoder state (-1,0,1) *
************************************************************************ */
int8_t read_encoder()
{
int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint8_t old_AB = 0;
/**/
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}