My Pi Description

My Experiences With the Raspberry Pi -- Tracking My Learning -- My Pi Projects
Showing posts with label AVR Libraries. Show all posts
Showing posts with label AVR Libraries. Show all posts

Friday, March 11, 2016

Interrupts and the Arduino Uno

Introduction

This post is a logical follow-up on my last post: "Programming the ATmega328P Registers From the Arduino IDE". This discussion is about using the Uno's interrupts. You have two ways to monitor the Uno's I/O pins (to monitor switches, for example). You can poll for inputs to change state, or you can use interrupts to sense when a change of state occurs.
Polling means you are just going to check the state of Uno I/O pins. In a simple system this if fine. Polling means your sketch does nothing else while you check the state of pins. Interrupts are a way to check pins without your sketch diverting its attention from its main tasks.
Here is an example taken from a project I have been reporting on in this blog: I have an enclosure that accepts a large number of temperature sensors. Measurements are made and the results are transmitted to a Raspberry Pi for storage and graphing. The enclosure has a 2 line by 16 character display. I use the first line to display a sensor's description, and the second line to display the sensor's latest temperature measurement. Since the display can only show one sensor at a time, I installed a switch that allows me to switch the display between sensors.
I first handled the switch by polling. I included a small routine in my code in a convenient location. This routine looks at the logic level of the pin connected to the switch, and if low, causes the display to go to the next sensor. A lot goes on in my main code, so the routine to check the switch repeats after several seconds have passed. If I'm not pressing the switch when the routine looks, the routine misses the change of state.
The operation just felt awkward. I now monitor the switch by interrupt. Whenever the switch is pressed, an interrupt occurs so my routine in the loop just looks to see if an interrupt occurred, not if my finger is on the switch. Much better operation.
When the condition for an interrupt occurs, a small function, you write, will run immediately. When that function has done all it needs to do, operation is returned to the task your sketch was doing at the time before the interrupt. Since you don't know when the interrupt will occur, you have to look closely at your sketch to assure there are no instances when an interrupt will impact some critical timing. There are ways to handle that situation. Interrupts can be turned on and off at any place in your sketch.

Interrupts and the Uno

If you want to use the Uno's interrupts, the Arduino Language Reference states there are only two pins, pin 2 and pin 3 you can use. I'm here to tell you it is possible to generate an interrupt from every I/O pin on the Uno. Unless you are using pins 2 and/or 3, however, you will not be able to use the Arduino IDE functions attachInterrupt() and detachInterrupt(). You can, however, use Register Programming to program interrupts on any I/O pin.
The interrupt capabilities of pins 2 and 3 are, however, more robust. All of the external interrupts, when enabled, will trigger an interrupt whenever a change of logic level is detected. Pins 2 and 3 have three additional capabilities.
The four capabilities available for pins 2 and 3:
  1. trigger an interrupt whenever the logic level of the pin changes (high to low or low to high).
  2. only trigger an interrupt whenever the logic level changes from a low to a high.
  3. only trigger an interrupt whenever the logic level changes from a high to a low.
  4. trigger an interrupt whenever the logic level of the pin is low.
Every pin operates with choice 1. Only pins 2 and 3 have the additional three choices.
If you are designing a new project, and you know you are going to use interrupts to monitor sensors or switches, certainly plan to use pins 2 and 3. If you need to utilize interrupts from other pins, or would like to learn the advantages of register programming interrupts, read on.

A Little Review

From my last post, we will need knowledge of the ATmega328P's datasheet, the AVR library for the ATmega328P, and how we handle pin names. To use register programming, we shift our focus from the Arduino Uno to the ATmega328P, itself. Indeed, you can remove a programmed ATmega328P from your Arduino and use it on a circuit board of your own design. With an FTDI adaptor, you can program the ATmega328P while it is on your circuit board.
To use register programming, we will not be using the pin numbers printed on the Uno (like pins 2 and 3). We will use the pin nomenclature found on the ATmega328P microcontroller datasheet. I'm going to repeat a graphic from my last post. Its a partial schematic showing how the Uno's pins connect to the ATmega328P:
All of the ATmega328P pins that connect to the Uno I/O pins have multiple functions. For example, the ATmega328P pin 2 has this label inside the graphic for the chip: "PD0 PCINT16/RXD". That means this pin has three functions:
  1. PD0 - Standard I/O function, Port D, Pin 0
  2. PCINT16 - External Interrupt Number 16. One of 24 External Interrupts. ATmega documentation calls them Pin Change Interrupts
  3. RXD - UART Receive Pin. For Serial Input From External Devices

