Basics of Input / Output
We can study theoretical concepts all day long but unless we get our hands dirty with some practical work, we tend to forget the theory pretty fast. So the time has come for us to start writing code for our first practical project. We will be doing what every other beginner has done for so long – we will be trying to light up an LED. It may seem a bit too simple but it teaches one of the most basic and fundamental concept of how to make a pin HIGH or LOW and reading the state of a pin. This knowledge will be required in every project that you do. Be it reading a sensor and controlling another appliance or sending data to another microcontroller, everything is done by changing the state of pins since in digital electronics, everything is defined by 2 states – HIGH or LOW.
First I will show and explain the code and then, how to use software to write and compile the code. There are various compilers available in the market. Some are free and some are paid but the code is same for all.
I will use a very cheap and widely available microcontroller, Atmega 8 to demonstrate the concepts. The original Atmega 8(16 PU) could run at a maximum frequency of 16 MHz and the supply voltage range was 4.5 – 5.5V. But in present times, the original version is not available. Instead, the Atmega 8L is available. It can work over a large range of voltage 2.7– 5.5V and its maximum working frequency is 8MHz. What it loses in speed, it more than makes up for it with the wide range of supply voltage that it can work with. With the 3.3V devices becoming popular day by day, the Atmega 8L is capable of working with both older 5V devices and the newer 3.3V ones.
And even more recently, another newer version, the Atmega 8A has been released. It can be supplied within the range of 2.7-5.5V and its maximum speed is 16MHz. Basically it mixes the best of the original Atmega 8 and the 8L. All codes which have been written for the Atmega 8/8L will work with the Atmega 8A since it is pin compatible.
The Atmega 8/8L/8A has 23 programmable I/O lines. That is there are 23 pins which can be used for digital input/output. The pins are grouped together into 3 parts – Port B(8 I/O pins), Port C(7 I/O pins), Port D(8 I/O pins). A limitation of the AVR architecture is that individual pins of a Port cannot be accessed for reading/writing. The whole Port has to be addressed and if we need to read or write to a specific pin of a port, we will have to make use of some very simple binary logic. We will come to that in some other tutorial. For now, we will use Port B to input some data and Port D to output some data.
There are 3 registers which control the I/O pins – DDRx, PORTx, PINx (In place of x we have to put the name of the port on which we want to operate).
Using DDRx (data direction register) command, we can configure a pin as an input pin by writing a 0 to the specific bit. And if we write 1 to a bit, that pin will be configured as an output pin.
If we want to configure the pins 7,3,2,1 of Port D as input pin and pins 6,5,4,0 of Port D as output pin, the code for that will be:
DDRD = 0b01110001
The ‘0b’ at the beginning of the value tells the compiler that a binary number sequence will follow. The leftmost number always represents the pin 7 of the port and the rightmost value represents the pin 0 of the port.
PORTx register is used to configure the output state of the pins.
If 1 is written to a bit which has been configured as an output pin, the pin goes HIGH (It will output 5V on that pin since we are experimenting with 5V devices in here).
If 0 is written to a bit which has been configured as an output pin, the pin goes LOW (it will output 0V on that pin).
PORTD = 0b11110000;
Makes the output of pins D7, D6, D5, D4 HIGH and that of the pins D3, D2, D1, D0 LOW (Port D has been configured as an output port).
If we write 1 to a bit which has been configured as an input pin, the pull up resistor of that pin is activated. We will learn more about pull up resistors in the next section.
PINx register is used to read the state of an input pin.
PINx are read only registers. The state of the pins can be known by studying the value of this register.
We use the command:
x = PIND
to read the state of the pins of Port D. The value of the register PIND is copied into the variable x.
If the value of x = 0b00010010, it means that the input to pins D4 and D1 is high while that to the other pins is low.
This microcontroller works on TTL (transistor transistor logic). If the input to a pin is anything in between 2 – 5 V, the controller reads it as HIGH input. While if the voltage level is in between 0 – 0.8 V, the controller reads it as LOW input.
But if the input voltage is between 0.8 – 2 V, we cannot correctly predict what the microcontroller will see. It may classify it as HIGH input or maybe as LOW input. So if there are any conditional loops which depend on the value of that pin, erroneous results will occur.
Pins which have been configured as input pins but have not been connected to an external device like a sensor or something similar, pick up noise from the environment and the voltage level at these pins may be anything from 0V to a few volts. If we read these pins mistakenly, erroneous results will occur. So generally it is advisable to never keep pins in floating state. Usually they should be connected to either Vcc or Ground through a high value resistance(Usually anything between 10k ohm to 50k ohm). The pins connected to Vcc are said to have been pulled up to Vcc. And the high value resistance in series is known as a pull up resistor. The pins which are connected to Ground are said to have been pulled down to ground. The high value resistances in series are known as pull down resistors.
All the ports of Atmega 8 have pull up resistors. The unconnected pins should ideally have their pull up resistors enabled to minimize noise interference.
If we configure a pin as an input pin using the DDRx register and then write a 1 to that pin using the PORTx register, the pull up resistor of that pin is activated.
e.g – we will configure the pins of Port B as input pin. In the circuit we attached a sensor to only pin B0. All the other pins are left open. So we will activate the pull up resistors on the other pins.
DDRB = 0b00000000;
PORTB = 0b11111110;
In the next post, I will write the code and explain how it works.