Bascom and AVR, Encoders


Encoders are a special type of rotary switches. Most encoder have an A and B output, that switches as the encoder is turned:


Encoders come in two variants: contacting and optical. A contacting encoder has switches that close or open as the encoder is turned. As the switches are contacting, you must consider contact bounce. Read more on contact bounce if you need to. Contacting encoders often have detents that 'click' through an entire A/B cycle per detent.
Optical encoders have a slotted disc with two led/photo-transistor combinations. These sensors are arranged around the disc such that when the disc is turned, the A/B pattern is output. Take an old-fashioned computer mouse apart and you'll understand.
Contacting encoders usually have three pins: the centre pin goes to Ground, the other two are A and B that switch to Ground. So, reading the switches involves no more than connecting them to two AT90S2313 inputs. I always use 10k pull-ups on these input pins.
Optical encoders often have pull-up resistors built-in. (Check the datasheet!) They have a power-supply, a Ground and A/B pins. Sometimes they have an 'index' output, a pin that switches independently of A/B. Optical encoders have a transistor that does the switching, so you need not worry about contact bounce.
Both type of encoders sometimes have an extra switch that is operated by pushing the shaft. This can be a nice sort of 'Enter' button.
Let us start with attaching a contacting or optical encoder to the AT90S2313:


And begin with a simple program that continuously reads the A/B switches:
test-encoder.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pind.2 = Input
Config Pind.3 = Input

Const Debouncetime = 25


Dim Wtime As Byte
Dim Encounter As Integer


Cls

Wtime = 100
Encounter = 0

Set Portd.2
Set Portd.3

Do
  Cls
  Lcd Pind.2 ; " " ; Pind.3
  Waitms Wtime
Loop

End
In the Do Loop, the state of Pind.2 and Pind.3 are displayed on the LCD.

Using a contacting encoder

I started with a Bourns ECW1J-B24-AC0024 contacting encoder. Such an encoder has contact bounce:


although it is not much. The datasheet states a conservative maximum of 5 milliseconds when turning the encoder at 15 rpm. I used a debounce time of 1 millisecond in the following program:
encoder-bourns-ecw1j.bas
$regfile = "2313def.dat"
$crystal = 4000000

Cha Alias Pind.2
Chb Alias Pind.3

Config Cha = Input
Config Chb = Input
Config Pind.6 = Output

Config Int0 = Falling
Const Debouncetime = 1

Dim Wtime As Byte
Dim Encounter As Integer

Set Chb

On Int0 Getencoder

Cls

Wtime = 100
Encounter = 0

Enable Interrupts
Enable Int0

Do
  Set Portd.6
  Waitms Wtime
  Upperline
  Lcd "encounter: " ; Encounter ; "     "
  Reset Portd.6
  Waitms Wtime
Loop

Getencoder:
  Waitms Debouncetime
  If Cha = 0 Then
    If Chb = 0 Then Incr Encounter Else Decr Encounter
  End If
  Gifr = 64
Return

End
Pin Portd.2 is Alias'ed to Cha, Portd.3 to Chb. Both are configured to Input.
Int0 is configured to occur on trailing edges of Cha input.
Debouncetime is set to 1 millisecond.
In the Do Loop the Led is switched on and off and the value of encounter is put on the Lcd.
In the interrupt routine Getencoder, a debouncetime is executed, a check is done on Cha (must be low) and if Chb is low, encounter is incremented, else decremented.

Formally, the check on Cha = 0 in the interruptroutine is not necessary; we are in the interrupt routine >because< Cha went from high to low! I have found this check to be useful however, especially with contacting encoders that sometimes 'jitter' between two A/B states.

Using an optical encoder

I also tried a HP, now Agilent 500 cycles-per-revolution, TTL-output, HEDS-5700 optical encoder: (This type is not on Agilent's web-site anymore, try: HEDS-550 for a similar type)
Optical encoders do not have contact bounce:

(note the time-base setting)
encoder-hp-heds-5700.bas
$regfile = "2313def.dat"
$crystal = 4000000

Cha Alias Pind.2
Chb Alias Pind.3

Config Chb = Input
Config Pind.6 = Output

Config Int0 = Falling

Dim Wtime As Byte
Dim Encounter As Integer

Set Chb

On Int0 Getencoder

Cls

Wtime = 100
Encounter = 0

Enable Interrupts
Enable Int0

Do
  Set Portd.6
  Waitms Wtime
  Upperline
  Lcd "encounter: " ; Encounter ; "     "
  Reset Portd.6
  Waitms Wtime
Loop

Getencoder:
  If Chb = 0 Then Incr Encounter Else Decr Encounter
  Gifr = 64
Return

End
In the interrupt routine, the debounce is omitted as well as the Cha = 0 check.

A 'special' encoder
I have tested an ALPS contacting encoder, that does not have an A/B output, but instead an A output that pulses closed when turned left (B is then open) and a B output that pulses closed when turned right (A is then open). I could not find this type on the ALPS web-site. This type of encoder is the easiest to use: both A and B output can be connected to an interrupt pin:
encoder-alps-special.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output

Config Int0 = Falling
Config Int1 = Falling

Dim Wtime As Byte
Dim Encounter As Integer

Const Debouncetime = 5

On Int0 Encodera
On Int1 Encoderb

Cls

Wtime = 100
Encounter = 0

Enable Interrupts
Enable Int0
Enable Int1

Do
  Set Portd.6
  Waitms Wtime
  Upperline
  Lcd "encounter: " ; Encounter ; "     "
  Reset Portd.6
  Waitms Wtime
Loop

Encodera:
  Waitms Debouncetime
  Incr Encounter
  Gifr = 64
Return

Encoderb:
  Waitms Debouncetime
  Decr Encounter
  Gifr = 128
Return
End
Both Int0 and Int1 is used.
I used a debouncetime of 5 milliseconds.
The interrupt routines only have an increment or decrement, no Cha or Chb testing is required.
Note the Gifr = 64/128. Read more on this.

TOC