External Interrupt Register Programming - Register Definitions From the ATmega328P DataSheet

I cobbled together the following from the ATmega328P datasheet:
Pin Change Interrupt Control Register, PCICR: As I described in my last post, the ATmega328P has three ports, Port B, Port C, and Port D. PCICR lets you to enable or disable interrupts on any of the three ports. You can enable interrupts on more than one port. To enable interrupts on a specific port, write a "1" to the applicable bit in the PCICR register. Writing a "0" disables interrupts on the applicable port. Use the following when writing to PCICR:
  • PCIEO (bit 0) controls the interrupts on Port B, pins PCINT0 - PCINT7. PCINT6 and PCINT7 are not available as those pins connect to the crystal.
  • PCIE1 (bit 1) controls the interrupts on Port C, pins PCINT8 - PCINT14. PCINT14 is not available as it reserved for the RESET function.
  • PCIE2 (bit 2) controls the interrupts on Port D, pins PCINT15 - PCINT23.
Pin Change Mask Registers, PCMSK0, PCMSK1, PCMSK2: These three registers let you enable or disable individual pins in a port whose interrupts have been enabled with the PCICR register.

External Interrupt Register Programming - The ATmega328P Arduino Library

As mentioned in the last post, you do not have to consult the library. I include it to show how the datasheet, pin names, and the library come together to help you to write your sketches. The "#defines" in the library show what names you can use in your sketches. These names relate directly to the names in the datasheet register definitions and the ATmega328P pin names, as shown in the datasheet.

Examples Using Interrupts

From my setup in my last post, I'm going to use the red and green switches, and the Arduino Uno. I/m not using the LEDs. If a switch is pressed, a message to the serial monitor will appear.
In my first script, below, I will connect the green switch to PC4 (PCINT12, Uno Pin A4), and the red switch to PC5 (PCINT13, Uno pin A5). In this example, both switches connect to the same port. In setup you can see where I have setup my registers. Remember, from my last post I use the ATmega328p's internal pull-up resistors. They are enabled in line 28. Lines 32 and 33 enable interrupts for the two switches. That should be self-explanatory from the preceding discussion.
Here is where the magic happens: Note lines 13 to 20. This function is called the "Interrupt Service Routine" or ISR. Most of the time we will be in the while loop in setup. If either switch is pressed, processing diverts to the interrupt service routine. When this routine is done, processing goes back to where it was in the while loop.
It is important that you write line 13, the function definition, exactly as shown in my sketch (except for the comment, of course). This ISR will react to any enabled interrupt on port C. If you enabled interrupts on port B, or port D you would substitute "(PCINT0_vect)", or "(PCINT2_vect)", respectively, for "(PCINT1_vect)".
You want your interrupt service routine to be as short as possible. Do not use delay() in the ISR, delay(), itself, uses interrupts. If you use millis() in your code, millis() will not continue to increment while you are in the ISR, resulting in errors.
Any variable declared outside of the ISR, but changed inside the ISR should be declared with the modifier "volatile". This changes where the values of these variables are stored within the ATmega328P. Declaring them as volatile assures the values will be stored in RAM and not in a register - more important is the fact the values will not be lost. In our case that refers to the variasbles "data" and "foundInterrupt".
What does the ISR do? First, it reads PINC (the port C input register), masks out all bits but those related to PC4, and PC5, and stores the value in the variable "data". Second, it disables the port C interrupts - this is a way to debounce the switch (further key presses will not trigger an interrupt). Lastly, it makes the Boolean variable "foundInterrupt" true. When it has done all of these things, it exits back to the while() loop.
Back in the while()n loop, The first "IF" statement will now be true. We now look at the variable "data" to see which switch was pressed (The bit will be low for the switch that was pressed). Variable "foundInterrupt" is set to false and the masked value of PINC is check. If neither switch is pressed, the interrupt for port C is enabled again. This last is another way to effect switch debounding. This assures that only one message will be displayed for each switch press. In this scenario, it is not possible to detect if both switches are pressed at the same time.
The sketch below illustrates connecting the two switches to different ports. The green switch connects to PB0 (PCINT0, Uno pin 8). The red switch connects to PC5 (PCINT13, Uno pin A5). As with the last sketch, look at setup() to see how the ports, pullup resistors, and interrupts are setup.
Since we would like to detect interrupts on both the B and C ports, we need an interrupt service routine for both ports. The two ISRs are very simple. When a switch is pressed, variable "foundInterrupt_PortB", or "foundInterrupt_PortC", is made true. Also, the interrupt for that port is disabled. This is for switch debounce to avoid multiple interrupts (without this provision, an interrupt would occur whenever the switch is pressed and whenever the switch was released).
I think the code in the while() loop is pretty clear. Note that this sketch allows the possibility to detect both switches pressed. Since the while() loop has a 2 second delay, if both switches are pressed within the same two second period, the serial monitor will show that both are pressed. To avoid that situation, you should disable both interrupts in the two ISRs. The last part of the while() loop enables the two interrupts if both switches are released.

