210 likes | 337 Views
CS-212 Dick Steflik. Butterfly I/O Ports. I/O for our labs. To get data into and out of our Butterfly its a little trickier than using printf and scanf as you did in CS211 Since the Butterfly doesn't have an Operating System we need to write and read data directly to/from the I/O ports
E N D
CS-212 Dick Steflik Butterfly I/O Ports
I/O for our labs • To get data into and out of our Butterfly its a little trickier than using printf and scanf as you did in CS211 • Since the Butterfly doesn't have an Operating System we need to write and read data directly to/from the I/O ports • Access to the ports is through the Special Function Registers (SFRs) that are defined symbolically in iom169.h which is included in your program by io.h
io.h # include <avr/iom165p.h> #elif defined (__AVR_ATmega168__) # include <avr/iom168.h> #elif defined (__AVR_ATmega168P__) # include <avr/iom168p.h> #elif defined (__AVR_ATmega169__) # include <avr/iom169.h> #elif defined (__AVR_ATmega169P__) # include <avr/iom169p.h> #elif defined (__AVR_ATmega8HVA__) # include <avr/iom8hva.h> #elif defined (__AVR_ATmega16HVA__)
iom169.h /* iom169.h - definitions for ATmega169 */ /* This should be up to date with data sheet version 2514J-AVR-12/03. */ #ifndef _AVR_IOM169_H_ #define _AVR_IOM169_H_ 1 /* This file should only be included from <avr/io.h>, never directly. */ #ifndef _AVR_IO_H_ # error "Include <avr/io.h> instead of this file." #endif #ifndef _AVR_IOXXX_H_ # define _AVR_IOXXX_H_ "iom169.h" #else # error "Attempt to include more than one <avr/ioXXX.h> file." #endif /* I/O registers */ /* Port A */ #define PINA _SFR_IO8(0x00) #define DDRA _SFR_IO8(0x01) #define PORTA _SFR_IO8(0x02) /* Port B */ #define PINB _SFR_IO8(0x03) #define DDRB _SFR_IO8(0x04)
sfr_defs.h • included in every compile via io.h • contains macro definitions for accessing Special Function Registers as if they were just c language variables • in iom169.h the statement: #define PINA _SFR_IO8(0x00) the symbol PINA is mapped to the SFR at address 0x00 in SFR memory • _SFR_IO8( ) is a macro (look in sfr_defs.h )
Why this is done • in your program you #include io.h and io.h in turn includes iom169.h (because in your project definition you picked the ATmega169 as the processor) • pass 1 of the compiler does all includes and macro substitutions ( in our example with PINA all occurrences of the symbol PINA in your program will be replaced with a reference to SFR 0x00) • This makes all SFR references look like references to C variables
Getting Data Into and Out Of • Initialize the ports • set the Data Direction Registers (DDRs) • use PORTB for input • use the 8 input DIP switch for inputting data • don't forget the pull-up resistors • use PORTD for showing output • use 8 LEDS • don't forget the current limiting, series resistors
Design • This warrants 3 subroutines • one to do initialization • one to do input • one to do output • These should be written very general purpose as we will use them for many of the labs
daemons • a daemon is a program that will sit in memory forever and run until the system loses power • since most embedded system run forever (until they lose power) our main() should be written as a never ending loop. • processing to be done can be done either in the body of the loop or asynchronously in response to an external or internal event (interrupt)
Interrupts • How Interrupts are handled • Somewhere in your program you define an Interrupt Servicing Subroutine, the compiler will put the address of this routine into the proper location in the interrupt vector (low memory) • When the interrupt occurs the system state is saved, a branch to the ISS is made via the interrupt vector • The ISS executes and returns the state of the system and you are right where you were before the interrupt occurred
What we want to do • use 8 LEDs for output • 8 switches for input • 1 button to tell when it is OK to read the switches • to do this we should us an interrupt
The way it should work /* Interrupt handler */ { // disable interruptes // read the switches // save the data at the next location in an array // light the lights // enable interrupts } int main () { // initialize ports B & D // initial Port E for interrupt on PE2 // loop forever – this is our daemon }
volatile keyword • the keyword “volatile” should be used to define any variable that may be modified inside of an interrupt handler • “volatile” flags a variable to the optimizing compiler to not optimize variables with the “volatile” keyword volatile char switches = 0; volatile uint8_t flags = 0;
char & uint8_t • Remember the char data type can be used for ASCII characters or a signed 8 bit integer • The AVR header files define another data type that allows an unsigned 8 bit integer, this is convenient for working with gpio ports as they are not usually signed data.
internal pullups for gpio inputs • on many MCUs you must add an external pullup resistor on gpio input lines • Atmel MCUs have built-in internal pullup resistors • to use the internal pullups write a one to that line after it has been defined as an input line in the DDR // define Port B as all outputs and Port D as all inputs DDRB = 0xFF; DDRD = 0x00; //turn on the internal pullup resistors for Port D PORTD = 0xFF;
Setting up an interrupt handler • Signal or ISR • the ISR macro has superceded the Signal macro and is the preferred way of setting up an interrupt handler ISR( name of the int from processor .h file) { ........ } ISR(SIG_PIN_CHANGE1) { .... }
Pin Change Interrupts • Input lines on Ports B and E can be set up to generate a pin change interrupt • Each port has an associated mask register • to enable an interrupt put a 1 into the correct position in the appropriate mask register • Port B PCMSK1 • Port E PCMSK0
PCINT Mapping PORT E PCINT7 7 SIG_PIN_CHANGE0 PCINT6 6 PCINT5 5 PCINT4 4 PCINT3 3 PCINT2 2 PCINT1 1 PCINT0 0 PORT B PCINT15 7 SIG_PIN_CHANGE1 PCINT14 6 PCINT13 5 PCINT12 4 PCINT11 3 PCINT10 2 PCINT9 1 PCINT8 0
Example //set bit 2 of Port B to cause an interrupt PCMSK1 = 0x04; // set bit 2 of Port B to cause interrupt EIMSK = 0xC0; // Ext Int Mask Reg EIFR = 0xC0; // Ext Int Flag Reg DDRB & = 0xFB; // make bit 2 an input line PORTB | = 0x04; // turn on pullup ISR(SIG_PIN_CHANGE1) { cli( ); // disable interrupts . . . sei( ); // enable interrupts }