200 likes | 379 Views
Lecture : Using Interrupts with Embedded C using PIC microcontrollers Teaching resources on on www.eej.ulst.ac.uk My office 5B18, telephone 028 90 366364 My email IJ.McCrum@ulster.ac.uk. EEE305 Microcontroller Systems. Normal Code.
E N D
Lecture : Using Interrupts with Embedded C using PIC microcontrollers Teaching resources on onwww.eej.ulst.ac.uk My office 5B18, telephone 028 90 366364 My email IJ.McCrum@ulster.ac.uk EEE305 Microcontroller Systems http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Normal Code • On startup the processor goes to address zero and starts executing the code it finds there. Usually a “goto” instruction is placed there, to an address in program memory. • For example the simple C program • #include <stdio.h> • #include <xc.h> • #define _XTAL_FREQ 4000000 • #define LED0 RB0 • void main(){do{RB0=0;RB0=1;}while(1);} • Generates code; http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Compiled Code 104 07F7 _main: 105 07F7 l5: 109 07F7 1283bcf 3,5 ;RP0=0, select bank0 110 07F8 1303 bcf 3,6 ;RP1=0, select bank0 111 07F9 1006 bcf 6,0 ;volatile 112 07FA 1406 bsf 6,0 ;volatile 113 07FB 2FF7goto l5 114 07FC __end_of_main: The intel HEX file below is produced by MPLAB – the programmer uses it. I have inserted extra spaces to show the fields of each line. Note 16 bits given as LSB:MSB This is known as little Endian (LSB stored first in memory). Also note that the HEX files has byte addresses twice that of the word addresses in the PIC :06 0000 00 0A12 8A11 FC2F 18 :10 0FEE 00 8312 0313 0610 0614 F72F 8301 0A12 8A11 B7 :02 0FFE 00 F72F CB 0FFE / 2 = 07F7 :00 0000 01 FF this is the end of file record. One Intel Hex format is shown below, a type of 00 is actual data, (see wikipedia for detail) <:> <byte count(2)> <address(4)> <type(2)> <list of bytes> <checksum> http://www.eej.ulst.ac.uk/~ian/modules/EEE305
The complete truth As well as the code on the previous slide the compiler will insert a prologue and epilogue to your code, and place a jump instruction at address zero. The prologue is called the “startup code” and the epilogue is (usually) never reached. Some compilers give you the source code the startup routine sometimes called crt.as so you can modify it. The simple C code here needs no startup. :06 0000 00 0A12 8A11 FC2F 18 :10 0FEE 00 8312 0313 0610 0614 F72F 8301 0A12 8A11 B7 :02 0FFE 00 F72F CB 07FC __end_of__initialization: 145 07FC 0183 clrf 3 146 07FD 120A 118A 2FF7 ljmp _main ;jump to C main() function At address zero we have the code below, SFR 0xA is the high bits of the PC :06 0000 00 0A12 8A11 FC2F 18 This represents PIC instructions of 120A, 118A 2FFC Table 15.2 allows us to convert these (bold text shows the field splits) 0000 : 120A ; bcf 0xA,4 ; 01 0010 0000 1010 0001 : 118A ; bcf 0xA,3 ; 01 0001 1000 1010 0002 : 2FFC ; goto 7FC ; 10 1111 1111 1100 http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Interrupts • When a machine powers up or is reset, it begins executing main code at address zero. • Later on when certain other hardware events occur it begins executing at address 0x04 if certain bits are set in various registers. • For this to work the PIC is designed so that it can stop what it is doing in main code, temporarily save any registers it is currently using, store the current value of the program counter and then do a “JUMP to address 0x04” • After the code at address 0x04 is executed the reverse applies – the registers are restored and a “JUMP back to the saved address” undertaken. • This allows the machine to appear to do two things at once! • We call the “event” that causes the jump an INTERRUPT and we call the code executed an INTERRUPT SERVICE ROUTINE or ISR. The saving of registers is known as a CONTEXT SAVE and the exit from the ISR causes a CONTEXT RESTORE. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Sources of INTERRUPTs (DS 12.10) The PIC16F877 has 14 possible sources of interrupt. The INTCON register is full of flag bits and one of these gets set if an interrupt event occurs. There are other relevant registers as well – PIR1, PIR2 and PIE1 and PIE2 As well as the FLAG bits there are also ENABLE bits. Individual and global. For an interrupt to occur a global Interrupt enable must be set as well as an individual interrupt enable. The GIE bit is bit 7 of INTCON If both of these are not set you can still go and look at the FLAG bit in your main code but no actual INTERRUPT (jump to ISR) will occur. You have done this already when you checked RCIF or TXIF to use the serial port. When a “RETURN FROM INTERRUPT” is done the GIE bit gets set again so that interrupts get re-enabled. (On entry to an ISR the GIE is initially reset to avoid interrupting the interrupter! It is also reset on powerup. ) The ISR MUST clear the source of interrupt before exiting the ISR or it just gets interrupted again. Usually this means writing code to reset the appropriate IF flag. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Sources of Interrupt (DS Fig 12-9) The RCIF and TXIF are from the serial port, the ADIF is from the ADC. We shall only consider timer zero (T0IF) and signals affecting PORTB – there are two ways to generate an interrupt from PORTB. The peripherals on the left can be disabled using PEIE. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
PORTB interrupts PORTB INT INTERRUPT (bit 0 only) An external interrupt on the RB0/INT pin is edge triggered, either rising, if bit INTEDG (OPTION_REG<6>) is set, or falling, if the INTEDG bit is clear. When a valid edge appears on the RB0/INT pin, flag bit INTF (INTCON<1>) is set. This interrupt can be disabled by clearing enable bit INTE (INTCON<4>). Flag bit INTF must be cleared in software in the ISR before re-enabling this interrupt. PORTB INTERRUPT ON CHANGE (bits 7 to 4 only) If set to be inputs, the input pins (of RB7:RB4) are compared with the old value latched on the last read of PORTB. The “mismatch” of RB7:RB4 are OR’ed together generates the RB Port Change Interrupt with flag bit RBIF (INTCON<0>). The interrupt can be enabled/disabled by setting/clearing enable bit RBIE (INTCON<4>) http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Using Interrupt on Change • This interrupt can wake the device from SLEEP. • The user, in the Interrupt Service Routine, can clear the interrupt in the following manner: • a) Any read or write of PORTB. This will end the mismatch condition. • b) Clear flag bit RBIF. • A mismatch condition will continue to set flag bit RBIF. Reading PORTB will end the mismatch condition and allow flag bit RBIF to be cleared. • The interrupt-on-change feature is recommended for wake-up on key depression operation and operations where PORTB is only used for the interrupt-on-change • feature. Polling of PORTB is not recommended while using the interrupt-on-change feature. • This interrupt-on-mismatch feature, together with software configureablepull-ups on these four pins, allow easy interface to a keypad and make it possible for wake-up on key depression. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
TMR0 There are 3 ways of using a timer. Clear it, let it count and keep checking to see if it has reached a target value Load it with 0xFF-n and keep checking T0IF to see if it has set, polling from main code Set up an interrupt and write an ISR to handle the TMR0 interrupt (when T0IF sets) TMR0 INTERRUPT An overflow (FFh-> 00h) in the TMR0 register will set flag bit T0IF (INTCON<2>). The interrupt can be enabled/disabled by setting/clearing enable bit T0IE (INTCON<5>) You need the GIE set and you have to write a ‘0’ into T0IF to clear it before returning http://www.eej.ulst.ac.uk/~ian/modules/EEE305
The INTERRUPT SFR (Special File Registers) INTCON is repeated through all banks, the OPTION register below is in banks 1 and 3 Whilst we use the PIR and PIE SFRs for most peripherals, PORTB and TMR0 are in the INTCON OPTION http://www.eej.ulst.ac.uk/~ian/modules/EEE305
INTERRUPTs in C – see -> 5.9 INTERRUPTS The MPLAB XC8 compiler incorporates features allowing interrupts to be fully handled from C code. Interrupt functions are often called Interrupt service Routines, or ISRs. 5.9.1 Writing an Interrupt Service Routine The function qualifier interrupt may be applied to a C function definition so that it will be executed once the interrupt occurs. The compiler will process the interrupt function differently to any other functions, generating code to save and restore any registers used and return using a special instruction. An interrupt function must be declared as type void interrupt and may not have parameters. This is the only function prototype that makes sense for an interrupt function since they are never directly called in the source code. You should NOT call functions that might have been executing when the interrupt occurred – this is called RE-ENTRANCY and XC8 does not allow this, you code might work most of the time so this would be a really dodgy way to code. VERY hard to debug/test so be careful. Create two versions of every function needed in both domains … e.g func1() and func1_ISR() http://www.eej.ulst.ac.uk/~ian/modules/EEE305
An example of an interrupt function • inttick_count; • void interrupt tc_int(void){ • if (TMR0IE && TMR0IF) { • TMR0IF=0; • tick_count++; • return; • } • // process other interrupt sources here, if required • } // back in main you can poll tick_count. If it is 16 bits you might need to disable • //interrupts before and after reading it. di() and ei() are used for this. • Code generated by the compiler will be placed at the interrupt vector address which will • execute this function after any context switch that is required. 5.9.4 Enabling Interrupts Two macros are available, once you have included <xc.h>, which control the masking of all available interrupts. These macros are ei(), which enable or unmask all interrupts, and di(), which disable or mask all interrupts. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Using Timer0 to multiplex 7 seg displays If an interrupt happens 100 times a second then you switch between each of the 4 displays every interrupt and the human eye will think all 4 displays are active. Assume main code has a global variable called digit_now_on and the 4 digit number to be displayed stored in an unsigned char array called digits. Main clears digit_now_on on powerup and enables the timer INTERRUPT. Any time main code wants to display a number it splits it into 4 separate digits. DISABLES Interrupts, plants the 4 numbers into the array and RE-ENABLES Interrupts. The ISR reads digit_now_on, reads the appropriate (single) digit and outputs it. It then increments digit_now_on, putting it back to zero if it is 4 so it will cycle 0-3. The ISR needs to output 0001, 0010, 0100 or 1000 if digit_now_onis 0-3. This switches on the appropriate transistor that enables each seven seg display. (1<<digit_now_on) works! With care the same patterns can be used to read a keypad… saves i/o lines! http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Keypad 1 0 0 0 0 0 0 1 0 For example the Rows are driven with a pattern so that one row is active and the other 3 are inactive. Normally it is handier to have the columns pulled up to 5 volts through a resistor, since PORTB for example has internal pullups (if enabled - see the RBPU bit 7 of the OPTION reg.) Then the ideal pattern to drive the ROWs is {1110,1101,1011,0111} repeating. Unfortunately we need {0001,0010,0100 and 1000} to drive the 7-segment drivers so we must add pull down resistors to the Column lines. Also it would be useful to use the interrupt on change facility on RB3-RB7 but it is inconvenient because the programmer uses RB6 and RB7. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
PICking the PIC PINs (sic) http://www.eej.ulst.ac.uk/~ian/modules/EEE305
PICking the PIC PINs (sic) PicKIT Programmer 8 (PORT B) 6 (PORT A) 3 (PORT E) 4 (PORT D) 4 (PORT C) 4 (PORT C) Serial To PC 2 (PORT D) 2 (PORT D) http://www.eej.ulst.ac.uk/~ian/modules/EEE305
PICking the PIC PINs (sic) PORTA – RA0 wired to Rth – the Thermistor – name it Rth PORTA – RA1,2,3,4 inputs from Keypad – name it COL1 to COL4 PORTB –RB0-RB3 Output scan pattern – name it ROW1 to ROW4 PORTB– RB6,RB7 used by PicKIT Programmer PORTC – RC0-RC3 wired to LCD data4 to data7 – name it LCD_d3 to LCD_d7 PORTC – RC4,RC5 wired to RS and EN on LCD, – name it LCD_RS and LCD_EN (plus ground R/WBAR ) PORTC – RC6 TxD serial data to PC PORTC – RC7 RxD serial data from PC PORTD – to seven segments (and decimal point) – name it a-g and dp PORTE – not used Leaves RA5, RB4 and RB5 and 3 bits of PORTE free. We can drive the Piezo buzzer to add audio feedback for keyclicks http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Code We have code for ADC, serial and LCD 7-seg code will use variables digit_now_on and data[4]. Keypad routine will get read a keystroke if COL1 to COL4 are not 0000. you can readROW1 to ROW4 and combine with COL1 to COL4 to form a byte. Simplest to just have a switch statement. The aim is to make a thermometer, outputting on 7 segment displays, LCD displays and PC screens. A keypad can set an alarm threshold and when it is reached your code should do something – beep, flash 7-segs, print messages or whatever. The key approach is to start with small piece of workign code and evolve it testing at each stage to make sure you have not broken anything. http://www.eej.ulst.ac.uk/~ian/modules/EEE305
Suggested development Week 10 • Get the LCD working • Get the serial port working • Get the thermistor reading (0-1023) • Get a linearised temperature 15 to 36 degrees • Get the 7 segment going – one digit in main • Get the 7 segment going - 4 digits/interrupt • Get the keypad going • Get the Alarm going. • Write up as you go along, but put it all together and hand in. • Hand in After Easter – Friday at end of revision week. Week 11 Week 12 http://www.eej.ulst.ac.uk/~ian/modules/EEE305