Thursday, January 7, 2016

Programming the ATmega328P Registers From the Arduino IDE

Please Note:

Because I have no experience with other Arduino boards, I'm going to confine my comments to the Arduino Uno and the Atmel ATmega328P microprocessor.

Introduction

If you desire to do something beyond the Arduino programming language, like program an interrupt for any Uno I/O pin, do more precise control of pulse width modulation, or write to the EEPROM within the ATmega328P, you need to learn about the ATmega328P's internal registers. If you are studying someone else's code and see something like:
     PORTD |= (1 << PORTD1);
and you wondered what that meant, read on. By utilizing what I call "Register Programming", besides the examples above, you can more efficiently program the three I/O ports, use the ATmega328P's internal timers, program it's SPI, I2C, and serial interfaces, and make better use of the Analog to Digital converter pins, and the analog comparator.
I would not attempt to control a servo with the AnalogWrite() function. You need more resolution than 1 part in 256. Also, the frequency is wrong. AnalogWrite() gives you a frequency of 490Hz or 980Hz depending on the pin. The very common servo I am familiar with requires a frequency of 50Hz. Register programming gives you the opportunity of using an ATmega328P counter that gives you one part 65536 resolution rather than 256. You can also set the frequency exactly to your needs.
What are these registers? They are eight bit bytes of SRAM memory within the ATmega328P. These registers can be accessed through programming via the Arduino IDE. If you write "digitalWrite(13, HIGH);" in your code, you are writing to one of these resisters. The ATmega328P has 224 of these registers, but most are reserved for future use. I count 86 that are useful. All of these have particular purposes.

Resources

To do Register Programming you need three basic resources:
  1. Comparison between Arduino I/O pins and ATmega328P pins
  2. ATmega328P datasheet
  3. AVR libraries on your computer

Arduino Pins and the ATmega328P

The following partial schematic represents the ATmega328P and the Arduino Uno's pins as printed on the device and as used in the language reference:
The graphic of ATmega328P looks really messy. That is because, all the I/O pins, except power and grounds, have multiple functions. For the moment, we will concentrate on the ATmega328P's pin nomenclature closest to the border of the graphic, namely PB0 to PB7, PC0 to PC6, and PD0 to PD7. All but three of these pins connect to Arduino Uno I/O pins. PC6 does not because it is used by the Uno as a reset to the ATmega328P. PB6 and PB7 connect to the 16MHz crystal so can not be used as I/O pins. The other pins make up three ports, B, C, and D. Each port is controlled or monitored by three ATmega328P registers.

ATmega328P Datasheet

