Applied Robotics/Microcontrollers/Reading Sensors

From Wikibooks, open books for an open world
Jump to: navigation, search

Reading Digital Inputs[edit]

Simple 1-bit digital sensors are the simplest type of input to read on a microcontroller. Most pins on microcontrollers are usable as general purpose I/O, which allows for the interfacing of numerous 1-bit digital sensors. Examples of these sensors might be limit switches, IR proximity detectors, high gain photodetectors (light sensors), or pushbuttons.

Binary on/off switching type sensors are very easy to integrate simply by having a pull-up or pull-down resistor connected to the switch output.

To read a sensor on an AVR microcontroller, two registers must be used to handle pin setup and reading. These two registers are:

  • DDRx
  • PINx

Where the x indicates an arbitrary I/O port (i.e. A, B, C, D, etc.)

The DDR (Data Direction Register) is used to configure the specified pin as an input or output for a given port. A "1" bit for each location specifies that that port pin is set as an output, and a "0" bit for each location specifies that the pin is configured as an input.

The PIN register is used for reading the digital state of all 8 pins on each port, returning a 1 for logic high and 0 for logic low states for each bit. Note that this is not the PORTx register. PORTx is used for setting output values, while PINx is used for reading inputs.

To use a digital input, you must configure the pin as an input, and then read the appropriate bit from the PINx register. The simplest way to extract a single bit from PINx is to use a bitmask.

DDRA = 0b00000000; // All PORTA pins configured as inputs
 
if (PINA & 0b00000100) {
  // if PA2 is high
}
else {
 // if PA2 is low
}
 
if ((PINA & 0b00001000) == 0) {
 // if PA 3 is low
}

In these examples, the bitmask is used to remove the state of all other pins on the port from the conditional. Like with digital outputs, you can use AVR-GCC macros to make your code easier to read:

if ((PINA & 1<<PIN3) == 0) {
 // if PA 3 is low
}

Reading Analog Inputs[edit]

Many sensors measure a continuous signal, such as the position of a knob, the light level in the room, or the distance of an object from a sensor. Often, the output of these sensors is a voltage which is linearly related to the measured signal. To interface with these sensors, you must use an Analog Digital Converter (ADC). Most microprocessors contain several ADCs. An ADC converts the voltage into an integer, the size of which depends on the accuracy of the ADC. Most ADCs are 8- or 10- bit, meaning they use 8 or 10 bits to represent the entire range (from 0 to Vcc). A 10-bit ADC discretizes the analog voltage into values, ranging from 0 (0 V) to 1023 (Vcc).

AVR's ADCs require several steps to configure and read an analog signal. In addition, only pins on the microcontroller can be used as analog inputs. Refer to your microcontroller's datasheet to find which pins can be used with the ADC (they are typically labeled ADCn, where n is a number). The registers involved with ADCs are:

  • PINx: Configures pin as an input
  • ADMUX: Selects the ADC input being read
  • ADCSRA: ADC Control Register
  • ADCL: Low 8 bits of the ADC conversion
  • ADCH: High bits of the ADC conversion

Because ADC pins can also be used as digital inputs/outputs, they must be configured as an input to be read by the ADC. The ADMUX register is used to select which ADC is being converted, and for single-ended inputs, is simply a number between 0..N, where N is the last ADC pin on the MCU. ADCSRA stands for ADC Control And Status Register A, and is used to begin a conversion and check when it has completed. ADCL and ADCH contain the result of the conversion.

// Configuration:
DDRF &= ~(1<<PIN0); // Configure PF0 as an input (connected to ADC0 on AT90USB64)
 
// Reading ADC:
ADMUX = 0; // Read ADC0
ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADPS0); // Enable ADC (ADEN), Start a Conversion (ADSC), Fast conversion (ADPS0)
while ((ADCSRA & (1<<ADIF))==0); // Wait for Conversion to Complete
unsigned short value = (ADCL) | (ADCH<<8); // Combine low and high portions of result