Sie sind auf Seite 1von 8

ECE2049: Embedded Computing in Engineering Design

A Term -- Fall 2015


Lecture #16: Interrupts and Event Driven Code
Reading for Today:
Reading for Next Class:
HW #4 (on web):
Lab #2 (on web):

Example code
Review all since exam 1
Due MONDAY 9/28/2015
Sign-off Bonus Today (by 5 pm)
Report due 9/29 --> In class

Exam #2 Tuesday 9/29/2015


Last Class: Interfacing various sensors to ADC12
>> Single channel, single conversion and multi-channel, single conversion
>> Use code examples
>> Add new requirements as needed starting from working code

Using ADC12 Interrupts ->> Examples so far have used software setting of ADC12SC bit to start conversion and
then we've been polling ADC12BUSY bit to see when conversion is complete
//Enable and start (single) conversion
ADC12CTL0 |= ADC12SC + ADC12ENC;
// Poll busy bit waiting for conversion to complete
while (ADC12CTL1 & ADC12BUSY)
__no_operation();

>> This, like the swDelay() function is demo.c, is example of Busy Waiting

>> The main purpose of on-chip peripherals like the Timer and ADC is to remove
burdens from and provide services to the CPU
--> Having the CPU wait in a polling loop for a peripheral to complete is
something like the having the Queen waiting on the maid!

>>Interrupts=ExternalsignalsrequestingCPUaction
>>RequestareinitiatedoutsideofCPUandtendtooccurasynchronously
>>CanbeacceptedandservicedbyappropriateInterruptServiceRoutine(ISR)
NonMaskableInterrupts(NMI)=Cannotbedisabled
Mustbeserviced(highestpriorities)

Maskableinterruptscanbedisabled
Mustbeindividuallyenabledinordertoberecognizedand
serviced

>>ISRarelikefunctions(labeledsubblocksofcodethatexecuteandreturn)
>> Sources of interrupts are HARDWIRED in the MSP430
--> Only certain devices can generate interrupts

--> Interrupts have a fixed priority ranking that is SET during processor design

>> Fixed Addresses (and names in msp430f5529.h) are associated with each interrupt
source
--> INTERRUPT VECTOR TABLE Resides in Highest Addresses of FLASH

--> RESET and NMI can not be ignored!

Interruptsareanexampleofdoubleindirection
>>InhighlevellanguageslikeCthisisequivalenttoapointertoapointer
>>TheCPUknowswhatINTERRUPTVECTORaddressbetween0xFF80and
0xFFFEisassociatedwitheachinterruptsource(seetableabove)
>>IntheMSP430,whenaninterruptisreceivedfromaparticularsource(and
interruptsareenabled)theCPUgetstheaddressoftheInterruptService
Routine(ISR)storedintheInterruptVectortableautomaticallyandgoestotheISR
>>Butfirst,thecompilermustplaceaddressofISRinInterruptVectorTable!

>> Some things to note about Interrupts in the MSP430F5529


--> By DEFAULT, interrupts are disabled within ISRs
--> If you want higher priority interrupts to be able to preempt an ISR you must
specifically re-enable interrupts within the ISR
For example: Re-enabling interrupts within an ADC12 ISR
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
_BIS_SR(GIE); // re-enabling interrupts inside an ISP
// DON'T usually do this. Only when
// absolutely necessary
....
}

--> You do not HAVE to re-enable interrupts and should do so only when
necessary!
-- There's a reason that the default behavior is to disable them

>> Servicing ANY interrupt automatically places address and data information on the
Stack
--> The Stack is an area of RAM used for temporary storage by the CPU during
program execution
--> The Stack has a fixed size!

--> Having multiple layers of nested ISRs aside from being exceedingly difficult
to debug may cause stack overrun which is usually fatal to programs

Using Interrupts with the ADC12 to signal end of conversion


>> Set up control registers ADC12CTL0 and ADC12CTL1 and ADC12MCTLx same as
before for polling
>> Must also set ADC12IE register bit corresponding to the last MEMx register used in
conversion.
// Single channel, single converion (internal temp. Sensor)
// to ADC12MEM1 register
REFCTL0 &= ~REFMSTR;
ADC12CTL0 = ADC12SHT0_9 | ADC12REFON | ADC12ON;
ADC12CTL1 = ADC12SHP + ADC12CSTARTADD_1;
ADC12MCTL1 = ADC12SREF_1 + ADC12INCH_10;
ADC12IE = BIT1;
_BIS_SR(GIE);
. . .

// using ADC12MEM1 for conversion result


// enable interrupt for MEM1
// Global Interrupt enable

ADC12CTL0 |= ADC12SC + ADC12ENC;

//Start conversion

. . .
// Else where in the code define ISR
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR(void)
{
// Interrupt is generated when conversion (or last
// conversion if multi-channel) is complete so just
// read the results
adc_inTemp = ADC12MEM1;
// Move results to global
// variable adc_inTemp
}

--------------------------------------------------// Possible multi-channel ISR for current & temp sensors


// MEM1 still the LAST channel converted so ADC12IE = BIT1;
// (But first channel to convert is MEM0 so use ADC12CSTARTADD_0)
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
// Interrupt is generated when conversion (or last
// conversion if multi-channel) is complete
adc_inCurr = ADC12MEM0;
// Move results to global
adc_inTemp = ADC12MEM1; // variables
}

Using TimerA2 ISR to schedule ADC measurements


>> Configure Timer A2 to measure desired interval (e.g. 1/4 sec)
void config_timerA2(void)
{
// Use ACLK (TASSEL_1),clock divider of 1 (ID_0), and start
// timer counting in Up mode (MC_1)
TA2CTL = TASSEL_1 + MC_1 + ID_0;
TA2CCR0 = 8191;

// 8191+1 ACLK periods = 0.25 seconds

TA2CCTL0 = CCIE;

// TA2CCR0 interrupt enabled on Timer A2

>>Inside Timer A2 ISR start ADC12 conversion


// Timer A2 interrupt service routine
#pragma vector=TIMER2_A0_VECTOR
__interrupt void Timer_A2_ISR(void)
{
timer++;
// interrupts occur every 0.25 sec probably
// would still want to keep track of time
ADC12CTL0 |= ADC12SC + ADC12ENC;
//
//
//
//
//
}

// Start conversion

Could either POLL the ADC12BUSY bit here or use ADC12


interrupts in which case you'd need to implement ADC ISR
DON'T DO BOTH! Do not poll ADC if interrupts are enabled!!
This assumes ADC12 control registers have already been
configured

Your main() might look like this -- Assuming ADC12 measurements are being initiated
in TimerA2 ISR and ADC12 interrupts are enabled ...
void main()
{
// variable declarations
. . .
// stop watch dog
. . .
// do other initializations
_BIS_SR(GIE);

// Global Interrupt enable

adc12_config();
run_timerA2();
. . .
while (1)
{
. . .
if (timer == displayTime)
{
display_current_ADC_readings();
timer = 0;
. . .
}
. . .
}
}

>> Notice that there is no busy waiting and no ADC12 polling!

Das könnte Ihnen auch gefallen