The next resource is the Atmel datasheet for the ATmega family of microcontrollers. Here is a link to that document.
Every function and every register is clearly presented and explained in the datasheet. You can learn a lot and do a lot by reading this document. Besides the control of the port registers, I have used the timer registers to precisely control motors and servos (better than using the Arduino's analogWrite() function). I have also used the registers for writing to and reading from the ATmega328P's internal EEPROM, and I have used them to utilize external interrupts.

ATmega328P Port Registers

Let's create an example using one of the three port registers. We will use all eight pins of port D. Four LEDs will connect to PD0 to PD3. These correspond to pins labeled 0 to 3 on the Uno. We will connect four switches to PD4 to PD7. These correspond to pins labeled 4 to 7 on the Uno. Here is the partial schematic:
The following is copied directly from the datasheet for the ATmega328P. It details the three registers devoted to Port D:

AVR Libraries On Your Computer

Here is the final resource. YOU DO NOT NEED TO CONSULT THESE LIBRARIES TO PROGRAM YOUR DEVICES. I include this discussion to show why the Arduino IDE compiler allows you to use the Atmel register and register bit names in your code.
Atmel has supplied libraries for a large number of their microcontrollers to the Arduino development suite. If you have a PC, more than likely, you will find these libraries here:
     C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr.
There are 285 library files in this directory. No matter what version of the Arduino you have, you only need to know of one these libraries: io.h. You insert "#include <avr\io.h>" at the top of your code. The Arduino IDE, when compiling, will look at this file and knowing the specific Arduino you are using, will go to the appropriate library file. For the Arduino Uno, that library is iom328p.h. Wish I could print out the entire file here, but it is quite long.
iom328p.h consists of many, many #define statements. There is a #define for each of the ATmega328P's registers, and #define statements for each bit of each register. I never counted them, but I would think all 86 registers are addressed. All the nomenclature in the library is taken directly from the data sheet. PORTD in the data sheet is PORTD in the library. Consequently, you can write "PORTD" in your code.
Here is the portion of the library that deals with the three PORTD registers. Note the text corresponds exactly to the text in the data sheet:

Putting It All Together and Writing Code

While the text in the library conforms to the text in the data sheet register descriptions, the text on the schematic (also taken from the data sheet) does not. For example, the schematic pin labeled PD7 corresponds to PORTD7 in the PORTD register, PIND7 in the PIND register, and DDD7 in the DDRD register.
Now that we know that we can use the register names, like DDRD, and the register individual bit names, like DDD2, how do we write code? There are two basic ways I do it. If I want to specify all eight bits in a register (initializing a register), I will write something like this:
  PORTD = 0; //writes 0 to all eight bits.
  PORTD = 0b11110000; //top 4 bits high, lower 4 low.
If, however, I wish to simply change one, or more bits, without effecting the other bits in a register, I will do something like this:
  PORTD |= (1 << PORTD2);
This will make the logic level of I/O pin PD2 high, without effecting any of the other seven bits of PORT D. Rather than explain how this works here, I'll let you read further down this post where it will be explained.
Finally, it is time to discuss the functions of the three registers and how to used them in your code. We will make use of those four switches and four LEDs of our example circuit in our discussion.

The Data Direction Registers - DDRB, DDRC, and DDRD

The ATmega328P needs to know how you are using its pins. Are they inputs or outputs? That is the purpose of the data direction registers. You write a 1 to make a pin an output and a 0 to make it an input. In our example, PD0 to PD3 will be outputs, while PD4 to PD7 will be inputs. PD0 corresponds to DDD0 in the DDRD register. The other pins follow the same pattern as PD0. We now have enough information to write a line of code to establish which pins are inputs or outputs:
      DDRD = 0b00001111;
I'm using the binary representation (0b00001111 rather than 15 in decimal) because I can easily see each of the eight bits of DDRD. This is equivalent to using the pinMode() function in the Arduino language. However, using pinMode() you need to write eight lines of code:
      pinMode(0, OUTPUT);
      pinMode(1, OUTPUT);
      pinMode(2, OUTPUT);
      pinMode(3, OUTPUT);
      pinMode(4, INPUT_PULLUP);
      pinMode(5, INPUT_PULLUP);
      pinMode(6, INPUT_PULLUP);
      pinMode(7, INPUT_PULLUP);

Internal Pull-Up Resistors

What's with the PULLUP notation? As shown in the schematic, without further effort, the switches have no useful function. Whether a switch is pressed, or not, a logic 0 will be seen at the switch's pin. There are two ways to make the switches useful. The first is to connect a resistor between each of a switch's I/O pin and 5V. With this "pull-up" resistor connected, a logic 1 is recorded at the pin if the switch is not pressed. Since the switch connects the I/O pin to ground, a logic 0 will be recorded if the switch is pressed.
The second way utilizes a convenient feature of the ATmega328P. Each I/O pin has an internal pull-up resistor that can be connected at the pin. These ATmega328p pull-up resistors are connected by programming. We have seen how that is done using pinMode(). We can do the same, and do it for all four switches, in one statement using register programming. Per our example, we will use the PORTD register.
If you look in the data sheet at the description of the PORT registers, you will find if a pin is declared an input, in the port's DDR register, programming a 1 in the pin's PORT register will turn on the internal pull-up resistor. To make it clear, in our example, PD4 - PD7 have been declared inputs. Therefore, we will program PORTD4, PORTD5, PORTD6, and PROTD7 as 1s. We will do this in one line of code:
      PORTD = 0b11110000;

Port Registers - PORTB, PORTC, and PORTD

We have already seen an example of using the port register for enabling the internal pull-up resistors of input pins. The other obvious use of these registers is to turn on and turn off external components like LEDs. In our example, we have four LEDs. In the line of code above, where we connected internal pull-up resistors, we have also turned the four LEDs off. A logic 0 provides, essentially, ground voltage to the device. A logic 1 provides, essentially, +5V to the device. In our case, 0 volts turns the LEDs off. To turn an LED on, program a 1 to the pin's PORTD register. To turn LEDs D1 and D3 on, and to keep LEDs D2 and D4 off, we can write the following line of code:
      PORTD = 0b11110101;
Note, by writing the line above, we have to keep track of the logic levels of all eight pins in port D. Sometimes, that is not convenient, and not possible. Once we setup those pull-up resistors, we don't want to inadvertently disconnect them by programming. What we need then is a way to write to as few as one pin in the PORTD register without disturbing the logic levels of the other pins. To do that we need to be familiarize ourselves with the logic functions given to us by mathematician George Boole: the bitwise operators.

The Bitwise Operators

What does the following statement do?
     PORTD |= (1 << PORTD1);
This statement will turn on LED D2 without effecting the other seven bits of port D. It utilizes two of the six bitwise operators. Let's list the six operators and the characters that represent them in code:
~ NOT
& AND
| OR
^ EXCLUSIVE OR
<< SHIFT LEFT
>> SHIFT RIGHT
These operators are called bitwise because they operate on individual bits of a byte.

The NOT Operator ~

This is the simplest operator. It will flip all eight bits of a byte. The 1s become 0s and the 0s, 1s. For example, if PORTD = 0b00001111, ~PORTD = 0b11110000.

The AND Operator &

This takes two bytes and results in a third byte. The best way to visualize the operation is to place one byte over the other and then state the rule. For example:.
      00001111  the first byte
      01000010  AND with the second byte
      00000010  the resulting byte
We compare the two bits, in the same bit position, of each byte, and come up with the value of the bit, in the same bit position, in the resulting byte. The rule for the AND operation: if BOTH bits are a 1, the resulting bit is a 1. If either bit is, or both bits are, a 0, the resulting bit is a 0.

The OR Operator |

This also takes two bytes and results in a third byte. For example:.
      00001111  the first byte
      01000010  OR with the second byte
      01001111  the resulting byte
The rule for the OR operation: if EITHER bit is a 1, the resulting bit is a 1. If BOTH bits are a 0, the resulting bit is a 0.

The EXCLUSIVE OR Operator ^

This takes two bytes and results in a third byte. For example:.
      00001111  the first byte
      01000010  EXCLUSIVE OR with the second byte
      01001101  the resulting byte
The rule for the EXCLUSIVE OR operation: if BOTH bits are the SAME, the resulting bit is a 0. If the bits are DIFFERENT, the result is a 1. This also has the useful function of flipping, or not flipping a bit. A logic 1 in a bit position of one byte will flip the corresponding bit in the second byte. A logic 0 in a bit position of one byte will leave the corresponding bit in the second byte unchanged.

The SHIFT LEFT Operator <<

This operator takes a byte and an argument. The argument of the operator indicates how may times we will shift all the bits in the byte to the left. After each shift, a 0 is loaded into the least significant bit position (the bit on the right end). If we have the expression "PORTD << 3;", and PORTD is 0b00001111, the resulting byte is 0b01111000.

The SHIFT RIGHT Operator >>

This operator also takes a byte and an argument. The argument of the operator indicates how may times we will shift all the bits in the byte to the right. After each shift, a 0 is loaded into the most significant bit position (the bit on the left end). If we have the expression "PORTD >> 3;", and PORTD is 0b00001111, the resulting byte is 0b00000001.

Now That We Have The Fundamentals, Let's Light Some LEDs

How would we turn on LED D3 and not effect the other LEDs or the switches? Let's write the line of code and then take it apart:
     PORTD |= (1 << PORTD2);
First, we know that is a compound operation like "A += 1;" is the same as "A = A + 1;" We can expand our expression to:
     PORTD = PORTD | (1 << PORTD2);
Looking at the library we see "#define PORTD2  2". We can replace PORTD2 with 2, as below:
     PORTD = PORTD | (1 << 2);
(1 << 2) indicates a shift left with the argument of 2. The operand is the integer 1 which we can state in binary form as 0b0000000000000001. The operation wants us to left shift this twice putting a 0 in the least significant bit position each time. The result of (1 << 2) is 0b0000000000000100. We can drop the upper eight bits as they will have no impact when next operating on the eight bit PORTD. The result now is 0b00000100.
This now resolves to:
     PORTD = PORTD | 0b00000100;
Let's assume PORTD is 0b11110000. Here is what the operation looks like:
      11110000  the original value of PORTD
      00000100  OR with the second byte
      11110100  the resulting value of PORTD. --- LED D3 TURNS ON.
We turned LED D3 on, now let's turn it off (without effecting the other LEDs or switches. Here is the line of code:
      PORTD &= ~(1 << PORTD2);
Some of the line of code is the same as before. (1 << PORT2) resolves to 0b00000100. However notice the NOT operator, ~. This flips all eight bits resulting in 0b11111011. Now we apply the AND operation as shown below:
      11110100  the original value of PORTD
      11111011  AND with the second byte
      11110000  the resulting value of PORTD. --- LED D3 TURNS OFF.

Writing To Multiple Outputs

What if you wanted to turn on (or off) two or more LEDs? You can string the operations together in one line. Let's turn on LED D1 and LED D4, then turn them off:
      PORTD |= (1 << PORTD0) | (1 << PORTD3);  ON
      PORTD &= ~(1 << PORTD0) & ~(1 << PORTD3);  OFF
You can even mix them. This will turn LED D1 and LED D3 on and LED D2 and LED D4 off:
      PORTD |= (1 << PORTD0) & ~(1 << PORTD1) | (1 << PORTD2) & ~(1 << PORTD3);
For more fun, stick that line above in setup(), and add the following lines in loop():
      PORTD ^= (1 << PORTD0) ^ (1 << PORTD1) ^ (1 << PORTD2) ^ (1 << PORTD3);
      delay(500);
This will toggle the four LEDs at a 1Hz cycle. Try it.

Reading the Logic Level of Input Pins

We Can't Forget Those Switches. Continuing the example, the switches are connected to input I/O pins. To read a pin that has been declared an input we use one of the PIN registers. In our case, that will be PIND. Since we have four switches we need a way to read one switch and ignore the others. To see how this is done, the line of code to evaluate if switch SW1 (connected to PD4) is pressed is:
      PIND &= (1 << PIND4);
The above expression will be logic 0 if SW1 is pressed and not 0, actually 16, if released. The other switches will not effect the result.
I, frankly, don't always use that last method for reading from a register. I usually establish, and apply an eight bit datamask to the value in the PIN register. Like this to read the value of PIND4:
      byte datamask = 0b00010000;
      byte data;
      data = PIND & datamask;
Here is a simple sketch. If SW1 is pressed, LED D1 will light, etc. This is not the best way to do this as it allows multiple LEDs to be lit and there is no switch debouching. The better way is to use interrupts to signal when a switch is pressed. My next blog entry will deal with that exact topic. Interrupts also are best handled with register programming.

One More Thing

Here is a photo of the actual hardware:
I had to Photoshop in the colors of the LEDs. They are of clear plastic and do not photograph well when lit.
Since we have introduced color, why not put that into the program. Here is the sketch carried to the extreme by adding a bunch of #define statements: