-
Notifications
You must be signed in to change notification settings - Fork 19
encoder interrupts
Among the many sources of interrupts on the ATMega328p, two are particularly interesting when you ant to interface encoders. These are INT0 and INT1. Each has a dedicated pin on the processor and each has a separate interrupt service vector. This means that the interrupt service can respond quickly to the specific pin without having to waste time working out which pin caused the change.
Each of these pins can be programmed to generate an interrupt when there is a logical change on the pin. The encoders attached to the motors generate a series of pulses that change state 12 times for every rotation of the motor. By using the INT0 and INT1 interrupts, the processor can keep a count of hose changes and so measure the speed of rotation and the distance travelled by the robot.
The ATMega328p can generate interrupt as a result of changes of state on two of its pins:
PD2 for INT0 - Arduino Digital Pin 2 PD3 for INT1 - Arduino Digital Pin 3
The INT0 and INT1 interrupts can be triggered by a change in state on the corresponding pin. This action is set up by writing bits to the External Interrupt Control Register A (EICRA
).
###The EICRA
The External Interrupt 0 is activated by the external pin INT0 if the corresponding interrupt mask is set and interrupts are enabled. The level and edges on the external INT0 pin that activate the interrupt are defined as
ISC01 | ISC00 | Description |
---|---|---|
0 | 0 | Low Level of INT0 generates interrupt |
0 | 1 | Logical change of INT0 generates interrupt |
1 | 0 | Falling Edge of INT0 generates interrupt |
1 | 1 | Rising Edge of INT0 generates interrupt |
The External Interrupt 1 is activated by the external pin INT1 if the corresponding interrupt mask is set and interrupts are enabled. The level and edges on thexternal INT1 pin that activate the interrupt are defined as
ISC11 | ISC10 | Description |
---|---|---|
0 | 0 | Low Level of INT1 generates interrupt |
0 | 1 | Logical change of INT1 generates interrupt |
1 | 0 | Falling Edge of INT1 generates interrupt |
1 | 1 | Rising Edge of INT1 generates interrupt |
To enable these interrupts, bits must be set in the external interrupt mask register EIMSK
EIMSK:INT0
(bit 0) enables the INT0 external interrupt
EIMSK:INT1
(bit 1) enables the INT1 external interrupt
It may have seemed complicated to perform all this configuration but in reality it needs only a few of lines of code:
EICRA |= (0 << ISC01) | (1 << ISC00); // logical change on INT0
EIMSK |= (1 << INT0); // Enable the INT0 interrupt
EICRA |= (0 << ISC11) | (1 << ISC10); // logical change on INT1
EIMSK |= (1 << INT1); // Enable the INT1 interrupt
Once the state of one of the pins changes, the processor will generate an interrupt and the flow of execution will jump to a predetermined location where it expects to find some code that can respond to the interrupt. This is written as a special type of function called an Interrupt Service Routine (ISR). Note that, if the ISR is not present, the processor behaviour is not defined and the program will fail.
To declare an ISR in the arduino environment, you need to use one of the predefined names. For the INT0 and INT1 interrupts, you will need these two ISRs:
ISR(INT0_vect) {
}
ISR(INT1_vect) {
}
If there is no code in the ISR, the processor will clear the interrupt state and immediately return to normal execution.
Each of the robot's encoders behaves the same way. Two pins are used on the processor. One has the combined pulse signal from the XOR gate and is connected to one of the interrupt pins, INT0 or INT1. The other signal is connected to another pin so that the ISR can examine it and work out whether the motor has turned and in what direction. Both ISRs have essentially the same code but operate on different pins. here are the complete ISRs:
int encoderLeftCount;
int encoderRightCount;
ISR(INT0_vect) {
static bool oldA = 0;
static bool oldB = 0;;
bool newB = digitalReadFast(encoderLeftPinB);
bool newA = digitalReadFast(encoderLeftPinAxB) ^ newB;
encoderLeftCount += (oldA ^ newB) - (newA ^ oldB);
oldA = newA;
oldB = newB;
}
ISR(INT1_vect) {
static bool oldA = 0;
static bool oldB = 0;;
bool newB = digitalReadFast(encoderRightPinB);
bool newA = digitalReadFast(encoderRightPinAxB) ^ newB;
encoderRight += (oldA ^ newB) - (newA ^ oldB);
oldA = newA;
oldB = newB;
}
The code is a little hard to understand but it is written to execute as fast as possible. On a 16MHz Arduino, each function will take 3 microseconds to execute so it should be possible to respond to quite high pulse rates without getting the processor bogged down.
Getting Started Pages
Building Guide
- Tools and Materials
- Assemble the main board
- Addendum for V1.3a main board
- Assemble the basic line sensor
- Assemble the mini wide line sensor board
- Assemble the basic wall sensor
- Mounting sensor boards
- Choosing motors
- Fitting Encoders
- Mounting the motors
- Choosing batteries
- Mounting the battery
Reference Pages
Troubleshooting
Developer Notes