Rotary Dial Arduino Input
by jakeofalltrade in Circuits > Arduino
5404 Views, 27 Favorites, 0 Comments
Rotary Dial Arduino Input
This project was intended to serve as a proof of concept for using a rotary phone’s dial as an interface between a user and the Arduino microcontroller. However, this experiment quickly turned into an opportunity to learn all about the Atmega328P’s various timer and interrupt capabilities and how to manually set registers within the Arduino IDE in order to utilize them and handle IO operations more efficiently. This section includes a brief video demonstration of the rotary dial's use as an input to a microcontroller.
Connecting the Rotary Dial to the Arduino
While disassembling a few legacy phones, I was excited to learn that their dials are completely self-contained devices that operates as mechanical switch. When the user draws the dial back to a specific number the switch will periodically open that number of times, with a specific timing that is dictated by the rotary dial’s inner clockwork. In order to test the possibility of using the device to act as a sort of keypad, the circuit depicted in the top diagram was constructed. Both LEDs have their own digital pins (D3 and D4) which are simply used to communicate whether the correct predetermined code has been entered into the dial. The microcontroller counts the low pulses at the rotary dials pin (D2) in order to determine which number has been entered.
Coding for Rotary Dial Input
Though the circuit is very straight forward, programming for this operation required an intimate knowledge of the Atmega328P’s timers. A timer had to be utilized in unique ways in order to distinguish one pulse from another, debounce the rotary dial's switching, and decide when to stop counting pulses for one digit and start counting pulses for the next digit. The project has left me with a much deeper understanding of the registers used to control the 328P and a comfortability with the technical documentation involved. Below is the entire program used to run the demonstration in the introductory video.
#define dialPin 2 #define redLED 4 #define greenLED 3 bool checkPin ( int *input, int *truePin ); bool keep_count = 1; int Pindex = 0; volatile int Cycle = 0; volatile int Tick = 0; int inputPin[5] = { 0, 0, 0, 0, 0}; //we add an extra element to avoid buffer overflow int truePin [4] = { 1, 7, 3, 8}; void setup() { pinMode (dialPin, INPUT); pinMode (redLED , OUTPUT); pinMode (greenLED,OUTPUT); pinMode ( 11, OUTPUT); digitalWrite(11, LOW); noInterrupts(); TCCR2A = 0; // Timer/Counter ContRol Registers used to set mode, prescalar, other options TCCR2B = 0; TCCR2B |= ( 1 << CS22); // we set the Clock Select bit of this register so that our prescalar is 64 // desired f = 1000 Hz // Clock Speed /( prescaler * ( counter max - start of count ) ) = f // 16 000 000 / ( 64 * ( 256 - x ) ) = 1000 Hz // x = 6 TCNT2 = 6; // Timer CouNTer register. This is what is incremented. We start OUR specific count at 48 TIMSK2 = 0; TIMSK2 |= ( 1 << TOIE1 ); // on the Timer Interrupt Mask Register the TimerOverflowInteruptEnable bit is set Serial.begin (9600); } //IMPORTANT NOTE: with an 8 bit counter running with a prescalar of 64 and an initial count of 6 we get about 1000Hz //So we want 3 seconds at 1000 cycles per second = MAX_CYCLES of 3000 //For a debounce of 50ms we likewise need 75 cycles #define MAX_CYCLES 1500 //for 1.5 seconds #define MAX_WAIT 20 // for 20 ms debounce void loop() { Pindex = 0; digitalWrite ( redLED, HIGH ); digitalWrite ( greenLED, LOW); while ( Pindex < 4 ) { noInterrupts(); Cycle = 0; keep_count = 1; if ( digitalRead(dialPin) == HIGH ) { interrupts(); //inputPin [Pindex] ++; //wait_a_tick(1); while ( Cycle < MAX_CYCLES ) { if ( keep_count && digitalRead( dialPin ) == HIGH) { inputPin [Pindex] += 1; keep_count = 0; wait_a_tick(1); } else if ( digitalRead ( dialPin ) == LOW ) { keep_count = 1; wait_a_tick(1); } } noInterrupts(); TCNT2 = 0; Pindex ++; } Serial.print(inputPin [0] );Serial.print(inputPin [1] ); Serial.print(inputPin [2] );Serial.print(inputPin [3] ); Serial.print("\n"); } if ( checkPin ( inputPin, truePin ) )// Lets check to see if our pin number is right { digitalWrite ( redLED, LOW ); digitalWrite ( greenLED, HIGH); interrupts(); wait_a_tick(100 ); //1 tick = 20ms so 2 seconds = 100 ticks } memset ( inputPin, 0, sizeof(inputPin)); // we reset the entered pin to zero } /////////////////////////////////// bool checkPin ( int *input, int *truePin ) { bool pinCorrect = 1; for ( int i = 0; i < 4; i ++ ) { if ( (*(input + i)) != (*(truePin + i)) ) { pinCorrect = 0; } } return pinCorrect; } ///////////////////////////////////////// void wait_a_tick (int ticks) { for ( int tocks = 0; tocks <= ticks; tocks ++) { Tick = 0; while ( 1 == 1 ) { if ( Tick >= MAX_WAIT ) { break; } } } return; } /////////////////////////////////////////////////// ISR(TIMER2_OVF_vect) // timer compare interrupt service routine { Cycle ++; Tick ++; }