====== Encoder ======
//Required knowledge:
[HW] [[et:hardware:homelab:digi]], [HW] [[et:hardware:homelab:combo]],
[LIB] [[et:software:homelab:library:module:encoder]], \\
[LIB] [[et:software:homelab:library:module:lcd_graphic]],
[AVR] [[et:avr:timers]], [AVR] [[et:avr:interrupts]]//
===== Theory =====
[{{ :et:examples:sensor:kooder_optron.jpg?220|Optocoupler}}]
An encoder is an electro-mechanical device that converts shaft rotation angle or angular velocity into an analog signal or a digital code. There are also linear encoders, which differ from rotary encoders in that their movement is along a straight line. Otherwise the operating principle is similar.
There are two main types of encoders: absolute and incremental (counting). The output of an absolute encoder shows the current shaft position. After power loss, the position can be determined again when power is restored. In the picture below, the right encoder disk is a simple absolute encoder disk read by three sensors. The resulting binary code allows measuring shaft position with 45-degree steps.
[{{ :examples:sensor:encoder:kooder_kettad.png?300|Encoder disk (left incremental, right absolute)}}]
The output of an incremental encoder provides information about shaft movement that can be processed further. Typically speed and direction are computed. An incremental encoder does not allow determining the exact shaft position after power loss. It produces a cyclic output signal only when the shaft rotates.
An incremental optical encoder consists of a disk with slots and an optocoupler. The optocoupler is a system of an IR LED and a phototransistor with the slotted disk between them, so that as the disk rotates, the slots periodically interrupt light and thus switch the phototransistor on and off. This produces a square wave at the phototransistor output, which can be used to calculate angular velocity. Encoders with a single optocoupler allow measuring only rotation speed from pulse frequency. Direction information is missing. In addition to optical encoders, Hall-effect encoders are widely used. Such an encoder uses a disk with alternating magnetic field or a permanent magnet, which generates voltage pulses in a semiconductor as it rotates. The pulses are amplified and converted to a digital signal for the controller. Reading encoder signals is similar to reading and interpreting optical encoder signals.
To determine rotation direction, a second optocoupler is needed. If the optocouplers are positioned to create a 90-degree phase shift between their signals, the outputs are in quadrature. The state diagram for optocoupler signals is shown in the image below, where the outputs are labeled A and B. Outputs are read in software, typically using interrupts triggered on rising or falling edges. Direction can be determined by comparing output codes. For example, if the last value is 00 and the new value is 01, then the shaft has rotated one half-step clockwise.
[{{:et:examples:sensor:koodri_graafika.png?350|Encoder operating principle}}]
^ ^ Clockwise \\ rotation ^^ ^ Counterclockwise \\ rotation ^^
^ Phase ^ A ^ B ^ ^ A ^ B ^
| 1 | 0 | 0 | | 1 | 0 |
| 2 | 0 | 1 | | 1 | 1 |
| 3 | 1 | 1 | | 0 | 1 |
| 4 | 1 | 0 | | 0 | 0 |
===== Practice =====
In HomeLab, encoder inputs are connected to timer pins. You can use both Hall-effect and optical encoders. Electrical connections are described in the hardware description of the respective board.
The HomeLab library allows reading encoder pulses. Pulse counting occurs via interrupts generated on the falling edge of the pin. When the interrupt occurs, the pulse count for the encoder is incremented by one.
To use an encoder, the HomeLab library provides //encoder_init//, which configures the encoder pins as inputs and sets up the interrupt. The function //encoder_get_pulses// returns the number of pulses, and //encoder_reset_pulses// resets the pulse count of the selected encoder.
Below is an example of reading encoders with the ATmega2561 controller.
// Number of encoders
#define NUM_ENCODERS 2
// Encoder pulse count per second
#define ENCODER_TICKS (F_CPU / 8 / 256)
// Pin definitions
static pin encoder_pins[NUM_ENCODERS] =
{
PIN(E, 6),
PIN(E, 7)
};
typedef struct
{
unsigned short num_pulses;
}
encoder_data;
static encoder_data encoder[NUM_ENCODERS];
// Interrupt handler
void encoder_pulse(unsigned char index)
{
encoder[index].num_pulses++;
}
// Interrupts
ISR(INT6_vect) {
encoder_pulse(0);
}
ISR(INT7_vect) {
encoder_pulse(1);
}
// Encoder initialization
void encoder_init(unsigned char index)
{
// Configure pin as input with pull-up resistors enabled
pin_setup_input_with_pullup(encoder_pins[index]);
// Enable external interrupt on falling edge
switch (index)
{
case 0:
bit_set(EICRB, ISC61);
bit_clear(EICRB, ISC60);
bit_set(EIMSK, INT6);
break;
case 1:
bit_set(EICRB, ISC71);
bit_clear(EICRB, ISC70);
bit_set(EIMSK, INT7);
break;
}
// Initialize
encoder[index].num_pulses = 0;
}
// Reset selected encoder
void encoder_reset_pulses(unsigned char index)
{
cli();
encoder[index].num_pulses = 0;
sei();
}
// Get encoder pulse count
unsigned short encoder_get_pulses(unsigned char index)
{
return encoder[index].num_pulses;
}
The example program below demonstrates use of the functions by displaying the encoder pulse count on the screen. Button S2 resets the encoder and starts counting again.
// HomeLab encoder usage example program
// Encoder pulse count is displayed on the LCD
#include
#include
#include
#include
#include
// Main program
int main(void)
{
unsigned short pulses = 0;
char text[16];
// Configure button
pin_setup_input_with_pullup(S2);
// Encoder initialization
encoder_init(1);
// Reset and start counting
encoder_reset_pulses(1);
// LCD initialization
lcd_gfx_init();
lcd_gfx_clear();
lcd_gfx_backlight(true);
lcd_gfx_goto_char_xy(3, 1);
lcd_gfx_write_string("Encoder");
// Infinite loop
while (1)
{
pulses = encoder_get_pulses(1);
// Build text.
sprintf(text, "Pulses: %5d",pulses);
lcd_gfx_goto_char_xy(0, 3);
lcd_gfx_write_string(text);
// Is button S2 pressed?
if(button_read(S2))
{
// Reset and start counting
encoder_reset_pulses(1);
// Overwrite old text with spaces
lcd_gfx_write_string(" ");
}
// Pause 10 milliseconds
sw_delay_ms(10);
}
}