Making an FM radio-Part 2; the incremental encoder

Despite being a digital radio capable of doing a lot of stuff I wanted a simple design with a knob for volume and another one to change station. The volume is just a potentiometer and for the stations I wanted to use an encoder, unfortunately I didn’t have one. What I did have is a hard drive motor and as it turns out you can use them as rotary encoders. I found two ways to do this; one is to use the signals from the hall sensors and the other one is to use the back fem generated by the winding. The motor has all the electronics sealed making it difficult to figure out how to use the sensors so I used the second method. I found two projects that use brushless motors as encoders taking advantage of the back EMF. In one op-amps are used as amplifiers and the outputs are sampled with a microcontroller’s ADC, in the other the op-amps are used as comparators thus generating a couple of square waves. With the second approach I could use interruptions instead of sampling both ADC channels all the time so I went for the comparator setup.
For the first tests I used only a couple of led at the op-amp outputs to see if the circuit was doing something. Then I watched the outputs with a logic analyzer and let me tell you, it wasn’t pretty. Anyway, I searched for ways to read encoder signals with a microcontroller and I found this. I copied that code and add it a few lines to have a couple of pins generating pulses depending on the direction of rotation.

Unfortunately that code was made to be used with a real encoder with a much cleaner signal. In the picture I’m moving the motor in one direction but both outputs (channel 2 and 3) are generating pulses.

If you use an optical encoder all you have to do is detect a flank (either low->high or high->low) in one signal and check the value of the other one. With my “encoder” I have to detect both flanks and check the values of the other signal in those situations to see if an acceptable sequence has occurred.  And after a few changes I got this;

The first picture shows the signals generated when I move the motor fast first in one direction and then in the other. In the second picture I’m moving the motor slower (as if I’m looking for a station) in only one direction.  The signal is worst when I turn it slower but the microcontroller managed to detect a few pulses which I’ll be using to control the radio. Finally the code;

#include <msp430g2452>;
#include "stdint";
int aux=0;
int edge=1; //0=high-&gt;low, 1=low-&gt;high
int dir=0;
int accept=0;
void main( void )
 {
 // Stop watchdog timer to prevent time out reset
 WDTCTL = WDTPW + WDTHOLD;
BCSCTL1 = CALBC1_16MHZ; // Set range
DCOCTL = CALDCO_16MHZ; // SMCLK = DCO = 16MHz
P1REN |= BIT1 + BIT2;  // select pullup resistors for pin 1.1 and 1.2
P1DIR |= BIT5 + BIT4;  // outputs for testing
P1OUT |= BIT1 + BIT2;  // need to set P1.1 and P1.2 to high so PULLUP resistor will be selected
P1IES &= ~ BIT2;  // select low to high transition for interrupts on all port pins (this actually the default)
P1IE  |= BIT2;  // enable interrupt for P1.2 to reader status of encoder every time Encoder sigal A goes high
P1IFG = 0x00;  // clear interrupt register
__bis_SR_register(LPM0_bits + GIE);
for (;;) {
}
}
#pragma vector = PORT1_VECTOR
 __interrupt void InterruptVectorPort1()
 {
 if( P1IFG &amp; BIT2 ) {
    switch(edge){
     case 1://always start with the 0-&gt;1 edge
      aux=( P1IN &amp; BIT1 );//save the other channel input
      P1IES =BIT2; //Edge sensitivity
      edge=0;//the next interruption will be in 1-&gt;0
      accept=0;//reset value
     break;
     case 0:
      if((P1IN & BIT1)==aux){accept=0;}//Not a good pulse sequence;
      else{accept=1;//Good pulse sequence
      P1IES &= ~BIT2 ; // Toggle Edge sensitivity
      edge=1;
      if(P1IN & BIT1){dir=2;}//save direction of movement
      else{dir=1;}
     break;
 } if(accept){//if a good sequence is detected...
    switch(dir){//output a short pulse in the correct direction
     case 2:
      P1OUT |= BIT4; // output P1.4
      __delay_cycles(1500);
      P1OUT &= ~BIT4;
     break;
    case 1:
      P1OUT |= BIT5; // output P1.5
     __delay_cycles(1500);
     P1OUT &= ~BIT5;
    break;
 }
 }
 P1IFG&= ~ BIT2;
 }
}
Advertisements