31_EMBEDDED_GR_ppapag_Interrupts_ISRs_MULTITASKING

Download Report

Transcript 31_EMBEDDED_GR_ppapag_Interrupts_ISRs_MULTITASKING

TI MSP430
Polling, Interrupts, ISRs
1
Polling, Interrupts
• Polling
• Εισαγωγή στα Interrupts
• Παραδείγματα
2
Polling vs Interrupt
3
Interrupt Service Routines in Assembly
•
Το υποπρόγραμμα εξυπηρέτησης
διακοπής είναι αρκετά απλό.
(Παρατηρήστε την εντολή “reti”.)
•
Αποθηκεύει την διεύθυνση της
συγκεκριμένης ISR (PORT1_ISR)
στην κατάλληλη θέση του πίνακα
διανυσμάτων (vector table) με την
οδηγία (directive) ORG.
•
Το PORT1_VECTOR ορίζεται στο
αρχείο επικεφαλίδας (header file)
αναφορικά με το INTVEC segment.
Η οδηγία COMMON πρέπει να
χρησιμοποιηθεί ώστε η καθορισμένη
διεύθυνση (PORT1_ISR) να
τοποθετηθεί στην σωστή θέση. Αυτό
είναι χρήσιμο ώστε να ορίζουμε
4
ISRs σε πολλαπλά αρχεία.
PORT1_ISR:
; respond to inputs on port1
reti
; return from interrupt
ORG
DW
0FFE8
PORT1_ISR
-- or -COMMON
ORG
DW
INTVEC
PORT1_VECTOR
PORT1_ISR
Interrupt Service Routines in C
// tell the compiler the vector for this ISR
#pragma vector = PORT1_VECTOR
__interrupt void port1_isr(void)
{
// respond to inputs on port1
}
•
Η οδηγία “#pragma vector”
πληροφορεί τον compiler για την
αποθήκευση της διεύθυνσης του
υποπρογράμματος στην θέση
PORT1_VECTOR.
•
Η οδηγία“__interrupt” πληροφορεί
τον compiler ότι αυτή η
συνάρτηση πρέπει να τελειώνει με
την εντολή “reti”.
Το όνομα της συνάρτησης
port1_isr δεν έχει σημασία…
•
5
Interrupt Vector Jump Table
• Ο πίνακας των διανυσμάτων πληροφορεί
το πρόγραμμα για το που θα συνεχίσει
όταν συμβεί ένα interrupt.
• Ένα διάνυσμα- vector δείχνει στην
διεύθυνση όπου ξεκινά το πρόγραμμα
εξυπηρέτησης της διακοπής (interrupt
service routine).
• Ο πίνακας διανυσμάτων διακοπών για τον
MSP430 είναι αποθηκευμένος στην μνήμη
RAM από 0xFFC0 έως 0xFFFF και μπορεί
6
να τροποποιηθεί.
MSP430 Interrupts
7
MSP430 Status Register
• GIE (bit 3): Global Interrupt Enable
– 1: Ενεργοποιεί όλες τις διακοπές (Οι διακοπές πρέπει
επιπλέον να ενεργοποιηθούν η καθεμία ξεχωριστά!!!)
– 0: Απενεργοποιεί όλες τις διακοπές (Disable all
interrupts )
Σημείωση: Ο καταχωρητής κατάστασης SR σώζεται στον σωρό
(stack) και καθαρίζεται (απενεργοποιούνται οι διακοπές!!!) όταν
εκτελείται μια ISR .
8
Setting and Clearing the General Interrupt
Mask
• Assembly
EINT – Enable interrupts (set GIE)
DINT – Disable interrupts (clear GIE)
• C
asm(“
asm(“
EINT”);
DINT”);
• C (#include intrinsics.h)
__enable_interrupt();
__disable_interrupt();
9
ECE447: MSP430 Interrupt execution flow
RAM JUMP TABLE
0xFFFF
EE
PC
SR
10EE
00F8
10
0000
0xFFC0
STACK
F8
00
SP
RAM SERVICE ROUTINE
0x10EE
10
MSP430 Interrupt execution flow
11
Interrupts Example: Toggling LEDs
// Listing 6.5: Toggles LEDs in ISR using interrupts from timer A CCR0
// in Up mode with a period of about 0.5s
#define LED1 BIT3
#define LED2 BIT4
void main (void)
{
WDTCTL = WDTPW | WDTHOLD;
// Stop watchdog timer
P2OUT = ~LED1;
// Preload LED1 on, LED2 off
P2DIR = LED1 | LED2;
// Set pins with LED1,2 to output
TACCR0 = 49999;
// Upper limit of count for TAR
TACCTL0 = CCIE;
// Enable interrupts on Compare 0
TACTL = MC_1 | ID_3 | TASSEL_2 | TACLR;
// Set up and start Timer A
// “Up to CCR0” mode, divide clock 8, clock SMCLK, clear timer
__enable_interrupt ( );
// Enable interrupts (intrinsic)
for (;;) {
// Loop forever doing nothing
}
// Interrupts do the work
}
// Interrupt Service Routine for Timer A channel 0
#pragma vector = TIMERA0_VECTOR
// Assoc. the funct. w/ an interrupt vector
__interrupt void TA0_ISR (void)
// name of the interrupt function (can be anything)
{
P2OUT ^= LED1 | LED2;
// Toggle LEDs
12
}
MSP430 Interrupts
13
14
Interrupt processing
15
MSP430 Interrupts
16
Interrupts
17
Προτεραιότητες interrupts
18
Interrupt vector addresses
19
Stack
20
Μηχανισμός Interrupt
21
Μηχανισμός Interrupt (…)
22
23
MSP430 Interrupt Exercise
• Write a program that collects samples from input
Port 2. A signal line that indicates a new data
value is available is connected to pin P1.0. Use
an interrupt to detect a rising edge on the P1.0.
When an interrupt is triggered the value on Port2
should be stored into a byte array of 32 entries.
When 32 samples have been collected, P1.7
should be driven as an output high to indicate
the memory is “full”
24
25
26
27
28
29
BITFIELDS
Time Based Processing on an MSP430 Launchpad
I recently saw a video on MyBitBox on using bitfields in time based
processes and thought it was and excellent example of a fairly advanced topic
in embedded systems. In that blog, an mBed is used, which is an ARM based
dev kit. I thought it would be useful to port this example to the MSP430
Launchpad. In this example, I’m using the MSP430G2231, but any MSP430
with Timer_A can be used.
This example is used to show that microcontrollers can be setup to run
several tasks at multiple intervals. The desired result is a system that
executes these commands deterministically, meaning that at the desired
time interval a routine WILL execute. this is useful when a microcontroller
needs to multitask or divide a workload.
For this example, we’re going to setup a routine to run every second and
another routine to run every 100ms. There are two LEDs on the
Launchpad, so for this example we’ll toggle one LED at 100ms and the
other at 1 second. The first thing to do is define a struct with a bitfield to set
flags whenever a time interval is reached.
30
http://busted240sx.wordpress.com/2011/11/27/time-based-processing-on-an-msp430-launchpad/
typedef struct time_flags{ unsigned
time_1ms
:1;
unsigned time_100ms
:1;
unsigned time_1s
:1;}time_flags;extern volatile struct
time_flags sys_time;
These flags will be set by an interrupt service routine that executes
every millisecond. This ISR increments several counters.
counter_base increments every millisecond until it reaches 100 and
is reset. counter_100ms increments every 100ms until it reaches 10
(1 second), then it is reset. whenever we reach these desired
increments we set the flags of the structs we created earlier. This
method can be extended as much as desired so you could have an
hour counter or day counter if you wanted.
31
#pragma
vector=TIMERA0_VECTOR__interrupt
void Timer_ISR(void){
sys_time.time_1ms = TRUE;
counter_base++;
if(counter_base>=100)
{
counter_base=0;
sys_time.time_100ms = TRUE;
counter_100ms++;
//this is for 1sec
if(counter_100ms>=10)
{
counter_100ms=0;
//counter_1s++;
sys_time.time_1s =
TRUE;
}
}
return;}
32
In a separate file we’ll setup our main functions and the routines we wish to
call.
After turning off the Watchdog timer, we setup Timer A which we’ll use to
trigger
the interrupt. We set Timer A to Up mode, which will generate an interrupt
when
the timer reaches the value of CCR0 and then reset the timer. By default, the
SMCLK is setup for 1MHz, so I didn’t have to divide the clock down or change
the source. If you want to use a different clock, go right ahead but be aware
that the value of CCR0 will have to be changed.
the while(1) starts an infinite loop that the microcontroller will
never break from. This is known as the “idle loop”. The ISR
will handle all the counting and setting of the flags and most of the time,
the MSP will just loop through this and check the flags status. If the flag is
set, a routine is called and the flag is cleared.
33
void main(void){
WDTCTL = WDTPW + WDTHOLD;
//stop watchdog
TACTL = TASSEL_2 +
MC_1 + TAIE;
//Timer A, SMCLK, up mode, enable
interrupts
P1DIR |= BIT0 + BIT6;
CCTL0 =
CCIE;
//CCR0 Interrupt enable
CCR0 =
1000-1;
//SMCLK/CCR0 = 1MHz/1000 = 1kHz
_BIS_SR(GIE);
//enable global interrupts
TRUE)
while(1)
//idle loop
{
if(sys_time.time_1ms == TRUE)
{
//main_1ms_routine , if needed
sys_time.time_1ms = FALSE;
}
if(sys_time.time_100ms == TRUE)
{
main_100ms_routine();
sys_time.time_100ms = FALSE;
}
if(sys_time.time_1s ==
{
main_1s_routine();
sys_time.time_1s = FALSE;
}
}
}
34
Finally, we define the routines we want to run. Here, we’re just blinking
LEDs, but it could be anything. On the Launchpad, the Green LED is on Port
1 Pin 6, so I set Bit 6 to toggle with an XOR every second. The same is
done with the red LED on Port 1 Pin 0 every 100ms.
void main_1s_routine (void){
//toggle Green LED
P1OUT ^= BIT6;}void
main_100ms_routine (void){
//toggle Red LED P1OUT ^=
BIT0;}
35
Thats it! We’ve setup a timer to trigger an interrupt every millisecond,
defined the flags and counters, incremented the counters and set the flags
in the ISR, and created routines to run based on the flags that are set. Now,
we Build the code and watch it run!
You can download the full source code here.
Thanks. Big thanks to myBitBox for enlightening me to this method.
36
37
MSP430 Launchpad - Low Power Thermometer Code
TI has a really low cost development platform called launchpad
that I have been playing with recently. It is $4.30 with free shipping
until august and it has some crazy low power capabilities. Below
is my first program beyond a blinky. It sleeps in the second to
lowest power mode then wakes up, reads the temperature, counts
it out on 2 LEDs on the dev board then goes back to sleep. Other
than driving the LEDs, this should use so little power that batteries
could conceivably die from age as fast as they are used if I cranked
the sleep all the way up. It also has the advantage of working on
a launchpad in factory condition with no additional parts.
.
38
http://bill-landers.blogspot.com/2010/08/msp430-launchpad-low-power-thermometer.html
// the goal was to sleep as long as possible, read temp
// then display it through flashing the launchpad leds
//
// when not flashing or measuring go to the lowest power reasonable
//
#include "msp430x20x2.h"
//unsigned short cc = 65470;
//unsigned short cc = 1000;
unsigned short cc = 10000;
//longest CCR0
//debugging CCR0 value
//interval between tests - CCR0 value
long IntDegF;
//this is all setup
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// Stop WDT
BCSCTL3 |= LFXT1S_2;
// make clock work without crystal
P1DIR |= 65;
// P1.0 & 6 output
P1OUT = 65;
// lights on so we know we are started
CCTL0 = CCIE;
// CCR0 interrupt enabled
CCR0 = cc;
// set the long interval
TACTL = TASSEL_1 + MC_1 + ID_3 ;
// SMCLK, upmode, / 8
_BIS_SR(LPM3_bits + GIE);
}
// Enter LPM3 w/ interrupt
39
//this captures the temp value
void dotemp(void)
{
WDTCTL = WDTPW + WDTHOLD;
// Stop WDT
ADC10CTL1 = INCH_10 + ADC10DIV_3;
// Temp Sensor ADC10CLK/4
ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;
ADC10CTL0 |= ENC + ADC10SC;
// Sampling and conversion start
__bis_SR_register(CPUOFF + GIE);
// LPM0 with interrupts enabled
// oF = ((A10/1024)*1500mV)-923mV)*1/1.97mV = A10*761/1024 - 468
long temp = ADC10MEM;
// raw adc temp
IntDegF = ((temp - 630) * 761) / 1024;
CCR0 = 999;
// set short interval for display
}
40
// ADC10 interrupt service routine #pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
__bic_SR_register_on_exit(CPUOFF);
}
// Clear CPUOFF bit from 0(SR)
41
/ Timer A0 interrupt service routine - this is the main flow control
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
if (CCR0<1000)
//if we are on the short interval we are blinking
if (P1OUT > 0)
//if the leds are on turn them off
{
P1OUT = 0;
CCR0 = 600;
}
else
//time to count down
if (IntDegF == 0) //if we have counted to zero back to the long count
CCR0 = cc;
else
{
CCR0 = 100;
if (IntDegF > 9) //if there are more than 10 left use the first led
{
P1OUT = 1;
//first led on
IntDegF -= 10; //decrement 10
}
else
{
P1OUT = 64;
//less than ten, second led
IntDegF -= 1; //decrement one
}
}
else
dotemp();
//do the temp measure
}
42