Unformatted Document Excerpt
Coursehero >>
Utah >>
BYU >>
CS 124
Course Hero has millions of student submitted documents similar to the one
below including study guides, practice problems, reference materials, practice exams, textbook help and tutor support.
Course Hero has millions of student submitted documents similar to the one
below including study guides, practice problems, reference materials, practice exams, textbook help and tutor support.
8
Timers
Most CHAPTER modern microcontrollers provide a range of timers and the MSP430 is no exception. All devices contain two types of timer and some have five. Each type of timer module works in essentially the same way in all devices. Timer_A is identical in almost all MSP430s, for instance, except that a few have a different number of capture/compare channels. Watchdog timer: Included in all devices (newer ones have the enhanced watchdog timer+). Its main function is to protect the system against malfunctions but it can instead be used as an interval timer if this protection is not needed. Basic timer1: Present in the MSP430x4xx family only. It provides the clock for the LCD and acts as an interval timer. Newer devices have the LCD_A controller, which contains its own clock generator and frees the basic timer from this task. Real-time clock: In which the basic timer has been extended to provide a real-time clock in the most recent MSP430x4xx devices. Timer_A: Provided in all devices. It typically has three channels and is much more versatile than the simpler timers just listed. Timer_A can handle external inputs and outputs directly to measure frequency, time-stamp inputs, and drive outputs at precisely specified times, either once or periodically. There are internal connections to other modules so that it can measure the duration of a signal from the comparator, for instance. It can also generate interrupts. We used a few of its capabilities in earlier chapters and most of this chapter is devoted to Timer_A. Timer_B: Included in larger devices of all families. It is similar to Timer_A with some extensions that make it more suitable for driving outputs such as pulse-width
www.newnespress.com
276
Chapter 8
modulation. Against this, it lacks a feature of sampling inputs in Timer_A that is useful in communication. The simplest way to generating a delay is to use a software loop as in the section "Automatic Control Flashing Light by Software Delay" on page 91. This should be avoided wherever possible in favor of one of the hardware timers because the timers are more precise and leave the CPU free for more productive activities. Alternatively, the device can be put into a low-power mode if there is nothing else to be done. We already saw that the MSP430 spends much of its time asleep in many applications and is awakened periodically by a timer.
8.1
Watchdog Timer
The main purpose of the watchdog timer is to protect the system against failure of the software, such as the program becoming trapped in an unintended, infinite loop. Left to itself, the watchdog counts up and resets the MSP430 when it reaches its limit. The code must therefore keep clearing the counter before the limit is reached to prevent a reset. The operation of the watchdog is controlled by the 16-bit register WDTCTL. It is guarded against accidental writes by requiring the password WDTPW = 0x5A in the upper byte. A reset will occur if a value with an incorrect password is written to WDTCTL. This can be done deliberately if you need to reset the chip from software. Reading WDTCTL returns 0x69 in the upper byte, so reading WDTCTL and writing the value back violates the password and causes a reset. The lower byte of WDTCTL contains the bits that control the operation of the watchdog timer, shown in Figure 8.1. The RST/NMI pin is also configured using this register, which you must not forget when servicing the watchdog--we see why shortly. This pin is described in the section "Nonmaskable Interrupts" on page 195. Most bits are reset to 0 after a power-on reset (POR) but are unaffected by a power-up clear (PUC). This distinction is important in handling resets caused by the watchdog. The exception is the
7 WDTHOLD rw(0)
6 WDTNMIES rw(0)
5 WDTNMI rw(0)
4 WDTTMSEL rw(0)
3 WDTCNTCL r0(w)
2 WDTSSEL rw(0)
1 WDTISx rw(0)
0
rw(0)
Figure 8.1: The lower byte of the watchdog timer control register WDTCTL.
www.newnespress.com
Timers
277
WDTCNTCL bit, labeled r0(w). This means that the bit always reads as 0 but a 1 can be written to stimulate some action, clearing the counter in this case. The watchdog counter is a 16-bit register WDTCNT, which is not visible to the user. It is clocked from either SMCLK (default) or ACLK, according to the WDTSSEL bit. The reset output can be selected from bits 6, 9, 13, or 15 of the counter. Thus the period is 26 = 64, 512, 8192, or 32,768 (default) times the period of the clock. This is controlled by the WDTISx bits in WDTCTL. The intervals are roughly 2, 16, 250, and 1000 ms if the watchdog runs from ACLK at 32 KHz. The watchdog is always active after the MSP430 has been reset. By default the clock is SMCLK, which is in turn derived from the DCO at about 1 MHz. The default period of the watchdog is the maximum value of 32,768 counts, which is therefore around 32 ms. You must clear, stop, or reconfigure the watchdog before this time has elapsed. In almost all programs in this book, I take the simplest approach of stopping the watchdog, which means setting the WDTHOLD bit. This goes back to the first program to light LEDs, Listing 4.2. If the watchdog is left running, the counter must be repeatedly cleared to prevent it counting up as far as its limit. This is done by setting the WDTCNTCL bit in WDTCTL. The task is often called petting, feeding, or kicking the dog, depending on your attitude toward canines. The bit automatically clears again after WDTCNT has been reset. The MSP430 is reset if the watchdog counter reaches its limit. Recall from the section "Resets" on page 157 that there are two levels of reset. The watchdog causes a power-up clear, which is the less drastic form. Most registers are reset to default values but some retain their contents, which is vital so that the source of the reset can be determined. The watchdog timer sets the WDTIFG flag in the special function register IFG1. This is cleared by a power-on reset but its value is preserved during a PUC. Thus a program can check this bit to find out whether a reset arose from the watchdog. Listing 8.1 shows a trivial program to demonstrate the watchdog. I selected the clock from ACLK (WDTSSEL = 1) and the longest period (WDTISx = 00), which gives 1s with a 32 KHz crystal for ACLK. It is wise to restart any timer whenever its configuration is changed so I also cleared the counter by setting the WDTCNTCL bit. LED1 shows the state of button B1 and LED2 shows WDTIFG. The watchdog is serviced by rewriting the configuration value in a loop while button B1 is held down. If the button is left up for more than 1s the watchdog times out, raises the flag WDTIFG, and resets the device with a PUC. This is shown by LED2 lighting.
www.newnespress.com
278
Chapter 8
Listing 8.1: Program wdtest1.c to demonstrate the watchdog timer.
// wdtest1 . c - trival program to demonstrate watchdog timer // Olimex 1121 STK board , 32 KHz ACLK // J H Davies , 2007 -05 -10; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430x11x1 .h > // Specific device // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Pins for LEDs and button # define LED1 P2OUT_bit . P2OUT_3 # define LED2 P2OUT_bit . P2OUT_4 # define B1 P2IN_bit . P2IN_1 // Watchdog config : active , ACLK /32768 -> 1 s interval ; clear counter # define WDTCONFIG ( WDTCNTCL | WDTSSEL ) // Include settings for _RST / NMI pin here as well // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTCONFIG ; // Configure and clear watchdog P2DIR = BIT3 | BIT4 ; // Set pins with LEDs to output P2OUT = BIT3 | BIT4 ; // LEDs off ( active low ) for (;;) { // Loop forever LED2 = ~ IFG1_bit . WDTIFG ; // LED2 shows state of WDTIFG if ( B1 == 1) { // Button up LED1 = 1; // LED1 off } else { // Button down WDTCTL = WDTPW | WDTCONFIG ; // Feed / pet / kick / clear watchdog LED1 = 0; // LED1 on } } }
I have to admit with embarrassment that this program gave me a little trouble. In my first version I serviced the watchdog with the following line of C. The obvious idea was to clear the counter and leave everything else as it was:
WDTCTL = WDTPW | WDTCNTCL ; // Feed / pet / kick / clear watchdog
In my defense, this is given as an example in the family user's guide. Distressingly, the MSP430 seemed to reset as soon as the button was released, even if this was less than 1s after the power was applied. The reason is that this line overwrites the complete register and clears all the bits other than WDTCNTCL. In particular, the clock reverts to SMCLK, which reduces the period to 32 ms instead of 1s. It also wipes out the settings for the RST/NMI pin if I changed them. The correct approach is to rewrite the complete configuration to WDTCTL as in the listing. Do not try to set only the WDTCNTCL bit either, as follows:
WDTCTL_bit . WDTCNTCL = 1; // Crashes
www.newnespress.com
Timers
279
This causes a reset because it violates the password protection. Maybe the watchdog is not quite so simple after all. Example 8.1 Experiment with Listing 8.1, which you may need to adapt to your hardware. You might wish to try some of the stupid mistakes too--it is a good way of remembering them. You should find that LED2 remains lit permanently after a PUC because the WDTIFG bit needs to be cleared in software and I deliberately omitted this. It is cleared only after a POR, which can be requested by the Reset command in the debugger. Listing 8.2 shows a traditional example of how the watchdog is used to protect a program built around a paced loop. The general idea was explained in the section "Returning from a Low-Power Mode to the Main Function" on page 203. A timer is configured to generate interrupts every 10 ms. Its interrupt service routine restores active mode, which causes the program to resume the main loop. This services the watchdog before carrying out its tasks. Both the watchdog and the other timer run from ACLK and therefore continue to operate in low-power mode 3. Listing 8.2: Outline of a traditional paced loop protected by the watchdog.
// Configure a timer for interrupts every 10 ms and return to active mode // Configure watchdog for 16 ms period from 32\ , KHz ACLK # define WDTCONFIG ( WDTCNTCL | WDTSSEL | WDTIS1 ) WDTCTL = WDTPW | WDTCONFIG ; // Configure and clear watchdog for (;;) { _ _ l o w _ p o w e r _ m o d e _ 3 (); // Enter low power mode LPM3 // Wait here , pace loop until timer expires and ISR restores Active Mode WDTCTL = WDTPW | WDTCONFIG ; // Service watchdog // Tasks of paced loop }
What should we choose for the period of the watchdog? Clearly it should be greater than 10 ms. I chose the next period up, 16 ms, which is given by selecting ACLK with a period of 512 and setting WDTISx = 10. This may be a bit short. It does not matter too much if one pass through the paced loop is slower than 10 ms provided that the next is shorter, so that the program never lags by more than 10 ms and misses an interrupt. Unfortunately the next interval up is 250 ms, which is too long. It is important that the watchdog is serviced from the highest level of the program. This should never be done in an interrupt service routine. In this example it would be tempting to reset the watchdog in the ISR for the timer but this would lose a great deal of protection.
www.newnespress.com
280
Chapter 8
Suppose that the program became stuck in one of the tasks of the paced loop. Interrupts would still be generated periodically and the watchdog would continue to be serviced correctly if this were done in the ISR. On the other hand, the watchdog would expire and cause a reset with the structure in Listing 8.2. It is possible that the watchdog may time out during the initialization of a program, which is carried out by the startup code before the user's main() function is called. This would happen if it took longer than 32 ms to initialize the RAM, which is possible if a large number of global or static variables are used. In EW430 you can supply a function _ _low_level_init(), which is called before the RAM is initialized. The watchdog can be stopped or reconfigured here. Watchdogs vary considerably from one type of microcontroller to another. Some have a set of passwords that must be used in a prescribed order, rather than a single value; a reset occurs if a password is used out of sequence. Windowed watchdogs must be serviced only during a particular part of their period, such as the last quarter; clearing the watchdog earlier than this causes a reset. Some have their own built-in oscillator, which protects them from failure of the main clocks. Many watchdogs are controlled by write-once registers, which means that their configuration cannot be changed after an initial value has been written. This would be a problem in the MSP430, where the watchdog may need to be reconfigured for low-power modes. The reset caused by the watchdog can be a nuisance during development because a PUC destroys much of the evidence that could help you to detect a problem that caused the watchdog to time out. A solution might be to generate an interrupt rather than a reset by using the watchdog as an interval timer, which is described shortly. The interrupt service routine could copy critical data to somewhere safe, signal a problem by lighting an LED, or simply cause execution to stop on a breakpoint. Nagy [4] has further suggestions.
8.1.1
Failsafe Clock Source for Watchdog Timer+
Newer devices, including the MSP430F2xx family and recent members of the MSP430x4xx, have the enhanced watchdog timer+ (WDT+). This includes fail-safe logic to preserve the watchdog's clock. Suppose that the watchdog is configured to use ACLK and the program enters low-power mode 4 to wait for an external interrupt, as in Listing 7.1. The old watchdog (WDT) stops during LPM4 and resumes counting when the device is awakened. In contrast, WDT+ does not let the device enter LPM4 because that would disable its clock. Therefore it is not possible to use LPM4 with WDT+ active; the
www.newnespress.com
Timers
281
watchdog must first be stopped by setting WDTHOLD. Similarly, it is not possible to use LPM3 if WDT+ is active and gets its clock from SMCLK. If its clock fails, WDT+ switches from ACLK or SMCLK to MCLK and takes this from the DCO if an external crystal fails. The watchdog interval may change dramatically but there must be serious problems elsewhere if this happens.
8.1.2
Watchdog as an Interval Timer
The watchdog can be used as an interval timer if its protective function is not desired. Set the WDTTMSEL bit in WDTCTL for interval timer mode. The periods are the same as before and again WDTIFG is set when the timer reaches its limit, but no reset occurs. The counter rolls over and restarts from 0. An interrupt is requested if the WDTIE bit in the special function register IE1 is set. This interrupt is maskable and as usual takes effect only if GIE is also set. The watchdog timer has its own interrupt vector, which is fairly high in priority but not at the top. It is not the same as the reset vector, which is taken if the counter times out in watchdog mode. The WDTIFG flag is automatically cleared when the interrupt is serviced. It can be polled if interrupts are not used. Many applications need a periodic "tick," for which the watchdog timer could be used in interval mode. The disadvantage is the limited selection of periods, but 1s is convenient for a clock. Some of the previous examples that used Timer_A could be rewritten for the watchdog instead and its use is illustrated in the standard sets of code examples from TI. Example 8.2 Rewrite the simple clock in Listing 7.4 to use the watchdog as a 1s interval timer instead of Timer_A.
Checklist--Have You . . .
Stopped the watchdog or ensured that it is serviced frequently enough? Set the WDTIE bit in the special function register IE1 if you want interrupts? (It is easy to forget this because it is not in WDTCTL.)
8.2
Basic Timer1
Basic Timer1 is present in all MSP430xF4xx devices and is perhaps the most straightforward module in the MSP430, as you might guess from its name. It provides the clock
www.newnespress.com
282
Chapter 8
BTSSEL SMCLK ACLK at 32KHz BTCNT1 ACLK/256 5 128 Hz BTFRQx BTIFG Clock to LCD module (not LCD_A) Interrupt if BTIE set BTIPx BTDIV CLK2 BTCNT2 1 Hz to RTC
Figure 8.2: Simplified block diagram of Basic Timer1.
7 BTSSEL
6 BTHOLD
5 BTDIV
4 BTFRFQx
3
2
1 BTIPx
0
Figure 8.3: The Basic Timer1 control register BTCTL. for the LCD module (but not LCD_A) and generates periodic interrupts. A slightly simplified block diagram is shown in Figure 8.2. Newer devices also contain a real-time clock driven by a signal at 1 Hz from Basic Timer1. The register BTCTL shown in Figure 8.3 controls most of the functions of Basic Timer1 but there are also bits in the special function registers IFG2 and IE2 for interrupts. An unusual feature of this module is that BTCTL is not initialized by a reset: This must be done by the user. It is not even specified whether the timer is running or not. The counters are not initialized either and this should be done before the timer is started or the first interval will be incorrect. There are two 8-bit counters in Basic Timer1, which can either be used independently or cascaded for longer intervals: BTCNT1: Takes its input from ACLK and provides the clock for the LCD module at frequency fLCD . The two BTFRFQx bits select the value of fLCD , which can vary from fACLK /256 to fACLK /32 in powers of 2. It is assumed that fACLK = 32 KHz. This gives fLCD from 128 Hz to 1 KHz, which should be suitable for the LCD. The calculation of fLCD is explained in the section "Driving an LCD from an MSP430x4xx" on page 256. The LCD_A controller does not need a clock from BTCNT1, so the counter's only function in this case is as a prescaler for BTCNT2.
www.newnespress.com
Timers
283
BTCNT2: Can be used independently of BTCNT1, in which case the BTSSEL bit selects the clock from ACLK or SMCLK. For longer intervals, BTCNT2 can be clocked from the output of BTCNT1 at a frequency of fACLK /256. Set the BTDIV bit to cascade the counters in this way. Setting the BTHOLD bit stops BTCNT2, but stops BTCNT1 only if BTDIV is also set. BTCNT2 provides no output signals. Instead it raises the BTIFG flag at a frequency determined by the BTIPx bits. The range goes from fCLK2 /256 to fCLK2 /2, where fCLK2 is the frequency of the clock input to BTCNT2. With the counters cascaded this gives a period from about 16 ms to 2s. The BTIFG flag is in the IFG2 register. An interrupt also is requested if the BTIE bit is set in IE2. The interrupt is maskable so GIE must also be set for the interrupt to be accepted. The BTIFG flag is cleared automatically when the interrupt is serviced. Alternatively the flag can be polled, in which case it must be cleared in software. We used Timer_A for many applications in Chapters 4 and 6. It would have been better to have used Basic Timer1 for most of these examples, except that it is restricted to the MSP430xF4xx family. The first instance was to flash a light by polling the TAIFG flag in the section "Automatic Control: Flashing a Light by Polling Timer_A" on page 105. This could be done in exactly the same way with Basic Timer1 and the BTIFG flag. The only difference is that a restricted range of intervals is available, unlike Timer_A in Up mode. Similarly, Listings 6.4 and 6.5 show how to do the same task using interrupts. Basic Timer1 could again be used instead. This is a convenient way of generating a real-time interrupt (RTI), which can be used as a "heartbeat" to wake a program periodically. Again this was done using Timer_A in Listing 6.8 but Basic Timer1 is often a better choice. Example 8.3 This is not very exciting but completes the set of possible timers: Rewrite the simple clock in Listing 7.4 to use Basic Timer1 as a 1s interval timer instead of Timer_A.
8.2.1
Real-Time Clock
A Real-Time Clock (RTC) module has been added to recent devices in the MSP430xFxx family. It counts seconds, minutes, hours, days, months, and years. Alternatively it can be used as a straightforward counter, which I describe briefly at the end of this section, but for now I assume that it is configured in calendar mode by setting RTCMODEx = 11 in the control register RTCCTL. The bits are shown in Figure 8.4. This register is initialized after
www.newnespress.com
284
7
Chapter 8
6 5 4 3 RTCTEVx 2 1 RTCIE 0 RTCFG
RTCBCD RTCHOLD
RTCMODEx
Figure 8.4: The Real-Time Clock control register RTCCTL. a power-on reset, unlike BTCTL, and the RTCHOLD bit is set so that the clock does not run by default. The current time and date are held in a set of registers that contain the following bytes: Second (RTCSEC). Minute (RTCMIN). Hour (RTCHOUR), which runs from 023 (24-hour format). Day of week (RTCDOW), which runs from 06. Day of month (RTCDAY). Month (RTCMON).
Year (RTCYEARL), assuming BCD format. Century (RTCYEARH), assuming BCD format.
The registers are arranged in pairs that can also be accessed as words. For example, RTCYEAR = RTCYEARH:RTCYEARL and RTCTIM0 = RTCMIN:RTCSEC. Their values can be stored either as normal binary numbers or as BCD. The latter is more convenient for driving a display and is selected by setting the RTCBCD bit in RTCCTL. The registers that hold the date and time are initialized when calendar mode is selected but the user will obviously need to store the current values if "real" time is to be real. The module automatically accounts for the different number of days in the months and allows for leap years during the current century. In principle it could compute the day of week from the rest of the date but it does not: RTCDOW is effectively an independent 06 counter, incremented daily. The user must initialize this appropriately and decide which day is the start of the week. There is no provision for a 12-hour clock with an AM/PM flag so this would have to be done in software. The clock needs a 1 Hz input, which it takes from Basic Timer1. The RTC module therefore takes control over Basic Timer1 and cascades the counters as if BTDIV = 1. It remains possible to take interrupts from BTCNT2 so the BTIPx bits are still useful.
www.newnespress.com
Timers
285
The Real-Time Clock has an interrupt flag RTCFG and corresponding enable bit RTCIE in RTCCTL. The flag is set every minute, every hour, daily at midnight, or daily at noon depending on the RTCTEVx bits. The interrupt vector is shared with Basic Timer1. It is maskable and can be used in two ways: Interrupts are generated by the Real-Time Clock module if RTCIE is set. Both the BTIFG and RTCFG flags are set at an interrupt and cleared automatically when it is serviced. The interval is determined by RTCTEVx. Interrupts come from Basic Timer1 as described earlier if RTCIE is clear. The interval is determined by BTIPx. The Real-Time Clock sets its RTCFG flag according to RTCTEVx but this does not request an interrupt. A program can poll the flag to check whether an interval of time has elapsed and must clear RTCFG in software.
There is no alarm clock--an interrupt requested at a specified time and date--which seems a curious omission. This must be implemented in software if desired. "Gotcha!" The flag is called RTCFG, not RTCIFG as you might expect. Listing 8.3 shows the relevant functions from a program to run a clock on the TI MSP430FG4618/F2013 Experimenter's Board. The display shows hours and minutes, separated by a colon that flashes at 1 Hz. An interrupt from Basic Timer1 every 0.5s toggles the colon, polls RTCFG and updates the digits if a minute has passed. The flag is cleared by software. The current version of the header file does not define bitfields for BTCTL and RTCCTL so I have had to use masks instead. Listing 8.3: Part of rtclock3.c to configure the Real-Time Clock and handle interrupts from Basic Timer1.
void InitRTC ( void ) { // BCD mode , stop RTC , calendar mode ( clock from BTCNT2 .6) , 1 min flag // ( interrupt remains under control of basic timer , not RTC ) // Clears RTC registers and configures basic timer a p p r o p r i a t e l y RTCCTL = RTCBCD | RTCHOLD | RTCMODE_3 | RTCTEV_0 ; // Initialize time and date // RTCTIM0 = 0 x0000 ; // Minutes and seconds RTCHOUR = 0 x17 ; // After - tea relaxation RTCDOW = 0; // Day of week - Sunday = 0? RTCDATE = 0 x0513 ; // Month and day RTCYEAR = 0 x2007 ; // Year (4 digits ) // Set basic timer for 0.5 s interrupts ( RTC overrides SSEL , HOLD , DIV ) BTCTL = B T _ f C L K 2 _ D I V 6 4 ; // Divide frequency by 64 BTCNT1 = 0; // Clear counters BTCNT2 = 0;
www.newnespress.com
286
Chapter 8
IE2_bit . BTIE = 1; RTCCTL &= ~ RTCHOLD ; RTCCTL |= RTCFG ; IFG2_bit . BTIFG = 1;
// Enable basic timer interrupts // Start RTC - No bitfield ! // Set minute flag and ... // ... request an interrupt to } // start display // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for basic timer ; flag cleared a u t o m a t i c a l l y // Time stored in RTC as seconds , minutes , and hours in BCD format // WARNING : potential problem of accessing RTC registers asynchronously // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = B A S I C T I M E R _ V E C T O R _ _interrupt void B A S I C T I M E R _ I S R ( void ) { LCDMem [2] ^= SEG_H ; // Toggle colon separator if (( RTCCTL & RTCFG ) != 0) { // Time needs to be updated ? RTCCTL &= ~ RTCFG ; // Clear flag manually LCDMem [1] = LCDHexChar [ RTCMIN & 0 x0F ]; LCDMem [2] = LCDHexChar [( RTCMIN >> 4) & 0 x0F ]; LCDMem [3] = LCDHexChar [ RTCHOUR & 0 x0F ]; LCDMem [4] = LCDHexChar [( RTCHOUR >> 4) & 0 x0F ]; } }
There are warnings in the user's guide about accessing the registers of the Real-Time Clock. The problem is that changes in these registers are triggered by ACLK but reading or writing from a program is synchronized to MCLK. It is therefore possible that you might try to read the registers while they are being updated, in which case the result would be corrupted. This is a classic example of the issue described in the section "The Shared Data Problem" on page 197. It should not be a problem in Listing 8.3 because the interrupt is requested just after the clock has been updated, at which point the values are stable for nearly 1s. The safest, general approach is to stop the clock while a reading is taken but this could lose 1s if a tick is missed during the read. Alternatively the registers can be read several times and a majority vote taken, which should discard any erroneous values. Another solution is to copy the values from the module into a set of "shadow" registers in RAM during interrupts from the Real-Time Clock. The shadow registers can be read safely from the main program provided that interrupts are disabled during the read. This will not cause any time to be lost because the Real-Time Clock is updated by hardware. The clock is stopped while values are written to its registers, which could again cause the loss of 1s. I stopped the Real-Time Clock with the RTCHOLD bit in Listing 8.3 and started it only when all registers had been initialized.
www.newnespress.com
Timers Example 8.4
287
The simplest program for a clock uses the Real-Time Clock to display only minutes and seconds without the flashing colon. Try this. Use interrupts from the RTC rather than Basic Timer1. Example 8.5 Extend this program to display the date (month and day) when a button is pressed down. Button S1 is connected to P1.0 on the TI MSP430FG4618/F2013 Experimenter's Board. This is much easier with the Real-Time Clock module. Without it, we would have to service interrupts regularly to update the time even while the date was displayed. The Real-Time Clock can instead be used as a straightforward, 32-bit counter if preferred. This is chosen with the RTCMODEx bits in RTCCTL, which also select the source of the clock. Interrupts can be taken from bits 8, 16, 24, or 32 of the counter. The maximum interval is 232 s, which is over 100 years.
8.3
Timer_A
This is the most versatile, general-purpose timer in the MSP430 and is included in all devices. It was introduced in the section "Automatic Control: Flashing a Light by Polling Timer_A" on page 105 and its general features will be familiar if you have used a general-purpose timer in any other modern microcontroller. There are two main parts to the hardware: Timer block: The core, based on the 16-bit register TAR. There is a choice of sources for the clock, whose frequency can be divided down (prescaled). The timer block has no output but a flag TAIFG is raised when the counter returns to 0. Capture/compare channels: In which most events occur, each of which is based on a register TACCRn. They all work in the same way with the important exception of TACCR0. Each channel can Capture an input, which means recording the "time" (the value in TAR) at which the input changes in TACCRn; the input can be either external or internal from another peripheral or software.
www.newnespress.com
288
Chapter 8 Compare the current value of TAR with the value stored in TACCRn and update an output when they match; the output can again be either external or internal. Request an interrupt by setting its flag TACCRn CCIFG on either of these events; this can be done even if no output signal is produced. Sample an input at a compare event; this special feature is particularly useful if Timer_A is used for serial communication in a device that lacks a dedicated interface.
Timer_A is modular and the number of capture/compare channels varies between devices. Most have three channels but the smallest members of the MSP430F2xx family have only two and some earlier devices had more. The number of channels is sometimes appended to the name as in Timer_A3. Capture/compare channel 0 is special in two ways. Its register TACCR0 is taken over for the modulus value in Up and Up/Down modes, so that it is no longer available for its usual functions. It also has its own interrupt vector with a higher priority than the other interrupts from Timer_A, which all share a common vector. Therefore channel 0 should be chosen for the most urgent tasks if it is free. It is important to realize that all channels within Timer_A share the same timer block: There is only one TAR. This ensures that actions performed by the different channels are precisely synchronized. The drawback is that they all work at the same fundamental frequency. Outputs must be supervised by software rather than driven purely by hardware if you need them to change at different frequencies or not to be periodic at all. A few devices have two Timer_A modules, which operate with independent time bases. A general principle is to use the hardware of Timer_A for the part of an event that needs precise timing and to reserve software for the less critical parts. This means that signals to be timed should be connected directly to capture inputs so that there is no delay. Outputs should be driven directly from the timer so that they change as soon as a compare event happens. Of course this works only if suitable pins are available or there is an internal connection to the peripheral concerned. Software can then respond to the event--calculate the duration of an input or set up the next output--but the delay required for this does not compromise the timing of the signals. Timer_A is well documented. There are extensive application notes, some of which I mention later, and over 160 pages in Application Reports (slaa024). Many of TI's code examples illustrate its applications.
www.newnespress.com
Timers
TASSELx TACLK ACLK SMCLK INCLK IDx divider /1/2/4/8 timer clock Timer block
289
16-bit timer register MCx TAR
TAIFG
Capture/compare channel 1 TA1 inputs CCI1A CCI1B GND VCC CCISx CMx capture mode CCI comparator
EQU0 output mode OUTMODx
TA1 output OUT1
capture
EQU1
TACCR1 CAP
TACCR1 CCIFG (CCIFG1) SCCI
latch
Figure 8.5: Simplified block diagram of Timer_A showing the timer block and capture/compare channel 1. The circles show external signals that may be brought out to pins of the device. Figure 8.5 shows a simplified block diagram of the timer block and a typical capture/compare channel. Many of the internal signals are omitted for clarity and it emphasizes different features from Figure 4.7. The internal compare signal EQU0 from channel 0 is particularly important because it plays an important role in the outputs from the other channels and controls the timer itself in Up and Up/Down modes.
8.3.1
Timer Block
This contains the 16-bit timer register TAR, which is central to the operation of the timer. It is controlled by the register TACTL shown in Figure 4.8. Remember that a timer is really no more than a counter and has no direct concept of time (the Real-Time Clock is an exception). It is the programmer's job to establish a relation between the value in the counter and real time. This depends essentially on the frequency of the clock for the timer. It can be chosen from four sources by using the TASSELx bits: SMCLK is internal and usually fast (megahertz).
ACLK is internal and usually slow, typically 32 KHz from a watch crystal but may be taken from the VLO in the MSP430F2xx family.
www.newnespress.com
290
Chapter 8
TACLK is external. INCLK is also external, sometimes a separate pin but often it is connected through an inverter to the pin for TACLK so that INCLK = TACLK.
TAR increments on the rising (positive) edge of the clock. The arrangement INCLK = TACLK in many devices allows TAR to be clocked on the falling (negative) edge of the external clock if required. An accurate, stable clock source is needed if the timer is to work in real time. This generally requires a crystal, described in the section "Crystal Oscillators, LFXT1 and XT2" on page 165. An alternative is to use the frequency of the AC mains if available. This is not particularly stable over short times but power companies usually aim to keep the average frequency accurate at 50 or 60 Hz over a day. See Section 6.3.8.7 of Application Reports (slaa024). The frequency of the incoming clock can be divided down by 2, 4, or 8 if desired by configuring the IDx bits. A slower clock reduces the resolution of the timer so that events are timed less precisely. Against this, it increases the natural period of the timer before it overflows, rolls over, and restarts from 0. It is not difficult to count the number of overflows for intervals longer than the period but of course it is easier to avoid it. The trade-off between resolution and period by choosing different clocks is shown in Table 8.1. The period of the timer can range from 4 ms with the fastest SMCLK to 16s with a 32 KHz ACLK and maximum division (even longer with the 12 KHz VLO). I assume that SMCLK runs at the same frequency as MCLK but it can also be divided in the MSP430x1xx and MSP430F2xx families, which allows longer periods from SMCLK. Table 8.1: Resolution and period of Timer_A in the Continuous mode with different clocks and input dividers. Values have been rounded for clarity.
Input clock Source SMCLK SMCLK SMCLK ACLK ACLK Frequency 16 MHz 1 MHz 1 MHz 32 KHz 32 KHz 1 1 8 1 8 Timer clock Divider Resolution
1 16
Range of timer Frequency 240 Hz 15 Hz 2 Hz 1 Hz 2 1 Hz 16 Period 4 ms 66 ms 0.5 s 2s 16 s s s s s s
1 8 31 240
www.newnespress.com
Timers These periods all apply to the Continuous mode, in which TAR cycles through its full range. The timer has four modes of operation, selected with the MCx bits:
291
Stop (MC = 0): The timer is halted. All registers, including TAR, retain their values so that the timer can be restarted later where it left off. Continuous (2): The counter runs freely through its full range from 0x0000 to 0xFFFF, at which point it overflows and rolls over back to 0. The period is 216 = 65,536 counts. This mode is most convenient for capturing inputs and is also used when channels provide outputs with different frequencies or that are not periodic at all. Up (1): The counter counts from 0 up to the value in TACCR0, the capture/compare register for channel 0. It returns to 0 on the next clock transition. The period is (TACCR0 + 1) counts. For example, if TACCR0 = 4, the sequence of counts is 0, 1, 2, 3, 4, 0, 1, . . . with period 5. Up mode is usually used when all channels provide outputs at the same frequency, often for pulse-width modulation. Up/Down (3): The counter counts from 0 up to TACCR0, then down again to 0 and repeats. The period is 2 TACCR0 counts. For example, if TACCR0 = 3, the sequence of counts is 0, 1, 2, 3, 2, 1, 0, 1, . . . with period 6. This is a specialized mode, typically used for centered pulse-width modulation. Precise control of the period of the timer is available in Up and Up/Down modes. The disadvantage is the loss of channel 0, whose register TACCR0 is taken over to hold the upper limit of the count--the modulus. Most modern timers include a modulus register in the timer block and Timer_A seems a bit primitive in this respect. The count in TAR and the divider can be cleared by writing a 1 to the TACLR bit in TACTL. This also resets the direction of counting in Up/Down mode. It is a good idea to do this whenever the timer is configured to ensure that the first period will be correct. The TACLR bit automatically clears itself after use. The flag TAIFG in TACTL is set when the timer counts to 0 and a maskable interrupt is requested if the TAIE bit is set. We used this several times already. The family user's guide always shows count in italics to emphasize that actions such as setting TAIFG occur only as a result of normal counting. They do not occur if the appropriate value is written directly to a register. For example, setting TACLR clears TAR but does not set TAIFG. You are strongly advised to stop the timer before modifying its operation (except the interrupt enables, interrupt flags, and TACLR) to avoid possible errors. Often the timer
www.newnespress.com
292
Chapter 8
clock is not synchronous with the CPU clock, such as when the timer is supplied from ACLK. In this case it is best to stop the timer before reading the value of TAR. Alternatively, the timer may be read multiple times while operating and a majority vote taken in software to determine the correct reading. This is not an issue when reading values from the TACCRn registers after a capture because they should be stable. An advantage of the 16-bit architecture of the MSP430 is that registers of the timer can be read in a single instruction. It can be awkward to read 16-bit timers in an 8-bit processor because the time may change between the two read instructions.
8.3.2
Capture/Compare Channels
Timer_A has three channels in most MSP430s although channel 0 is lost to many applications because its register TACCR0 is needed to set the limit of counting in Up and Up/Down modes, as we have just seen. Each channel is controlled by a register TACCTLn, shown in Figure 8.6. The central feature of each channel is its capture/compare register TACCRn. In Capture mode this stores the "time"--the value in TAR--at which an event occurs on the input; in Compare mode it specifies the time at which the output should next be changed and an interrupt requested. The mode is selected with the CAP bit. This is cleared by default so that the channel is in Compare mode. Any mixture of capture and compare channels can be used and the mode can be switched freely from one to the other. Remember to configure pins if you want to connect them to the timer. Otherwise the timer may produce a signal but it does not appear outside the chip. Typically this needs PnSEL.x = 1 to select the module rather than digital input/output and the appropriate value in PnDIR.x for input or output. Check the Application Information tables in the data sheet.
15 CMx 7
14
13 CCISx
12
11 SCS
10 SCCI 2 OUT 1
9
8 CAP 0 CCIFG
6 OUTMODx
5
4 CCIE
3 CCI
COV
Figure 8.6: The Timer_A capture/compare control register TACCTLn.
www.newnespress.com
Timers
293
Capture Mode Hardware
An event can be a rising edge, falling edge, or either edge on the input according to the capture mode bits CMx. The CCISx bits in TACCTLn select the input to be captured. Two of these, CCInA and CCInB, come from outside the timer module. They have many possible connections, set out in the section on Timer_A in the data sheet for the particular device. Often CCInA is connected to external pin TAn while CCInB is connected internally to another module. Here are the internal connections for the F20x1 as an example: CCI0B is connected to ACLK, which allows the frequency of SMCLK to be compared with ACLK. This enables a frequency-locked loop to be completed in software to synchronize the two clocks. CCI1B comes from CAOUT, the output of the comparator. This allows precise timing of a measurement without the overhead and delay that would arise if software were needed to trigger a capture when CAOUT changed. This provides an economical form of analog-to-digital conversion and is described in the section "Comparator_A" on page 371.
The internal connections are examples of the "system on chip" (SoC) theme of the MSP430, which enables peripherals to work together effectively. It avoids the delays that would be introduced by software and saves power because the CPU need not be restarted simply to notify one module about an event in another. The other two inputs that can be selected by CCISx are constants, GND and VCC. This looks pointless but their purpose is to allow captures from software. To do this, set CMx = 11 to capture both edges and set CCIS1 = 1 to select the pair of internal inputs. Toggling CCIS0 then inverts the apparent input and thus generates an event that can be captured. The state of the selected input can always be read in the CCI bit of TACCTLn. This input can change at any time and it is possible for a hardware "race" to occur if the input changes at the same time as the timer clock. The capture hardware therefore includes a synchronizer, which is enabled by setting the SCS bit. Capture events are then recorded on the next falling edge of the timer clock that follows the trigger, midway between increments of TAR. This bit should normally be set for safety; in fact I do not know when the synchronizer should not be used. When a capture occurs, the current value of TAR is copied into TACCRn and the channel flag TACCRn CCIFG (CCIFGn for short) is set. As usual, a maskable interrupt is
www.newnespress.com
294
Chapter 8
requested if the corresponding enable bit CCIE in TACCTLn is set. It is important to react rapidly to a capture because most applications need to compute the difference in time between successive captures. The first value must be stored away so that TACCRn is ready for the next capture. The capture overflow bit COV is set if another capture occurs before TACCRn has been read following the previous event. This warns the program that an event has been missed. Some timers include an extra buffer to allow recovery from a single missed event but Timer_A does not.
Compare Mode Hardware
The purpose of Compare mode is to produce an output and interrupt at the time stored in TACCRn. Several actions are triggered when TAR counts to the value in TACCRn: The internal signal EQUn is set. This in turn raises the CCIFGn flag and requests an interrupt if enabled. The output signal OUTn is changed according to the mode set by the OUTMODx bits in TACCTLn. The input signal to the capture hardware, CCI, is latched into the SCCI bit. I prefer to regard this as a separate Sample mode of Timer_A because it is an input but controlled by the hardware usually used for output. Often there is no need for an external output and a channel is used purely to provide a flag or interrupt, either periodically or after a single delay. We have done this several times already and will see more examples later. In some devices internal SoC connections enable CCIFGn to trigger other modules directly. These include the digital-to-analog converter (DAC, described in the section "Digital-to-Analog Conversion" on page 485) and direct memory access (DMA) controller. The output modes are listed in Table 8.2. They are more complicated than you might expect because changes in all channels can be triggered by EQU0 as well as EQUn. I illustrate the operation in later sections but here is a quick summary of their typical uses: Output (mode 0): In which output is controlled directly by the OUT bit in TACCTLn; TAR has no influence. It is as if the pin is used for normal, digital output but operated via Timer_A. This mode is used to set up the initial state of the output before compare events take over. For example, the first period of pulse-width modulation (PWM) may be erratic if this is not done, but it often does not matter.
www.newnespress.com
Timers Table 8.2: Output modes for capture/compare channel n (not all are applicable to TACCR0) and the counter mode in which each is most useful. The precise point at which the `TACCR0' actions occur is explained in the text.
Mode 0 1 2 3 4 5 6 7 Actions at TACCRn/"TACCR0" Output Set Toggle/Reset Set/Reset Toggle Reset Toggle/Set Reset/Set Most useful in counter mode (Output is controlled by OUT bit) Continuous Up/Down Up (Doubles period) Continuous Up/Down Up
295
Toggle (mode 4): Provides a simple way of switching a load on and off for equal times (50% duty cycle) and can be used even with channel 0 in Up and Up/Down modes. The disadvantage is that the load is toggled only once per cycle of the timer and its frequency is therefore halved (period doubled). Set (mode 1) and Reset (mode 5): Typically used for single changes in the output, usually in the Continuous mode. Reset/Set (mode 7) and Set/Reset (mode 3): Typically used for periodic, edgealigned PWM in Up mode of the counter. In this case the first action takes place when TAR matches TACCRn and the second action occurs when TAR returns to 0, one count after the match to TACCR0. (This is not made entirely clear in the user's guides.) Toggle/Reset (mode 2) and Toggle/Set (mode 6): Typically used for center-aligned PWM in Up/Down mode. The first action takes place when TAR matches TACCRn and the second occurs when TAR matches TACCR0. The second action is needed only once to fix the sign of the waveform; all subsequent action is done by toggling at matches of TACCRn. Please note that these are the typical, most straightforward uses of the different output modes in the different modes of the counter. In general you can use any output mode in any counter mode but the results may be confusing. Be warned that the EQU0 signal always influences modes 2, 3, 6, and 7. This applies even in the Continuous mode of the counter, when TACCR0 does not determine the maximum count. These four modes are useless with channel 0 because the two actions occur simultaneously.
www.newnespress.com
296
Chapter 8
A weakness in the design of the output unit can cause glitches in the output when the output mode is changed. Details are given in the user's guides. The most common changes in practice are between modes 1 and 5, set and reset, which are safe in both directions. If there is any doubt, use mode 7 as an intermediate step to avoid glitches. The TACCTLn registers are cleared by a power-on reset (they are not affected by a power-up clear). This means that the default state of a channel is in the Compare mode with the output determined by the OUT bit, which is clear. Thus an output pin immediately is driven low when it is connected to Timer_A. This is incorrect if the output should be high in its idle state, which often applies to communications. The OUT bit should be set before configuring the pin in this case.
8.3.3
Interrupts from Timer_A
Interrupts can be generated by the timer block itself (flag TAIFG) and each capture/compare channel (flag TACCRn CCIFG or CCIFGn for short). TACCR0 is privileged and has its own interrupt vector, TIMERA0_VECTOR. Its priority is higher than the other vector, TIMERA1_VECTOR, which is shared by the remaining capture/compare channels and the timer block. The CCIFG0 flag is cleared automatically when its interrupt is serviced but this does not happen for the other interrupts because the interrupt service routine (ISR) must first determine the source of the interrupt. The obvious way of doing this is to poll TAIFG and all the CCIFGn flags to locate which is active, clear the flag, and service the interrupt. Unfortunately this is slow, which is particularly undesirable in an ISR. The MSP430 therefore provides an interrupt vector register TAIV to identify the source of the interrupt rapidly. When one or more of the shared and enabled interrupt flags is set, TAIV is loaded with the value corresponding to the source with the highest priority. The possible values in TAIV for Timer_A3 are listed in Table 8.3 with their priorities and you see from the missing Table 8.3: Interrupt vector register TAIV for Timer_A3.
TAIV contents 0x0000 0x0002 0x0004 0x0006 0x0008 0x000A Source No interrupt pending Capture/compare channel 1 Capture/compare channel 2 -- -- Timer overflow Flag CCIFG1 CCIFG2 Priority Highest TAIFG Lowest
www.newnespress.com
Timers entries that this was designed with five channels (04) in mind. An interrupt service routine can read TAIV to locate the source immediately.
297
Any access to TAIV automatically resets both TAIV and the corresponding flag. If another interrupt is pending, TAIV is reloaded with the value for the source with the highest priority and another interrupt is requested as soon as the current one is serviced. This approach is similar to the handling of other interrupts in the MSP430 and saves the program having to acknowledge interrupts by clearing the flag, which is required by most processors. A side effect is that it makes debugging "interesting."1 Similar vectors are provided for other modules with shared interrupts. The values in TAIV are deliberately chosen to be even numbers because this allows a jump table to be used for selecting the appropriate action. The idea is to add TAIV to the program counter PC. The add.w instruction is followed by a sequence of jmp instructions, one for each possible value of TAIV. Each jmp occupies 2 bytes, hence the multiples of two in TAIV. Alternatively, reti can be used if no action is required and again takes 2 bytes of memory. This sounds complicated but is straightforward in practice, as shown in Listing 8.4. This flashes the LED (yet again--sorry) in an eZ430F2013. The LED is connected to P1.0, which cannot be routed to the timer, so the action must be performed by software. I configured the timer for the Continuous mode and enabled interrupts on TAIFG when TAR returns to 0. The LED is turned on when this interrupt is serviced. It is turned off again in another ISR when TAR matches TACCR1. This is equivalent to the Reset/Set mode (7) of the output unit. The two interrupts share a vector and must therefore be decoded with TAIV. Timer_A runs in the Continuous mode from SMCLK divided by 8. This is a frequency of about 125 KHz and the period of TAR is about s from Table 8.1. The value of 0x2000 in TACCR1 causes the interrupt to occur one eighth of the way through the cycle of TAR. 1 The LED is therefore illuminated for about /16 s and dark for 7/16 s in each cycle. Listing 8.4: Part of program tmrints1.s43 to show use of TAIV in assembly language.
; Set up timer channel 1 mov .w #0 x2000 , TACCR1 ; Full range /8 ( short flash of LED ) mov .w # CCIE , TACCTL1 ; Interrupts on TACCR1 compare ; Start timer : SMCLK , prescale /8 , continuous mode , interrupts , clear TAR
1
This applies to smaller devices, such as the F20xx. Larger devices, such as the FG416x, have a more powerful emulation module and TAIV seems to be better protected against the debugger.
www.newnespress.com
298
Chapter 8
mov .w # TASSEL_2 | ID_3 | MC_2 | TACLR | TAIE ,& TACTL InfLoop : bis .w # LPM0 | GIE , SR ; LPM0 with interrupts ( need SMCLK ) jmp InfLoop ; Infinite loop ; ----------------------------------------------------------------------; Interrupt service routine for TACCR1 . CCIFG , called when TAR -> TACCR1 ; and for TAIFG , called when TAR -> 0; share vector ; Interrupt flag cleared a u t o m a t i c a l l y with access to TAIV ; ----------------------------------------------------------------------TIMERA1_ISR : ; ISR for TACCR1 CCIFG and TAIFG add .w & TAIV , PC ; Add offset to PC for jump table jmp EndTA1_ISR ; Vector 0: No interrupt pending jmp CCIFG1_ISR ; Vector 2: CCIFG1 jmp FalseInterrupt ; Vector 4: Not possible ( CCIFG2 ) jmp FalseInterrupt ; Vector 6: Not possible ( CCIFG3 ) jmp FalseInterrupt ; Vector 8: Not possible ( CCIFG4 ) ; jmp TAIFG_ISR ; Vector A : TAIFG , last value possible TAIFG_ISR : ; Start of PWM cycle bis .b # BIT0 ,& P1OUT ; Light LED jmp EndTA1_ISR CCIFG1_ISR : ; End of duty cycle bic .b # BIT0 ,& P1OUT ; Extinguish LED jmp EndTA1_ISR FalseInterrupt : jmp $ ; Disaster . Loop here forever EndTA1_ISR : reti
There are six possible values in TAIV from 0 to 0x000A and I provide a jump for all of them except the last. Three should never occur with Timer_A2 but I trap them in an infinite loop in case I do something stupid while playing around with the debugger. The entry for a 0 value, jmp EndTA1_ISR, could be replaced by reti but I prefer functions to have single entry and exit points; admittedly it is just fussy in this trivial example. No jump is required for the highest value, which corresponds to TAIFG here, so there is a commented line as a reminder. There is no need to clear any of the interrupt flags because it is all done automatically when TAIV is read. Example 8.6 Try Listing 8.4 in the debugger. The program should run happily without modification but you may need to adapt it for another demonstration board. Now try the interesting aspects of debugging. Open a Register window for Timer_A2 and set a breakpoint on the line add.w &TAIV,PC in the ISR. The debugger now shows the value in TAIV on entry to the ISR, which alternates between 0x0002 and 0x000A. Unfortunately nothing else happens in the ISR and the LED never changes. Why not?
www.newnespress.com
Timers
299
Take out this breakpoint and set breakpoints on the lines with bis.b and bic.b instead. What happens now? Listing 8.5 shows the corresponding ISR in C. It uses a switch as you might expect. A standard switch with a few cases would normally be compiled into a sequence of comparisons but the intrinsic function _ _even_in_range(TAIV, 10) tells the compiler that TAIV can take only even values up to 10 and encourages it to implement a more efficient jump table instead. A possible disadvantage is that no checks are made that the value is either even or within the range but this should not be an issue with hardware. I use symbolic constants from the header file instead of the numerical values for the cases and could write TAIV_TAIFG instead of 10 in the switch itself.
Listing 8.5: Interrupt service routine from tmrints2.c to show use of TAIV in C.
# pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // ISR for TACCR1 CCIFG and TAIFG { // switch ( TAIV ) { // Standard switch switch ( _ _ e v e n _ i n _ r a n g e ( TAIV , 10)) { // Faster intrinsic fn case 0: // No interrupt pending break ; // No action case TAIV_CCIFG1 : // Vector 2: CCIFG1 P1OUT_bit . P1OUT_0 = 0; // End of duty cycle : Turn off LED break ; case TAIV_TAIFG : // Vector A : TAIFG , last value possible P1OUT_bit . P1OUT_0 = 1; // Start of PWM cycle : Turn on LED break ; default : // Should not be possible for (;;) { // Disaster . Loop here forever } } }
Example 8.7 Look at Listing 8.5 in the debugger. The disassembly code should look like a slightly more economical version of Listing 8.4. Try setting breakpoints or single-stepping through the ISR. You may find that debugging in C is even more confusing. There is no need to do any decoding if only one source for TIMERA1_VECTOR is active. On the other hand, you must remember to acknowledge the interrupt yourself, either by clearing the flag or by reading TAIV. The interrupt is reasserted immediately if you forget, which it is easy to do.
www.newnespress.com
300
Chapter 8
Here is another mistake to avoid:
if ( TAIV == 2) { // WRONG way to decode shared interrupt vector // Actions for channel 1 } else if ( TAIV == 10) { // Actions for TAIFG }
The problem is that TAIV is reset after the first comparison and does not have the same value in the second test. If you are lucky the compiler will save you by taking a copy of TAIV. Use a switch instead. The interrupt for TACCR0 is faster to service because it has a unique vector and decoding is not required. It also has higher priority and should therefore be chosen for the most urgent task in the Continuous mode. (Channel 0 has a special role in the Up and Up/Down modes so there is no choice there.)
Checklist--Have You . . .
Designed your system so that Timer_A can drive outputs directly for precise timing? Routed pins to Timer_A if you want it to handle external signals directly? Cleared the interrupt flag, either explicitly or by reading TAIV (except for channel 0)?
8.4
Measurement in the Capture Mode
The Capture mode is used to take a time stamp of an event: to note the time at which it occurred. A measurement typically requires two or more captures, although a trivial exception to this rule is given in an example. The timer can be used in two opposite ways illustrated in Figure 8.7: (a) In most cases the timer clock is either ACLK or SMCLK, whose frequency is known, and the unknown signal is applied to the capture input. To measure the length of a single pulse, we should capture both edges and subtract the captured times. This gives the duration of the pulse in units of the timer clock's period. For a periodic signal we might capture only the rising edges (or falling if preferred) and the difference gives the period directly. The period of the timer clock should be much less than the duration of the signal to give good resolution, not as in Figure 8.7.
www.newnespress.com
Timers
301
(b) The opposite approach is used to measure a signal with a high frequency. The signal is used as the timer clock and the captured events are typically edges of ACLK, whose frequency is known. The difference between that and the captured value gives the number of cycles of the signal in one cycle of ACLK. This gives the frequency rather than the period. The first method is much more common. The timer usually runs in the Continuous mode for captures because this makes it easy to calculate differences of times when TAR has rolled over between them. It is not too difficult to handle this in the Up mode but the Up/Down is definitely tricky. Here are a few examples of the use of the Capture mode: Many speed sensors produce a given number of pulses per revolution of a shaft--often just one for a bicycle wheel but many more in machinery. The signal is usually slow by electronic standards and the Capture mode is used to measure the period between pulses to determine the speed. Rotary knobs on the front panel of equipment can be read in the same way. Some sensors encode their outputs as a frequency, length of a pulse, or the duty cycle of a square wave, the fraction of the time during which the signal is high. The
(a) Measurement of a signal's duration or period by counting cycles of a known clock SMCLK or ACLK CCI duration period
(b) Measurement of a signal's frequency by counting cycles in a known time TACLK CCI from ACLK sampling time
Figure 8.7: Two ways in which the Capture mode is used to time a signal. (a) The duration or period of the signal CCI is measured by counting the number of cycles of a known clock. (b) The frequency of the external signal TACLK is measured by using it as the clock and counting the number of cycles during a known interval.
www.newnespress.com
302
Chapter 8 reason is that only one wire is needed to transmit the output and that time is easy to measure accurately. For example, the Maxim MAX66756677 are sensors whose output is a frequency, period, or delay proportional to temperature.
The delay between transmission and reception of an ultrasonic pulse is measured in the range finder described in application note Ultrasonic Distance Measurement with the MSP430 (slaa136). In communications, the Capture mode is used to detect and time stamp the start of data received, which typically begins with a falling edge on the input. Some forms of digital communication encode 0 and 1 as pulses of different length, sometimes with further lengths to denote the start and end of a packet. The Capture mode can decode these signals.
A frequency-locked loop can be emulated by using the Capture mode to compare the frequencies of SMCLK and ACLK. Many MSP430 devices contain an analog comparator, which can be used to detect when an external signal passes through particular levels. The output of the comparator can be routed internally to a Capture channel and the combination acts as an analog-to-digital converter. This is explained in the section "Comparator_A" on page 371. It is also possible to use the Schmitt trigger inputs as less precise comparators.
Example 8.8 A simple example that requires only single captures is to use Timer_A as a random number generator. Try this for simplified "dice" with four values on the Olimex 1121STK. One of the buttons, B2, is conveniently connected to capture input CCI1A and you can display 2 bits of the captured value in TACCR1 on the LEDs. Ignore bounce. A fancier version with the usual range of 16 (or more) and a better display could be built on the TI MSP430FG4618/F2013 Experimenter's Board.
8.4.1
Measurement of Time: Press and Release of a Button
As a simple example, let us measure the time between each press or release of button S2 on the TI MSP430FG4618/F2013 Experimenter's Board. (I chose this button because the board does a somersault if you press S1 too forcefully.) Listing 8.6 shows a simple
www.newnespress.com
Timers
303
program. Button S2 is connected to P1.1, active low with a pull-up resistor. It can be routed to compare input CCI0B so we must use capture/compare channel 0. This makes programming easy because of its dedicated interrupt vector but its higher priority and faster processing are wasted on such a slow signal and you would usually choose another channel. I put all the code for the display into a separate file LCDutils.c with prototypes in LCDutils.h. There are a few extra functions whose purpose should be obvious from their names. Look at the Web site for the details. Listing 8.6: Program press1.c to display the time between presses and releases of button S2 on the TI MSP430FG4618/F2013 Experimenter's Board. The configuration of ports P2P10 is not shown.
// press1 . c - Time interval between events on S2 = CCI0B using Timer_A // TI Experimenter 's Board with F4619 , default clocks // J H Davies , 2007 -06 -05; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430xG46x .h > // Specific device # include < intrinsics .h > // Intrinsic functions # include < stdint .h > // Integers of defined sizes # include " LCDutils . h " // SBLCDA4 utility functions void PortsInit ( void ); // Set up input - output ports // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer FLL_CTL0 = XCAP14PF ; // Configure load caps (14 pF ) PortsInit (); // Initialize ports LCDInit (); // Initialize SBLCDA4 DisplayLine (); // Display - - - - - - - on LCD // Capture either edge of CCI0B , synchronized , interrupts enabled TACCTL0 = CM_3 | CCIS_1 | SCS | CAP | CCIE ; // Start timer : ACLK , no prescale , continuous mode , no ints , clear TACTL = TASSEL_1 | ID_0 | MC_2 | TACLR ; for (;;) { // Loop forever with interrupts _ _ l o w _ p o w e r _ m o d e _ 3 (); // Only ACLK continues to run } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG , called on capture // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { static uint16_t LastTime = 0; // Last time captured DisplayUint ( TACCR0 - LastTime ); LastTime = TACCR0 ; // Display interval ( counts ) // Save time for next capture
} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Initialize ports for board : outputs driven low by default // Many of these will be overwritten by LCD initialization later // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
www.newnespress.com
304
Chapter 8
void PortsInit ( void ) { // Port 1: 0 = SW1 , 1 = SW2 , others used for Chipcon ( not placed ) P1OUT = 0; P1DIR = 0 xFF & ~ ( BIT0 | BIT1 ); // P1 .0 ,1 input , others output P1SEL = BIT1 ; // P1 .1 = S2 to Timer_A CCI0B
Timer_A runs in the Continuous mode with ACLK. Why did I choose this clock? Look back at Table 8.1 to see the most straightforward options. I wanted to get as fine a resolution as possible to detect switch bounce. This requires a fast clock. On the other hand, the period of the timer should preferably be longer than a reasonable press of the button, which is around 0.5 s. Thus ACLK without division looked like the best choice because it gives a period of 2 s, although SMCLK/8 might also work for users with nimble fingers. Another point is that ACLK is always accurate if it comes from a crystal but SMCLK is less accurate unless a frequency-locked loop is used. This is not an issue in the MSP430x4xx family. Capture/compare channel 0 is configured to capture and request interrupts on both rising and falling edges, corresponding to releases and presses of the button. The input is selected from CCI0B and captures are synchronized to the timer clock by setting SCS. Remember that configuring TACCTL0 is not sufficient by itself to connect the pin to CCI0B. You must also redirect the pin from digital input/output to Timer_A by setting the corresponding bit in P1SEL. All the action takes place in the ISR for TACCR0 CCIFG, which is straightforward. The time since the last edge is computed by subtracting the previous captured time from the latest value in TACCR0. The difference is then displayed on the LCD. Finally, the latest value is saved in a static variable LastTime to be ready for the next capture. Recall that this must be declared static so that it retains its value between calls of the function. If you forget this, you get a new variable at each interrupt and the memory of the last time is lost. An obvious concern is, What happens if TAR rolls over and restarts from 0 during the measurement? Then we would have TACCR0 < LastTime and it looks as though TACCR0 - LastTime will be negative. In fact this is no problem at all--declare the variables as unsigned integers and the difficulty takes care of itself. To illustrate this, suppose that the counter has 4 bits, LastTime = 0b0111 and TACCR0 = 0b0010. Binary, 4-bit subtraction gives 0010 - 0111 = 1011 and this sets the borrow bit to show that 1 needs to be borrowed from the next more significant column (which does not exist). Effectively the sum is 10010 - 00111, which is just what we want. Take a look in Maxfield and Brown's book [37] if you need any help with binary arithmetic.
www.newnespress.com
Timers Example 8.9
305
Rewrite the program so that it detects only the time spent down after the button is pressed, not the time spent up as well. A more serious issue arises if more than 216 cycles are counted, which exceeds the range of TAR. This can be resolved by counting the number of overflows by using interrupts on TAIFG. It is easier to choose the timer clock so that the number of pulses stays below 216 but this can be done only if the input is known to have a maximum duration. Humans are not so cooperative. Example 8.10 Extend the program to measure longer durations by counting the number of times that TAR rolls over. This part is not difficult but the routines to display values on the LCD need to be expanded for larger integers, which is harder. You may wish to look at the functions provided in LCDutils.c. It is worth a brief review of the advantages of capturing the input in hardware. A capture/compare channel copies the value of TAR into TACCRn as soon as a transition is detected on the input. The only delays are the inevitable propagation delay in the electronics and synchronization with the timer clock (which can be deselected, although this should be done with care). Subsequent processing, such as the DisplayUint() function in the interrupt service routine, does not upset the value stored. Even the interrupt latency has no effect, nor the time required to wake the processor if it were in a low-power state. These delay the processing but the captured value itself is accurate. Suppose that we could not use a capture/compare channel to record the time of the transition. The alternative would be to request an interrupt when the input changes and record the value of TAR during the ISR. This obviously suffers from the interrupt latency and the time to restart MCLK from a low-power mode. A further delay occurs if another interrupt is being serviced at the time and we have to wait for it to complete. The capture/compare hardware of Timer_A avoids all these problems. Example 8.11 Suppose that the designers of the MSP430FG4618/F2013 Experimenter's Board had been less cooperative and that the push buttons were not connected directly to capture inputs of
www.newnespress.com
306
Chapter 8
Timer_A. In this case the captures would have to be mediated by software. This could be done by setting the CCIS1 bit in TACCTL0, which connects the capture input to either VCC or ground, depending on the state of CCIS0. Set up port 1 to generate interrupts when S2 is pressed or released and copy its new state to TACCTL0.CCIS0 for capture. The rest of the program needs few changes. Measure only the time spent down.
8.4.2
Measurement of Time: Reaction Timer
Now for a slightly more complicated example: a reaction timer on the TI MSP430FG4618/F2013 Experimenter's Board. Here are instructions for the user: 1. Press and release button S2 to start a new trial. 2. LED4 lights after a random delay between 1 and 2s. 3. Press button S1 as soon as you see LED4 light. The LED goes out and your reaction time in milliseconds is displayed on the LCD. We use Timer_A to record the reaction time and the first question, as usual, is which clock to use. The time is displayed in milliseconds so a 1 KHz clock is most convenient. Unfortunately the slowest internal clock is ACLK and the maximum divider is 8, which gives a minimum frequency of 4 KHz. It will prove to be convenient to use the undivided ACLK. The only disadvantage is that this limits the slowest reaction that can easily be timed to 2s, which is not a problem in most cases. This is how the FG4618 is configured: Interrupts are enabled for the start button S2, connected to P1.1. The timing button S1, connected to P1.0, is routed to capture input CCI0A of Timer_A. Capture/compare channel 0 is used for the captures because S1 provides the input CCI0A.
Timer_A runs from ACLK with no division, giving a period of 2s. We now look at the basic actions needed and the different ways in which they can be implemented.
Wait for Button S2 to Be Pressed
A new trial starts when S2 is pressed, which generates a falling edge on the input (active low). In principle we could poll the input but that would be a ridiculous waste of power
www.newnespress.com
Timers
307
and an interrupt is the correct approach. The ISR disables further interrupts from this source, which conveniently means that there are no problems from any subsequent transitions caused by switch bounce. There is no need to detect the rising edge when S2 is released. The only real decision is the selection of low-power mode while waiting for the interrupt: LPM3 or LPM4? I use LPM3 for simplicity because this leaves ACLK running for the LCD. The display has to be shut down if LPM4 is used. Probably the best solution would be to stay in LPM3 for a reasonable time, until the user appears to have lost interest, before entering LPM4. Remember to reactivate the LCD_A module after wakening from LPM4. (An alternative approach would be to reset the MSP430 by writing an illegal value to the watchdog control register WDTCTL.)
Random Delay before Starting the Reaction Timer
There should be a random delay after pressing S2 before the LED lights to test the reaction time so that the user cannot predict when to press S1. I experimented and found that a delay of between 1 and 2s seem about right. It is awkward if the delay is too short and boring if it goes on for too long. The simplest way of getting a random delay is to use a free-running timer with a period of 2s, the maximum duration of the random delay. By good fortune this is just what Timer_A does with an undivided ACLK. Button S2 can be pressed at any time during the period of the timer, so the delay until the next overflow is random between zero and the full period. This is not quite what we want, though, because short delays are undesirable. These can be avoided by testing the value in TAR. If it is in the upper half of its range, which means that the timer overflows in less than 1s, a value of 0x8000 is subtracted from TAR to extend the delay by 1s. This does not destroy the randomness. I stop the timer while TAR is tested and adjusted to avoid possible problems. The interrupt on overflow (TAIFG) is enabled to trigger the next step.
Start the Reaction Timer
Timer_A generates a TAIFG interrupt when it overflows after the random delay. We now need to turn on the LED to start the measurement and record the starting time. However, the second step is unnecessary because TAR has just overflowed so the starting time is 0. Easy! Ideally the LED should be lighted precisely when TAIFG is set, which means that it should be done in hardware. This could in principle be done by driving the LED from Timer_A
www.newnespress.com
308
Chapter 8
and using a Compare event but no LEDs are connected in this way on the MSP430FG4618/F2013 Experimenter's Board. In any case, a few microseconds are insignificant on the human timescale. Instead, the LED is turned on during the ISR. Further interrupts from TAIFG are disabled and those from TACCR0 are enabled instead to be ready for the user to press the button.
Measure, Compute, and Display the Reaction Time
A capture occurs when the user presses S1 and the reaction time in counts of ACLK is given simply by the value in TACCR0 because the measurement started at 0. The ISR also turns the LED off and enables interrupts on the start button S2 so that the system is ready for the next trial. Counts of ACLK are not meaningful to most users so the time is rescaled to milliseconds. The formula is time (ms) = 1000 time (counts) fACLK (8.1)
with fACLK = 32 KHz = 32,768 Hz. It is tempting to "simplify" the numbers to give time (ms) = time (counts) 32,768 (8.2)
but this requires floating-point division, which is very slow indeed. I therefore do the calculation more economically in two steps. The first is multiplication by 1000, which needs a 32-bit variable to hold the result. The second step is division by 32 K = 215 . Do not try to do the operations in the opposite order because the division would destroy the data. The division implicitly rounds the value down, which is the most favorable outcome for the contestant. I wrote the division as a shift because I expected this to be more efficient. It was a waste of time because the compiler implements it as a single left-shift instead, which is a much better way. It did the same when I wrote the division instead. There is no point in doing trivial optimizations yourself with modern compilers.
How Should the Program Be Organized?
Clearly the action needs to be triggered by interrupts, but does this mean that the code should all be placed in interrupt service routines? Listing 8.7 shows the program written in this way, with the main loop reduced to putting the processor into a low-power mode.
www.newnespress.com
Timers
309
Listing 8.7: Program react1.c based on interrupt service routines to display the user's reaction time on the TI MSP430FG4618/F2013 Experimenter's Board. The configuration of ports P2P10 is not shown.
// react1 . c - Reaction timer using Timer_A , display in ms // Press S2 , wait for LED4 , press S1 as quickly as possible // Timer_A used for random delay and reaction time // Actions take place in ISRs // TI Experimenter 's Board with F4619 , default clocks // J H Davies , 2007 -06 -11; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430xG46x .h > // Specific device # include < intrinsics .h > // Intrinsic functions # include < stdint .h > // Integers of defined sizes # include " LCDutils . h " // SBLCDA4 utility functions # define LED P5OUT_bit . P5OUT_1 // Pin for LED void PortsInit ( void ); // Set up input - output ports // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer FLL_CTL0 = XCAP14PF ; // Configure load caps (14 pF ) PortsInit (); // Initialize ports LCDInit (); // Initialize SBLCDA4 DisplayHello (); // Display HELLO on LCD // Capture falling edge of CCI0A , synchronized , no ints yet TACCTL0 = CM_2 | CCIS_0 | SCS | CAP ; // Start timer : ACLK , no prescale , continuous mode , no ints yet , clear TACTL = TASSEL_1 | ID_0 | MC_2 | TACLR ; P1IFG = 0; // Clear any pending interrupts P1IE = BIT1 ; // Enable interrupts on P1 .1 = B2 for (;;) { // Loop forever with interrupts _ _ l o w _ p o w e r _ m o d e _ 3 (); // Only ACLK continues to run } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for port 1; need to clear flag // Enable TAIFG interrupts to give 1 -2 s delay before reaction test // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = PORT1_VECTOR // Flag NOT cleared a u t o m a t i c a l l y _ _interrupt void PORT1_ISR ( void ) { P1IFG = 0; // Acknowledge interrupt P1IE = 0; // Disable further P1 interrupts TACTL_bit . TAMC = 0; // Hold timer if ( TAR > 0 x8000 ) { // Will delay be < 1 s ? TAR -= 0 x8000 ; // Yes : add 1 s so 1 s < delay < 2 s } TACTL_bit . TAIFG = 0; // Clear any pending interrupt TACTL |= MC_2 | TAIE ; // Restart timer with interrupts } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TAIFG , called after random interval // Check source to clear flag and " service " erroneous interrupts // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
www.newnespress.com
310
Chapter 8
# pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y { if ( TAIV == TAIV_TAIFG ) { // Vector 10: TAIFG LED = 1; // Turn on LED to start trial TACTL_bit . TAIE = 0; // Disable further TAIFG interrupts TACCTL0_bit . CCIFG = 0; // Clear any pending interrupt TACCTL0_bit . CCIE = 1; // Enable TACCR0 capture interrupts } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG , called on capture // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { uint32_t ReactionTime ; // Computed reaction time , 32 bits TACCTL0_bit . CCIE = 0; // Disable further CCIFG0 interrupts ReactionTime = TACCR0 ; // Starting time was 0 counts ReactionTime *= 1000; // Convert seconds to ms ReactionTime > >= 15; // Economical /= 32 K ( f_ACLK ) DisplayUint (( uint16_t ) ReactionTime ); // Truncate and display LED = 0; // Turn off LED P1IFG = 0; // Clear any pending interrupts P1IE = BIT1 ; // Reenable interrupts on P1 .1 = B2 } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Initialize ports for board : pins are outputs driven low by default // Many of these will be overwritten by LCD initialization later // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PortsInit ( void ) { // Port 1: 0 = SW1 , 1 = SW2 , others used for Chipcon ( not placed ) P1OUT = 0; P1DIR = 0 xFF & ~ ( BIT0 | BIT1 ); // P1 .0 ,1 input , others output P1SEL = BIT0 ; // P1 .0 = S1 to Timer_A CCI0A // Interrupts on falling edge P1IES = BIT1 ;
The alternative is to put the action in the main loop, in which case the interrupt service routines simply clear their flags where necessary and return the processor to active mode on exit. This is shown in Listing 8.8. Which style do you prefer? I normally prefer to put the actions in ISRs but here the overall strategy is much easier to follow when it is in the main loop. To be honest this is an untypical example because the interrupts occur in a fixed sequence rather than arising randomly, which is often the case. Listing 8.8: Program react2.c concentrated on the main loop to display the user's reaction time on the TI MSP430FG4618/F2013 Experimenter's Board. The configuration of the ports is not shown.
// react2 . c - Reaction timer using Timer_A , display in ms // Press S2 , wait for LED4 , press S1 as quickly as possible
www.newnespress.com
Timers
311
// Timer_A used for random delay and reaction time // Actions take place in main loop ; minimal ISRs return to active mode // TI Experimenter 's Board with F4619 , default clocks // J H Davies , 2007 -06 -11; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430xG46x .h > // Specific device # include < intrinsics .h > // Intrinsic functions # include < stdint .h > // Integers of defined sizes # include " LCDutils . h " // SBLCDA4 utility functions # define LED P5OUT_bit . P5OUT_1 // Pin for LED void PortsInit ( void ); // Set up input / output ports // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { uint32_t ReactionTime ; // Computed reaction time WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer FLL_CTL0 = XCAP14PF ; // Configure load caps (14 pF ) PortsInit (); // Initialize ports LCDInit (); // Initialize SBLCDA4 DisplayHello (); // Display HELLO on LCD // Capture falling edge of CCI0A , synchronized , no ints yet TACCTL0 = CM_2 | CCIS_0 | SCS | CAP ; // Start timer : ACLK , no prescale , continuous mode , no ints yet , clear TACTL = TASSEL_1 | ID_0 | MC_2 | TACLR ; for (;;) { // Loop forever with interrupts P1IFG = 0; // Clear any pending interrupts P1IE = BIT1 ; // Enable interrupts on P1 .1 = B2 _ _ l o w _ p o w e r _ m o d e _ 3 (); // Wait for button to be pressed P1IE = 0; // Disable further P1 interrupts TACTL_bit . TAMC = 0; // Hold timer if ( TAR > 0 x8000 ) { // Will delay be < 1 s ? TAR -= 0 x8000 ; // Yes : add 1 s so 1 s < delay < 2 s } // Clear any pending interrupt TACTL_bit . TAIFG = 0; TACTL |= MC_2 | TAIE ; // Restart timer with interrupts _ _ l o w _ p o w e r _ m o d e _ 3 (); // Wait for random delay LED = 1; // Turn on LED to start trial TACTL_bit . TAIE = 0; // Disable further TAIFG interrupts TACCTL0_bit . CCIFG = 0; // Clear any pending interrupt TACCTL0_bit . CCIE = 1; // Enable TACCR0 capture interrupts _ _ l o w _ p o w e r _ m o d e _ 3 (); // Wait for user to press button TACCTL0_bit . CCIE = 0; // Disable further interrupts ReactionTime = TACCR0 ; // Starting time was 0 counts ReactionTime *= 1000; // Convert seconds to ms ReactionTime /= 0 x8000 ; // Divide by f_ACLK DisplayUint (( uint16_t ) ReactionTime ); // Truncate and display LED = 0; // Turn off LED } // Finished - go and start again } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for port 1; need to clear flag // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = PORT1_VECTOR _ _interrupt void PORT1_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y {
www.newnespress.com
312
Chapter 8
P1IFG = 0; // Acknowledge interrupt _ _ l o w _ p o w e r _ m o d e _ o f f _ o n _ e x i t ();
} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TAIFG , called after random interval // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y { TACTL_bit . TAIFG = 0; // Acknowledge interrupt _ _ l o w _ p o w e r _ m o d e _ o f f _ o n _ e x i t (); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG , called on capture // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { _ _ l o w _ p o w e r _ m o d e _ o f f _ o n _ e x i t (); }
There is no safeguard in either version of this program against overflows of TAR if the user waits for more than 2s before pressing the button. This is not hard to add because it is signaled by an overflow of TAR. Of course there should be no overflow if users genuinely try to measure their reaction time. Mine was about 0.16s. Example 8.12 Extend either version of the program so that it stops the trial after 2s and displays "Error" on the LCD using the DisplayErr() function. Example 8.13 Develop your program further so that it enters LPM4 if there is no activity for some time, which can be monitored by counting overflows of TAR. Remember to disable the LCD before entering LPM4 and to restart it afterward (or reset the MSP430).
8.4.3
Measurement of Frequency: Comparison of SMCLK and ACLK
The general principle for measuring a frequency f is to count the number of cycles N in a known interval of time T , whence f = N/T . Look at Figure 8.7(b). If T = 1s then N gives the frequency in hertz directly. Alternatively, if T = 1 ms then N gives the frequency in kilohertz.
www.newnespress.com
Timers
313
The signal under test is used as the timer clock and an external signal should be applied to the TACLK pin. Remember to set the PnSEL.x bit to connect this pin to the timer and select TACLK or INCLK using the TASSELx bits. In the example that follows I use an internal signal, SMCLK. We need to count the number of cycles of the test signal in a known time. This requires an accurate reference, which is provided by ACLK and the usual watch crystal. An obvious way of counting cycles is to start the timer at the beginning of a cycle of ACLK and stop it after a fixed number of cycles of ACLK have passed. The problem is that this requires software, which introduces errors in the timing. A far better approach is to do the measurement entirely in hardware. This is straightforward because ACLK is connected internally to one of the capture inputs of Timer_A. The channel is configured to capture the value of TAR and request an interrupt at a rising edge of ACLK. This value N1 is stored during the ISR. The next rising edge of ACLK stimulates another capture and interrupt. The difference between the new and old values of TAR, N2 - N1 , gives the number of cycles of the test signal in one cycle of ACLK. Thus the measured frequency is f = (N2 - N1 )fACLK . The result is restricted to multiples of fACLK if only a single cycle of ACLK is used, which gives a rather coarse set of values. Finer resolution is obtained by counting for more cycles of ACLK. For example, we might use 32 cycles. Then f = (N2 - N1 )fACLK /32. Now fACLK = 32 KHz so f = (N2 - N1 ) KHz, which is convenient. Note the capital K because this is a binary kilo, meaning 210 = 1024, rather than the decimal kilo of 1000 as in KHz. There is no problem if TAR overflows once during the measurement, as discussed already, and an overflow counter can be added if more than 216 cycles need to be recorded. As an example I chose to use SMCLK as the "unknown" signal and display its frequency on the LCD of the TI MSP430FG4618/F2013 Experimenter's Board. The quotation marks are there because fSMCLK should be a precise multiple of fACLK in a MSP430x4xx due to the frequency-locked loop (FLL+). Turning this around, it gives us a chance to explore the operation of the FLL+ so you might want to look back at the section "Frequency-Locked Loop, FLL+" on page 172. Listing 8.9 shows the relevant parts of the program: Listing 8.9: Program clkcmp1.c to display the frequency of SMCLK on the LCD of a TI MSP430FG4618/F2013 Experimenter's Board.
// // // // clkcmp1 . c - Show ratio of SMCLK : ACLK on display using Timer_A Compared every 0.5 s , triggered by Basic Timer ; flashes LED as signal TI Experimenter 's Board with F4619 , fairly complete i n i t i a l i z a t i o n J H Davies , 2007 -06 -03; IAR Kickstart version 3.42 A
www.newnespress.com
314
Chapter 8
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430xG46x .h > // Specific device # include < intrinsics .h > // Intrinsic functions # include < stdint .h > // Integers of defined sizes # include " LCDutils . h " // SBLCDA4 utility functions // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PortsInit ( void ); void TimersInit ( void ); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { volatile uint16_t i ; // Counter for FLL + delay WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer // Clock default : MCLK = 32 x ACLK = 1 MHz , FLL + operating FLL_CTL0 = XCAP14PF ; // Configure load caps (14 pF ) do { IFG1_bit . OFIFG = 0; // Try to clear OSCFault flag for ( i = 50000; i > 0; --i ) { // Delay for flag to reset } // if not yet stable } while ( IFG1_bit . OFIFG != 0); // OSCFault flag still set ? PortsInit (); // Initialize ports LCDInit (); // Initialize SBLCDA4 TimersInit (); // Initialize Timer_A and BT for (;;) { // Loop forever with interrupts _ _ l o w _ p o w e r _ m o d e _ 0 (); // SMCLK and DCO continue to run } // and FLL + remains active } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Initialize Timer_A for capture from ACLK , BT for 0.5 s interrupts // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimersInit ( void ) { // Initialize basic timer : ACLK , cascaded , 0.5 s interrupts , stopped BTCTL = BTHOLD | BT_ADLY_500 ; BTCNT1 = BTCNT2 = 0; // Clear counters IE2_bit . BTIE = 1; // Enable basic timer interrupts BTCTL &= ~ BTHOLD ; // Start basic timer running // Capture on CCI2B , rising edge , sync ; do not yet enable interrupts TACCTL2 = CCIS_1 | CM_1 | SCS | CAP ; // Start timer : SMCLK , no prescale , continuous mode , no ints , clear TACTL = TASSEL_2 | ID_0 | MC_2 | TACLR ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for basic timer : start captures on CCI2B // Flag cleared a u t o m a t i c a l l y // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = B A S I C T I M E R _ V E C T O R _ _interrupt void B A S I C T I M E R _ I S R ( void ) { TACCTL2_bit . COV = 0; // Clear previous overruns TACCTL2_bit . CCIFG = 0; // Clear pending interrupt TACCTL2_bit . CCIE = 1; // Enable interrupts on capture } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for Timer_A , called on capture
www.newnespress.com
Timers
315
// Read TAIV to clear flag and " service " erroneous interrupts // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # define NCAPTURES 32 // Number of captures to accumulate # pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // ISR for CCIFGn and TAIFG { static uint8_t Captures = 0; // Number of captures accumulated static uint16_t StartTime ; // Time at start of sampling if ( TAIV == TAIV_CCIFG2 ) { // CCIFG2 vector (4) switch ( Captures ) { case 0: // Starting new sequence of captures StartTime = TACCR2 ; // Starting time Captures = NCAPTURES ; // Initialize down counter P5OUT_bit . P5OUT_1 = 1; // Light LED to mark start captures break ; case 1: // Final capture of sequence DisplayUint ( TACCR2 - StartTime ); // Display result Captures = 0; // Finished TACCTL2_bit . CCIE = 0; // Disable further interrupts P5OUT_bit . P5OUT_1 = 0; // Turn off LED to mark end captures break ; default : // Sequence of captures continues -- Captures ; break ; } } }
I have been less lazy than useful and configured the clocks better. A loop tests the fault flag OFIFG and allows the oscillators to stabilize. Thus the frequencies should be stable before the measurements start and the displayed value should be reliable. Basic Timer1 is configured for a real-time interrupt every 0.5s to stimulate the measurements. Capture/compare channel 2 is used for the measurements because ACLK is connected to CCI2B. It is configured for rising edges and synchronized captures.
The timer block takes its clock from SMCLK without division and runs in the Continuous mode. The ISR for Basic Timer1 enables interrupts on TACCR2 to start a new measurement. It first clears the COV and CCIFG bits, which are set by previous capture events that were ignored while interrupts were disabled. Most of the work takes place in the ISR for TIMERA1_VECTOR. Only one interrupt is enabled but I checked TAIV in case the program is later extended or if
www.newnespress.com
316
Chapter 8 something terrible goes wrong. The action depends on the value of the variable Captures, which is tested in a switch. -- Captures is 0 at the start of a new measurement. The captured value of TAR in TACCR2 is stored in StartTime and Captures is initialized to the number of captures required for the measurement, 32 here. I also lit an LED to show that a measurement is in progress. -- The value of Captures is decremented in subsequent captures; there is no need to store the value of TACCR2 again. -- The final capture is taken when Captures is 1. The difference between the most recent capture, in TACCR2, and the stored value gives the frequency in binary kilohertz as explained already. This is displayed as an unsigned integer on the LCD. Further interrupts are disabled and the LED is turned off to show that the measurement is finished.
The MSP430 returns to LPM0 until the next interrupt from Basic Timer1. I chose low-power mode 0 so that the DCO and FLL+ run continuously. Usually we put the device into LPM3 because only ACLK is needed until the next measurement. The problem with this is that the FLL+ is not automatically reengaged when the MSP430 enters active mode after an interrupt. The default configuration for MCLK in the FG4618 is that the FLL+ has a multiplier of 32, in which case fMCLK = 32fACLK = 1024 KHz (= 1,048,576 Hz, not that the frequency is this accurate). The display should therefore show 1024 when the program runs. However, it does not. I found 1376 typically, although there were fluctuations. The reason is straightforward: Only 32 cycles of MCLK are available to service each interrupt before the next capture occurs, which is not long enough. How many cycles are required? The FG4618 has the MSP430X CPU, which needs fewer clock cycles to service interrupts than the original MSP430. It takes five cycles to call the ISR and three to return from it, plus one to synchronize the capture with MCLK. This represents 9 cycles of overhead, leaving only 23. Typically the CPU must process the decisions and decrement Captures. The complete interrupt requires 42 clock cycles according to the debugger, too many. There are several possible solutions to this problem. The code could be streamlined, perhaps by removing the check on TAIV. Perhaps the best approach would be to divide ACLK so that captures occurred less frequently. This can be done in the MSP430x1xx and
www.newnespress.com
Timers
317
MSP430F2xx families but is not possible in the clock module for the MSP430x4xx (it can divide the external signal ACLK/n but not the internal clock). I doubled the frequency of the FLL+ instead by raising the multiplier in SCFQCTL from 31 to 63. The display then showed 2048 as expected. Example 8.14 Experiment with Listing 8.9 in the debugger. Does the unmodified version fail as advertised? Do you see the COV bit set to show that a capture has been missed? Double the frequency of the DCO and confirm that the program now works correctly. Try higher frequencies as well but remember that 8 MHz needs VCC = 3.6 V. Example 8.15 Use the debugger to disengage the frequency-locked loop by setting the SCG0 bit in the status register. The DCO now runs freely at its last setting. Resume the program. Has the frequency changed? Test the sensitivity to temperature by pressing your finger on the FG4618. Does the frequency change? Reengage the FLL+ and confirm that the frequency no longer changes if you put your finger on the chip again. Example 8.16 It seems a bit extravagant to use two timers for this measurement. Why not use TAIFG to start measurements instead of the basic timer? Try this. Use a switch on TAIV. Example 8.17 Save power by entering LPM3 between measurements. What low-power mode should be used between captures during a measurement?
A practical use of this specific example is to emulate a frequency-locked loop in software for devices that lack the hardware. Algorithms are suggested in the application note Controlling the DCO Frequency of the MSP430x11x (slaa074). It also shows how the 50 or 60 Hz supply can be used as a reference instead of ACLK.
www.newnespress.com
318
Chapter 8
8.5
Output in the Continuous Mode
In the Continuous mode, TAR counts from 0 up to 0xFFFF and returns to 0 on the next clock transition, setting TAIFG as it does so. The only control over the period of TAR in real time is through the choice of clock, which is so coarse that it will rarely give the desired value. Therefore the duration of output signals must almost always be controlled by software rather than the period of the timer. This contrasts with the Up and Up/Down modes, where outputs can usually be generated by hardware alone. The Continuous mode is typically used in the following circumstances: All channels are needed for output, including channel 0. Outputs must be driven at different, unrelated frequencies. Single delays are required rather than periodic signals. Some channels are used for capture and some for compare events.
The Continuous mode is convenient when times have to be calculated because the 16-bit range of TAR matches the size of a simple unsigned integer variable, which means that there is no need to worry about overflows: The arithmetic and the counter overflow in the same way. This is the same as in the Capture mode. A fundamental limitation of Continuous mode is that the time for the next Compare event must be updated in software, usually during an interrupt service routine. Intervals therefore cannot be too short or there is insufficient time for the next event to be set up. This is the same issue that hampered frequent captures in the section "Measurement of Frequency: Comparison of SMCLK and ACLK" on page 312. The application note Implementing IrDA with the MSP430 (slaa202) shows how both TACCR1 and TACCR0 can be used to control OUT1 in the Continuous mode. This is tricky. There are several standard applications of the Continuous mode to digital communications and I describe an example in the section "A Software UART Using Timer_A" on page 590. Meanwhile, here are some more straightforward illustrations of its use.
8.5.1
Generation of Independent, Periodic Signals
An example that uses the Continuous mode to produce several independent, period signals is shown in Listing 8.10. It runs on the Olimex 1121STK, which has LEDs that can be
www.newnespress.com
Timers connected to outputs TA1 and TA2 of Timer_A3. There are four outputs with different frequencies, one from each channel and one from TAR itself:
319
LED1 is driven from TA1 and toggled every 0.25s so that it flashes with a 50% duty cycle (0.25s on, 0.25s off). The overall period is 0.5s. LED2 is driven from TA2 at roughly 100 Hz with a 10% duty cycle. The eye cannot resolve the flashes and the LED simply appears dim.
The piezo sounder is driven at nearly 440 Hz, the frequency of the standard note A used for tuning western music. It is not connected directly to Timer_A so its outputs are toggled in the interrupt service routine for channel 0. The sounder is toggled on and off every 2s by enabling and disabling its interrupts in the ISR for TAIFG. Listing 8.10: Program contall1.c to illustrate periodic outputs from Timer_A in the Continuous mode.
// contall1 . c - All channels in operation using Timer_A continuous mode // LED1 = TA1 in toggle mode , 50% duty cycle , period 0.5 s // LED2 = TA2 using set and reset modes , 10% duty , period roughly 10 ms // Piezo toggled by TA0 interrupts at close to 440 Hz ( standard 'A ') , // turned on and off every 2 s using TAIFG interrupts // Olimex 1121 STK , LED1 on P2 .3 = TA1 , LED2 on P2 .4 = TA2 , active low ; // piezo between P2 .0 and P2 .5 , Timer_A from ACLK = 32 KHz // J H Davies , 2007 -06 -19; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430x11x1 .h > // Specific device # include < intrinsics .h > // Intrinsic functions // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Delays in cycles of timer clock = ACLK = 32 KHz = 0 x8000Hz # define LED1half 0 x2000 // (1/4) s half - period for toggling # define LED2duty 30 // Duration of " on " duty cycle # define LED2period 300 // Total period # define PIEZhalf 37 // Toggle piezo to give f = 440 Hz nearly // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer // Channel 0 of timer , interrupts only to drive piezo TACCR0 = PIEZhalf ; // " Compare " value for first interrupt TACCTL0 = CCIE ; // Enable interrupts only ( no output ) // Channel 1 of timer , drive LED1 ( P2 .3) in toggle mode with interrupts TACCR1 = LED1half ; // Compare value for first interrupt TACCTL1 = OUTMOD_4 | CCIE ; // Toggle mode with interrupts // Channel 2 of timer , drive LED2 ( P2 .4) with set , reset and interrupts TACCR2 = LED2duty ; // Compare value for first interrupt TACCTL2 = OUTMOD_1 | CCIE ; // " Set " to turn LED off at next match // Configure ports 1 and 2; redirect P2 .3 ,4 to Timer_A
www.newnespress.com
320
Chapter 8
P1OUT = BIT0 | BIT1 ; // Output , high for Freq pin and TXD P1DIR = BIT0 | BIT1 ; P2SEL = BIT3 | BIT4 ; // Re - route P2 .3 to TA1 , P2 .4 to TA2 P2OUT = BIT0 | BIT3 | BIT4 ; // LEDs off ( active low ); prepare piezo P2DIR = BIT0 | BIT3 | BIT4 | BIT5 ; // Piezo and LED outputs // Start timer from ACLK , Continuous mode , clear , TAIFG interrupts TACTL = TASSEL_1 | ID_0 | MC_2 | TACLR | TAIE ; for (;;) { // Loop forever _ _ l o w _ p o w e r _ m o d e _ 3 (); // LPM3 , all action in interrupts } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG , called on compare // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R \ _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared automatically { P2OUT ^= ( BIT0 | BIT5 ); // Toggle piezo sounder TACCR0 += PIEZhalf ; // Calculate next compare value } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCRn . CCIFG ( n > 0) and TAIFG // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # vector pragma = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // ISR for TACCRn CCIFG and TAIFG { switch ( _ _ e v e n _ i n _ r a n g e ( TAIV , 10)) { // Acknowledges interrupt case 0: // No interrupt pending break ; // No action case TAIV_CCIFG1 : // Vector 2: CCIFG1 TACCR1 += LED1half ; // Calculate next compare value break ; // ( LED1 toggled a u t o m a t i c a l l y ) case TAIV_CCIFG2 : // Vector 4: CCIFG2 if ( TACCTL2 & OUTMOD_4 ) { // Was last action Reset ( on )? TACCR2 += LED2duty ; // Duration of On part of cycle TACCTL2 = OUTMOD_1 | CCIE ; // " Set " turns LED off next } else { // Last action was Set ( off ) TACCR2 += ( LED2period - LED2duty ); // Duration of Off part TACCTL2 = OUTMOD_5 | CCIE ; // " Reset " turns LED on next } break ; case TAIV_TAIFG : // Vector A : TAIFG TACCTL0_bit . CCIE ^= 1; // Toggle CCIFG0 ints ( piezo on / off ) break ; default : // Should not be possible for (;;) { // Disaster ! Loop here for ever } } }
The first job is, as always, to choose an appropriate clock for Timer_A. All the outputs are slow, which indicates ACLK. It could be divided, which would save a little power, but this would spoil the accuracy of the A note. If this were not important it would make sense to use ACLK/8.
www.newnespress.com
Timers
321
Next we need to work out the durations of the output signals. LED1 should be toggled every 0.25s. There are 32 K = 0x8000 cycles of ACLK per second so the interval is 0x2000 counts. Next, the frequency for LED2 should be around 100 Hz, which is a period of around 328 counts. I rounded this to 300 for convenience. The LED should be lit for 10% of the time or 30 counts. Finally, we would like a note of 440 Hz from the piezo sounder, which means that it should be toggled at 880 Hz. This is 37.24 counts, which I round to 37. The note therefore is a little sharp at 443 Hz but I certainly will not notice the error. (The next highest note is A at 466 Hz.) We see how to do better in the section "Generation of a Precise Frequency" on page 326. After stopping the watchdog, the channels of the timer are configured. All are done differently--this example was devised to show most of the likely modes. In all cases, the time at which the first event should occur is loaded into the capture/compare register TACCRn: Channel 0 produces only interrupts because the piezo sounder cannot be driven directly. Channel 1 is set up to toggle the output because it is on and off for equal times. Channel 2 is more complicated because the LED is on and off for different times. The first event is configured as a Set, which turns LED2 off because it is active low.
The ports are configured next. Do not forget to use P2SEL to connect the external pins to the outputs of Timer_A. The timer itself is started last of all and the main routine enters LPM3 so that all subsequent action takes place in the interrupt service routines. Channel 0 is simple because the vector is not shared. The ISR toggles the bits for the piezo sounder to produce a square wave, as shown in Figure 4.9. The time of the next interrupt is computed by adding the delay PIEZhalf to the current value.
The other interrupts share a vector and are decoded using TAIV as usual. The output for channel 1 is toggled automatically by the hardware as soon as Timer_A detects the match between TAR and TACCR1: It happens perfectly on time without intervention from software. An interrupt is requested at the same time and the ISR needs only to update TACCR1 for the next change in output. These are equally spaced so we just add LED1half. It is more complicated to handle channel 2 because it is on and off for different times. The ISR first tests the OUTMOD2 bit to distinguish between Set (mode 1)
www.newnespress.com
322
Chapter 8 and Reset (mode 5). It is slightly confusing because there is no individual definition for this bit in the io430x11x1.h header file so I use the constant OUTMOD_4 instead, which has the same value. -- If this bit is set, the channel is in mode 5 and the event that triggered this interrupt was a Reset of the output, which turned on the LED. We therefore increase TACCR2 by the time that the LED spends on, given by LED2duty. The LED should be turned off at the end of this time so the channel is therefore reconfigured to Set (mode 1). -- The opposite happens if the bit is clear. The output has just been Set to turn off the LED and this should last for the difference between the total period and the duty time. The LED should be turned on at the next event, which requires Reset (mode 5).
Finally, TAIFG is easy to handle: It simply toggles the bit that enables interrupts on channel 0 to turn the piezo sounder on and off. There is silence if the outputs are not toggled; we could instead disable the outputs. There is a subtle point about the value in TACCR0. This will fall out of synchronization with TAR if interrupts stop and the first Compare event might therefore be incorrect when interrupts resume. Fortunately it is not a problem here because we both stop and restart the interrupts when TAR returns to 0.
There is an important distinction between channels 1 and 2 on the one hand and channel 0 and TAIFG on the other. The outputs TA1 and TA2 are driven directly and therefore change at precise times regulated by the hardware of Timer_A: There is no delay while MCLK is restarted and the ISR is called. This is the same feature as for capture events. The processing of the interrupt takes place a little later but this has no effect on the timing provided that the time of the next compare event in TACCRn has been updated before it arrives. Thus the delay between two events cannot be too short. In contrast, the piezo sounder is driven from software and its timing is therefore not precise. The same applies to any signals controlled by TAIFG.
Example 8.18 Try this in the debugger. Single-stepping is instructive. You will find that the LEDs turn on after the lines with P2SEL and P2DIR have been executed. Setting P2OUT has no effect because the pins have been redirected to Timer_A and the initial state of the output
www.newnespress.com
Timers
323
units is low. You will also find that LED1 flashes slowly even when the program is stopped but the other outputs halt. Why is this? Example 8.19 Increase the duty cycle of the PWM on TA2 by 10% every time TAR overflows. Start again from 10% after 90% because special treatment is needed to get a duty cycle close to 0 or 100% in the Continuous mode. (You might wish to disable the piezo sounder if it drives you mad.)
8.5.2
A Single Pulse or Delay with a Precise Duration
It is often necessary to change an output once after a precise delay, to trigger another module such as the analog-to-digital converter or to request an interrupt. The Continuous mode is most convenient for this. Listing 8.11 shows a rather daft application: a doorbell that lights an LED for precisely 0.5s on the Olimex 1121STK. I chose this illustration because you can easily see what is happening. Usually the delay would be much shorter and you would need an oscilloscope rather than your eyes. (The "bell" is only an LED because I could not tolerate the noise from the piezo sounder any more.) More precisely, this is the sequence of events: 1. The processor waits in LPM4 until wakened by a falling edge when the button is pressed. Timer_A is stopped during the wait (in any case it would not run because ACLK is disabled). 2. Channel 1 of Timer_A is set up for a compare event after 10 ms and the timer is restarted. 3. The input from the button is checked after the delay to confirm that the button is still down. This is a simple approach to debouncing. The processor returns to LPM4 if the button is now up, in which case the first "press" must have been noise. 4. If the press of the button was valid, Channel 1 is set up for another 10 ms delay. This time the output mode is changed to Reset so that the LED (active low) is lit when the Compare event occurs. 5. After the 10 ms event that lights the LED, Channel 1 is set up for a further 500 ms delay and the output mode is changed to Set so that the LED is turned off.
www.newnespress.com
324
Chapter 8
6. Timer_A is stopped after the final delay and the processor returns to LPM4 until the button is next pressed. The action takes place in the main loop for clarity and the processor enters LPM3 to save power while waiting for the timer. Listing 8.11: Program doorbel1.c to light an LED for precisely 0.5s after a button is pressed, using Timer_A in the Continuous mode.
// doorbel1 . c - accurate 0.5 s doorbell after debounced press // Channel 1 of Timer_A , Continuous mode , ACLK /8 = 4 KHz // Olimex 1121 STK , LED1 on P2 .3 = TA1 , B1 on P2 .1 , both active low // J H Davies , 2007 -06 -19; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430x11x1 .h > // Specific device , newer format # include < intrinsics .h > // Intrinsic functions // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Duration of delays in cycles of timer clock = ACLK /8 = 4 KHz # define DELAY10ms 40 // 10 ms delay for debounce and pause # define DELAY500ms 0 x0800 // Duration of buzz and dead time // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer // Channel 1 of timer , drives LED1 ( P2 .3 = TA1 ) , active low TACCTL1 = OUTMOD_0 | OUT ; // Force output initially high ( LED off ) // Timer from ACLK /8 , no need to clear , stopped TACTL = TASSEL_1 | ID_3 | MC_0 ; P1OUT = BIT0 | BIT1 ; // Output , high for Freq pin and TXD P1DIR = BIT0 | BIT1 ; P2SEL = BIT3 ; // Reroute P2 .3 to Timer TA1 P2OUT = BIT3 | BIT4 ; // LEDs off ( active low ) P2DIR = BIT0 | BIT3 | BIT4 | BIT5 ; // Piezo and LED outputs P2IES = BIT1 ; // Detect falling edge on P2 .1 = B1 for (;;) { // Loop forever P2IFG = 0; // Clear any pending interrupts on P2 P2IE = BIT1 ; // Enable interrupts on P2 .1 _ _ l o w _ p o w e r _ m o d e _ 4 (); // LPM4 until button pressed P2IE = 0; // Disable further interrupts on P2 // Request interrupt after 10 ms delay using TACCR1 TACCR1 = TAR + DELAY10ms ; // " Compare " value for 10 ms delay TACCTL1_bit . CCIE = 1; // Enable interrupts on compare TACTL |= MC_2 ; // Start timer , Continuous mode _ _ l o w _ p o w e r _ m o d e _ 3 (); // LPM3 until timer interrupts if ( P2IN_bit . P2IN_1 == 0) { // Button still down ? Continue if so // Drive output low ( reset ) after 10 ms delay using TACCR1 TACCR1 += DELAY10ms ; // Further 10 ms delay TACCTL1 = OUTMOD_5 | CCIE ; // " Reset " output to turn on LED _ _ l o w _ p o w e r _ m o d e _ 3 (); // LPM3 until timer interrupts // Drive output high ( set ) after 500 ms delay using TACCR1 TACCR1 += DELAY500ms ; // Further 500 ms delay TACCTL1 = OUTMOD_1 | CCIE ; // " Set " output to turn off LED
www.newnespress.com
Timers
325
_ _ l o w _ p o w e r _ m o d e _ 3 (); // LPM3 until timer interrupts } TACCTL1_bit . CCIE = 0; // Disable further interrupts on CCIFG1 TACTL &= ~ MC_3 ; // Stop timer } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for port 2; need to clear flag // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = PORT2_VECTOR _ _interrupt void PORT2_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y { P2IFG = 0; // Acknowledge interrupt _ _ l o w _ p o w e r _ m o d e _ o f f _ o n _ e x i t (); // Return to main routine } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for CCIFG1 ( only source active for vector ) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y { TACCTL1_bit . CCIFG = 0; // Acknowledge interrupt _ _ l o w _ p o w e r _ m o d e _ o f f _ o n _ e x i t (); }
An obvious question about this program is why there is a 10 ms delay after the state of the button has been confirmed, before the LED is illuminated. The reason is the requirement for a precise duration of 0.5s for the low pulse that drives the LED. This means that the LED must be turned on and off directly by the hardware of the timer, which requires a Compare event. It cannot be done from software in response to another event because the precision would be lost. Thus a dummy delay is needed before the first Compare event, for which the output mode is Reset to drive the pin low. It could be shorter than the 10 ms delay used for debouncing but I just use the same value for convenience. After the LED is illuminated, TACCR1 is increased by the value needed for a further 0.5s and the output mode is changed to Set so that the LED is turned off. Thus a precise flash is controlled directly by Timer_A. There are a couple of other points about the program that I should mention. The LED is connected active low and the pin should therefore be driven high as soon as it is configured as an output. This is ensured by setting the OUT bit of TACCTL1 in output mode 0 (which is the default) before the pin is rerouted to Timer_A and becomes the output TA1. The output mode is changed later to turn the LED on. The other detail is that we need to read TAR so that TACCR1 can be set up for the first delay. This can be hazardous if the timer is running because of a possible race between ACLK and MCLK, pointed out in the section "Timer Block" on page 289. Here we do not
www.newnespress.com
326
Chapter 8
need Timer_A while the program waits for the button to be pressed so I stop it. We can then read TAR at leisure before restarting it. Another way of taking a reliable reading of TAR is to read it repeatedly until two successive values agree. This needs ACLK slower than MCLK, of course. A simple dowhile loop for this is shown in Listing 8.12. It is essential that TAR is declared volatile in the header file (which it is) or the loop would be meaningless. Listing 8.12: Synchronization of LastTAR with TAR in doorbel2.c.
do { LastTAR = TAR ; } while ( LastTAR != TAR ); // Loop until two successive reads // of TAR agree with each other // Take care debugging this
Example 8.20 The program already has the convenient feature that it is triggered by edges on the input, so it responds only once if the button is pressed continuously. Add a further 0.5s delay after the LED has gone out, during which the doorbell does not respond to the button. This helps to reduce the nuisance from irritating callers who press the button repeatedly.
8.5.3
Generation of a Precise Frequency
One of the outputs of the program in the section "Generation of Independent, Periodic Signals" on page 318, which produced several independent frequencies, drove a piezo sounder at a frequency close to 440 Hz by toggling it at twice this frequency. I mentioned that this is the note used for tuning orchestras for western music, in which case we might like to do better than "close to" 440 Hz. In fact the frequency was 443 Hz, assuming that the crystal is accurate (I ignore this issue). This is an error of about +11 cents, where 100 cents is a semitone. (Without going into musical theory, a difference in frequency of 1% is about 17 cents and an octave is 1200 cents. Musical intervals depend on ratios of frequencies, which means that cents are defined in terms of logarithms.) My daughter plays the oboe, which sounds the note used to tune an orchestra. She tells me that 443 Hz is not good enough, and even a school orchestra is tuned to an accuracy of better than 5 cents. How can we generate a more precise frequency? The problem is that each interval generated by the timer must be an integer multiple of the period of its clock--there is no way of timing fractional cycles.
www.newnespress.com
Timers
327
An obvious method is to run the timer from a faster clock. Suppose that we used a frequency-locked loop to set fSMCLK = 32fACLK = 220 Hz, which is a binary megahertz. The number of counts for events at 880 Hz would then be 220 /880 = 1191.56. That is not a good choice. Double the frequency again, which gives 2383.13 counts. Now the rounding to an integer, 2383, would give an error of only 0.1 cent, which is more than good enough. Unfortunately there are two problems. First, fSMCLK must be kept accurate by a frequency-locked loop, either in hardware or software. Second, it must be kept running, which raises the power consumption. We prefer to keep only ACLK running to generate a frequency as low as 880 Hz. One way around these problems is to adopt the idea of modulating the length of each interval. This is used by the digitally controlled oscillator (DCO), described in the section "Digitally Controlled Oscillator, DCO" on page 167. The idea is to accept that the time between any two successive changes of the output is not correct but to ensure that the average over a longer time is accurate. Here is how this could work for toggling the output at 880 Hz from ACLK at 32 KHz. The ideal number of cycles per interval is 32 K/ 880 = 37.24 to two decimal places (I describe an approach with only integers shortly): 1. The first interval is chosen to be 37 cycles, which is the integral part of 37.24. This means that the interval is too short by 0.24 cycle, which I call the shortfall. 2. We aim to make the next interval longer to compensate for the shortfall in the first. The target for the second interval is therefore 37.24 + 0.24 = 37.47 cycles. Again the interval is 37 cycles and the shortfall rises to 0.47 cycles. 3. Same again for the third interval: It is 37 cycles and the shortfall is now 0.71 cycle. 4. Same again for the fourth interval: It is 37 cycles and the shortfall rises further to 0.95 cycle. 5. The target for the fifth interval is 37.24 + 0.95 = 38.18 cycles. This time the interval is increased by 1 to 38 cycles, leaving a reduced shortfall of 0.18 to carry forward. Thus the first five intervals are four of 37 cycles followed by one of 38 cycles of ACLK. The average number of cycles per interval is 37.2 over this range, which corresponds to a frequency of 881 Hz. This average will approach closer to 880 Hz as time goes on. The shortfall always lies between 0 and 1. Here is another way of doing the arithmetic. The ideal number of cycles per interval is given by the division ftimer clock /foutput . All the numbers are integers and we can write the
www.newnespress.com
328
Chapter 8
result as a quotient and remainder--just what we learned at school, many years ago. This can be written formally as the equation ftimer clock = quotient foutput + remainder. (8.3)
For example, 32 K = 32,768 = 37 880 + 208. The quotient of 37 gives the number of cycles of the timer clock in the shorter intervals and 208 is the remainder. The shortfall per interval in the previous calculation is just (remainder/foutput ) = 208/880 = 0.24. It is both exact and easier to do the calculations in cycles of the timer clock. We just add the remainder to the accumulated shortfall in each interval and introduce an extra count when the shortfall exceeds the desired frequency, foutput . Then the accumulated shortfalls in each interval are 208, 416, 624, 832, and 1040. The last exceeds the frequency of 880 so an extra cycle is added to the interval and the shortfall is reduced by 880 to 160. It builds up again in successive intervals until it next exceeds 880 and an extra cycle is included. This approach gives exactly the same outcome as the previous method using floating-point numbers but is far more convenient and efficient. This explanation probably sounds a lot more complicated than it is to implement. Listing 8.13 shows an interrupt service routine to handle this for the Olimex 1121STK. It drives LED1 directly, which gives a precisely timed signal that can be displayed on an oscilloscope, and toggles the piezo sounder to give a "musical" note. The quotient and remainder could be entered as expressions to be calculated by the compiler but I write the values explicitly. Listing 8.13: Interrupt service routine for Timer_A from freqgen1.c, which toggles the output at a precise average frequency of 880 Hz.
# define FREQUENCY 880 // Desired frequency of events // Generally : f_timer = QUOTIENT * FREQUENCY + REMAINDER // or QUOTIENT = f_timer / FREQUENCY ; REMAINDER = f_timer % FREQUENCY # define QUOTIENT 37 // values for 32 KHz timer clock # define REMAINDER 208 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // Flag NOT cleared a u t o m a t i c a l l y { static uint16_t shortfall = 0; // Accumulated ( summed ) shortfall TACCTL1_bit . CCIFG = 0; P2OUT ^= ( BIT0 | BIT5 ); shortfall += REMAINDER ; if ( shortfall >= FREQUENCY ) { TACCR1 += ( QUOTIENT + 1); shortfall -= FREQUENCY ; // // // // // // Acknowledge interrupt Toggle piezo sounder Update accumulated shortfall Over threshold for extra count ? Yes : extra count in output period Subtrct correction from shortfall
www.newnespress.com
Timers
} else { TACCR1 += QUOTIENT ; } }
329
// No : usual count
How well does this work? Unfortunately the musical quality of the note from the piezo sounder is atrocious (it is not intended for such demanding applications) and contains so many harmonics that my daughter's electronic tuner was unable to lock to the frequency. Despite this, I could discern the difference between a perfect square wave with equal intervals and the modulated wave. This was a little surprising because the intervals vary only between 37 and 38. It was interesting to push the method further and Figure 8.8a shows the observed signal for four frequencies of the timer clock. This can be divided down to 4 KHz, in which case the interval is either four or five cycles. Now it sounds much worse. The frequency of the timer clock can be reduced further by dividing ACLK itself, configured with the BCSCTL1 register in the MSP430Fx1xx family. The most extreme value is a clock of 1 KHz, in which case the output is roughly a pattern of five intervals of one cycle followed
(a) Output as a function of time 16 14 12 signal/V 10 8 6 4 2 0 0 5 10 15 time/ms 20 25 0 1000 2000 frequency/Hz 8KHz 220 225 3000 4KHz 2KHz 1KHz 8KHz 1KHz (b) Power spectra 5 0 power/dB 25 210 215
Figure 8.8: Generation of a precise average frequency of 440 Hz using a range of frequencies for the timer clock. The data from the oscilloscope show (a) output as a function of time and (b) measured power spectra for the two extreme cases. Odd multiples of 440 Hz, expected for a square wave, are shown as vertical lines on the plot of the power spectrum.
www.newnespress.com
330
Chapter 8
by one interval of two cycles. (The frequency would be 877.7 Hz if this pattern were the exact output.) The two durations are clear on the oscilloscope trace. The signal sounds terrible and is quite unusable, even if its average frequency is mathematically correct. The signal contains so many harmonics that the ear does not focus on the desired tone of 440 Hz. In fact it sounds closer to the note C than A. For those who are interested, I plotted the power spectra of the signals from timer clocks of 1 KHz and 8 KHz in Figure 8.8(b). The plot for 1 KHz is better as an example of a spread-spectrum signal than an aid to tuning. I had to modify the program that used a 1 KHz clock because of a flaw in the design of Timer_A. This is documented as Bug TA12 in the errata to the data sheet for the F1121A. It affects Timer_A in all devices, as far as I know. The problem is that the next interrupt is lost if a capture/compare register TACCRn is incremented by only 1. There is nothing wrong in principle with doing this provided that the timer clock is slow compared with MCLK and there is a factor of 1000 between the frequencies in my program. However, the bug causes the next interrupt to be lost. A workaround is suggested in the errata. The moral of this is to check the errata if something strange seems to be happening: It may not be your program. Example 8.21 You might like to try this out and hear how the sound changes as a function of the frequency of the timer clock.
8.6
Output in the Up Mode: Edge-Aligned Pulse-Width Modulation
The period of Timer_A is set by TACCR0 in the Up mode, rather than cycling through its natural range of 0x00000xFFFF. This offers precise control of the period but the capabilities of channel 0 are greatly restricted. The major advantage of the Up mode is that periodic outputs can be produced completely automatically in hardware, without any intervention from software after Timer_A has been configured. Thus the MSP430 can be left undisturbed in LPM3 if ACLK is used for Timer_A. This contrasts with the Continuous mode, where a new Compare value must be calculated in an ISR after each match has occurred. Because of the special nature of TACCR0, the notation TACCRn implies n 1 throughout this section.
www.newnespress.com
Timers
331
4
0
1
2
3
4
0
1
2
3
4 TAR TAIFG CCIFG0 CCIFG1
reset set toggle period
set reset
OUT1 (Reset/Set, 7) OUT1 (Set/Reset, 3) OUT0 (Toggle, 4)
Figure 8.9: Sketch of output from channels 0 and 1 of Timer_A in an Up mode. Channel 0 is in the Toggle mode with TACCR0 = 4. The output from channel 1 is shown for both Reset/Set and Set/Reset modes with TACCR1 = 2. Figure 8.9 shows the behavior of TAR in the Up mode with possible outputs from a normal channel (1) and the special channel 0. I have chosen TACCR0 = 4 and TACCR1 = 2. These are the main features: TAR counts from 0 up to the value in TACCR0, which is 4 here, and returns to 0 to start a new cycle on the next clock transition. The period is therefore TACCR0 + 1 = 5 counts. The flag CCIFG0 is set when TAR counts to TACCR0 and the TAIFG flag is set when TAR returns to 0, one cycle later. The flag CCIFG1 is set when TAR counts to TACCR1, which is 2 here. I show the output from channel 1, OUT1, for the two output modes that are usually used in the Up mode. It is best to think that Reset/Set, mode 7, sets the output when TAR counts to 0 and resets (clears) it when TAR counts to TACCR1. Note that the output is set when TAR counts to 0, not when it counts to TACCR0. This is not entirely clear in the family user's guides. The Set/Reset mode (3) does the opposite.
The only output mode that changes OUT0 periodically is Toggle, mode 4, which is also shown. Note that it changes when TAR counts to TACCR0 and is therefore one count earlier than the changes in other channels. The output has twice the period of the timer because of the toggling action.
www.newnespress.com
332
Chapter 8
I show the flags being cleared within one cycle of the timer clock in the figure but this depends on the software. Interrupts can be requested as usual when the flags are set. However, there is no need to request interrupts nor to clear the flags if only the outputs are needed: The outputs are driven periodically as long as the timer runs even if the CPU is turned off and the flags are never cleared. This can be seen when debugging older devices because ACLK remains active and outputs continue to change automatically even when the CPU is stopped by the debugger. The examples I showed earlier for the Continuous mode can be adapted for the Up mode with more or less trouble. This applies to both Capture and Compare operations. Unfortunately it is tricky to handle overflows when calculating the next Compare time or the difference between two Capture times. Recall that this was trivial in the Continuous mode because unsigned, integer arithmetic and the timer registers overflow and wrap around in the same way. Suppose that we are calculating the next Compare time in the Up mode. There are two ways in which values can overflow: The sum can exceed the 16-bit range of an unsigned integer or it can exceed TACCR0. Both cases must be treated correctly. Be careful! A simple case is if TACCR0 = 0x00FF, in which case the arithmetic can be performed with unsigned bytes and overflows are handled automatically. The program in the application note Pong Video Game Using the MSP430 (slaa177) relies heavily on Timer_A to produce video signals. It is far too intricate to explain here, sadly.
8.6.1
Uses of Channel 0 in the Up Mode
Channel 0 is special in the Up mode because its output remains constant for each period between settings of CCIFG0, as shown for Toggle mode (4) in Figure 8.9. This is the simplest way of generating a symmetric square wave of arbitrary frequency. The timer clock and TACCR0 are chosen to give half the desired period, the output mode for channel 0 is set to Toggle, and the signal is generated automatically. This is the ultimate simplification of the program to flash a LED at 1 Hz, started in the section "Automatic Control Flashing Light by Software Delay" on page 91. Unfortunately the TA0 output is not connected to a LED on the Olimex 1121STK (it is used for sending serial data instead) so it does not work. This code would do the job if TA0 were available:
TACCR0 = 0 x3FFF ; // (1/2) s period with 32 KHz clock TACCTL0 = OUTMOD_4 ; // Toggle mode , no interrupts // Start timer from ACLK , no division , Up mode , clear , no interrupts
www.newnespress.com
Timers
TACTL = TASSEL_1 | ID_0 | MC_1 | TACLR ; for (;;) { // Loop forever _ _ l o w _ p o w e r _ m o d e _ 3 (); // Remain in LPM3 , CPU not needed } // ( nor are interrupts )
333
It is a little pedantic to use the value 0x3FFF for TACCR0 rather than 0x4000 because the frequency is not that accurate. Channel 0 can also be used to produce an evenly spaced but otherwise arbitrary sequence of high and low values on the output TA0 by configuring the next Compare event either to Set or Reset the output. This is done in an ISR in much the same way as in the Continuous mode but is simpler because there is no need to update the time for the next Compare event: The constant interval is instead set by TACCR0. This can be the most straightforward way of transmitting serial data, for instance.
8.6.2
Edge-Aligned PWM
Microcontrollers are often required to vary the power supplied to a load through a continuous range, not just on or off. This might seem to call for a digital-to-analog converter, or DAC, but very few microcontrollers contain true DACs. A few MSP430s provide them but most do not. The reason is that pulse-width modulation, or PWM, provides an adequate substitute for a DAC in most applications. It requires only a timer, which is purely digital and therefore much simpler and cheaper to fabricate than the analog circuits required for a DAC. The idea behind PWM is very simple: The load is switched on and off periodically so that the average voltage has the desired value. The fraction of the time while the load is active is called the duty cycle D. This is illustrated in Figure 8.10. Assume that the output is driven either to ground or to VCC . Then the average voltage across the output is given by Vave = D VCC = ton ton pulse width VCC = VCC = VCC . ton + toff tperiod period (8.4)
The duty cycle is almost always varied by keeping the period constant and changing the width of the pulses, hence the name of PWM. Occasionally a different approach is used, such as keeping ton constant and varying toff instead. This is pulse-frequency modulation. A second reason for preferring PWM is that the load is always either fully on or fully off, never in a partly powered state. This is more efficient and reduces the power dissipated in the switching transistors, as explained in the section "Driving Heavier Loads" on page 247.
www.newnespress.com
334
Chapter 8
(a) pulse width average power pulse width 5 32 1/8 power (b) pulse width 5 128 1/2 power (c) pulse width 5 224 7/8 power
period 5 256
time
Figure 8.10: Edge-aligned pulse-width modulation showing three average powers. Pulse-width modulation seems a crude substitute for a real analog output but is entirely satisfactory for many applications. For instance, suppose that you wish to vary the brightness of an LED. It would be obvious if PWM were used at 1 Hz because you would see the LED flash on and off. However, the variation becomes too rapid for the eye to follow if the frequency is raised above 100 Hz and only the average brightness is resolved. At least, this is true provided that you look steadily at the LED. The flashing becomes visible if you flick your eyes across the LED or wave the demonstration board about. If you try this on almost any piece of equipment you see that the LEDs flash rather than remain on continuously. Some loads, notable motors, are inductive and smooth the current waveform intrinsically. This is helpful because PWM is used widely to control motors. In other cases a square wave is not acceptable and the PWM signal must be smoothed to bring it closer to a steady voltage. This requires an analog, low-pass filter outside the MSP430, which is easier to implement if the frequency of the PWM signal is as high as possible. With luck a simple, first-order, RC circuit may be sufficient. A higher-order, active filter is necessary if the ripple remains too large. In this case a "real" DAC, either internal or external, may be a better solution.
8.6.3
Simple PWM
The usual arrangement for PWM is that each output is turned on when TAR returns to 0 and turned off after a variable time that gives the desired duty cycle. This means that increasing the value in TACCRn increases the duty cycle, which makes the operation simpler to understand. Figure 8.9 shows that this needs the Reset/Set output mode (7)
www.newnespress.com
Timers
335
for loads driven active high. This is sometimes called positive PWM. Conversely, the Set/Reset mode (3) should be used for active low loads or negative PWM. Typically there are several outputs, each driven by a separate channel of Timer_A. Channel 0 cannot be used, of course, so Timer_A3 can support only two channels of PWM. They all are switched at the same frequency, controlled by the timer clock and the value in TACCR0. All outputs are turned on at the same time, which is why this is called edge-aligned PWM. They turn off at different times, according to the particular duty cycle Dn of each channel n (for n > 0). A timer with three channels available for PWM, such as Timer_A5, could produce the three signals shown in Figure 8.10 simultaneously. The duty cycle is given by Dn = pulse width n TACCRn = . period TACCR0 + 1 (8.5)
This equation holds for 0 TACCRn (TACCR0 + 1). Larger values of TACCRn give Dn = 1 as are explained below--it is impossible to have Dn > 1. Listing 8.14 shows very slow PWM on an Olimex 1121STK. The period is 1 Hz, determined by TACCR0 and ACLK, so that it is easy to see what is happening without an oscilloscope. The duty fractions for LED1 and LED2 are one half and one quarter, set by the values in TACCR1 and TACCR2. This board has the LEDs connected active low, which requires negative PWM and the Set/Reset output mode (3). The final step is to start the timer. Everything is automatic after that: The outputs are driven directly by Timer_A and the CPU can be stopped. No interrupts or anything else is needed. Listing 8.14: Very slow pulse-width modulation at 1 Hz on an Olimex 1121STK in 1 simppwm1.c. LED1 has duty cycle D1 = 1 and LED2 has D2 = 4 . 2
// Set up Timer_A for PWM on active low LEDs , channels 1 and 2 TACCR0 = 0 x7FFF ; // 1 s period from 32 KHz timer clock TACCR1 = 0 x4000 ; // Duty cycle D = 1/2 TACCR2 = 0 x2000 ; // Duty cycle D = 1/4 TACCTL1 = OUTMOD_3 ; // Set - reset mode for negative PWM TACCTL2 = OUTMOD_3 ; // Set - reset mode for negative PWM // Start timer from ACLK , no division , Up mode , clear , no interrupts TACTL = TASSEL_1 | ID_0 | MC_1 | TACLR ; for (;;) { // Loop forever _ _ l o w _ p o w e r _ m o d e _ 3 (); // Remain in LPM3 , CPU not needed } // ( nor are interrupts )
The LEDs turn on simultaneously when TAR returns to 0, as usual for edge-aligned PWM. LED2 turns off after 0.25s, LED1 remains alight for a further 0.25s, and both stay dark for the remaining 0.5s before the next cycle starts.
www.newnespress.com
336
Chapter 8
If it is important that the very first cycle of PWM is correct, the OUT bit must be used to put the output into the correct initial state before starting PWM. For the usual form of edge-aligned PWM this means that the output should be turned on. The reason is that the first count of TAR is from 0 to 1 (assuming that it is cleared), so the system misses the actions that usually start a new cycle when TAR counts to 0. The outputs are active low on the Olimex 1121STK and the OUT bit should therefore be cleared. This happens to be the default so no explicit code is needed. (Often it does not matter that the first cycle is erroneous and this issue is ignored. The second and subsequent cycles are correct.) Example 8.22 Single-step through this program. At what point does the PWM start? (The behavior may be different if you use a more modern device than the F1121A and the debugger has more control over the clocks.) Example 8.23 How would the program need to be changed if the LEDs were driven active high rather than active low? Hint: Two changes should be made for each channel. Example 8.24 Speed up the PWM so that the flashing becomes too fast for the eye to resolve. A convenient frequency is 256 Hz because it is 0x0100 in hexadecimal, which makes the changes easy to calculate. Remember to adjust the values in all the TACCRn registers. Does the brightness of the two LEDs look very different, given the factor of 2 in power between them? Reduce the duty cycle of LED2 to its minimum nonzero value with the same value of TACCR0. Does the LED still appear to be illuminated? Another important advantage of performing PWM in hardware rather than software is that it is straightforward to get the extreme duty cycles of 0 and 1. These are the two cases: Zero duty cycle is obtained by setting TACCRn = 0, consistent with equation (8.5). The set and reset events now occur simultaneously when TAR returns to 0 and the logic is designed to give zero output. The channel flag CCIFGn
www.newnespress.com
Timers
337
is set in every cycle at the same time as TAIFG and interrupts are requested if they have been enabled in TACCTLn. Unity duty cycle is obtained by making TACCRn greater than TACCR0; the obvious value from equation (8.5) is (TACCR0 + 1) but any value larger than TACCR0 has the same effect. Now the compare event never happens because TAR never counts as high as the value in TACCRn. Suppose that the output mode is Reset/Set (7). In this case the output is set every time TAR returns to 0 but the reset events never occur. Thus the output remains set continuously, giving a duty cycle of 1.
There are a couple of pitfalls with the limit of Dn = 1. First, the channel flag CCIFGn is never raised because the compare event does not happen. This also means that no interrupts are requested--beware if you are using the channel to generate interrupts as well as driving the output directly. The second problem arises if you are using the full range of the timer with TACCR0 = 0xFFFF. In this case it is impossible to store a larger value in the other registers TACCRn so the hardware cannot give Dn = 1 automatically. It must be treated as a special case. Alternatively, set TACCR0 = 0xFFFE and avoid the problem.
8.6.4
Design of PWM
There are two main parameters that must be chosen before suitable values can be selected for PWM: The number of desired values of the duty cycle (the resolution). The frequency of the output waveform. These are linked because it is not possible to have both high resolution and a high frequency. Suppose that the duty cycle of the output is specified in percent to the nearest integer. This means that there are 101 possible values of 0, 1, 2, . . . , 99, 100%. The simplest way of handling this is to choose TACCR0 = 99, which gives a period of 100 counts. The desired duty cycle in percent can then be written directly to TACCRn for channel n (where n > 0). Remember that the denominator in equation (8.5) for the duty cycle is (TACCR0 + 1).
www.newnespress.com
338
Chapter 8
The appropriate frequency fPWM of the output waveform depends strongly on the type of load. Anything higher than about 100 Hz is sufficient for an LED. The period of the PWM is the same as that of the timer and the frequency is therefore given by fPWM = ftimer clock ftimer clock = . period of TAR in counts TACCR0 + 1 (8.6)
This means that ftimer clock must be above 100 Hz 100 counts = 10 KHz for the LED. There would be no problem in running the timer from ACLK at 32 KHz except that 10 KHz is not readily available. The simple options are fACLK /2 = 16 KHz or fACLK /4 = 8 KHz. It would be better to choose the higher frequency to avoid visible flashing of the LED. ACLK may instead be derived from the VLO. This is slower at 12 KHz but just fast enough for this application. This would be too slow for driving a motor because inevitably some vibration occurs at the frequency of the PWM. You might therefore choose fPWM = 20 KHz, above the audible range. Now the frequency of the clock should be above 20 KHz 100 counts = 2 MHz. This needs SMCLK rather than ACLK but is not otherwise a problem. Difficulties arise if we also want to improve the resolution at the same frequency. Suppose that the duty cycle is specified to 0.1% instead of 1%, meaning 1000 values above 0 instead of 100. We have to raise TACCR0 to 999, giving a period of 1000 counts, and the timer must be clocked at 20 MHz. Too fast! The frequency of the clock limits the product of resolution and PWM frequency. The simple approach also fails if the desired frequency is too low. In this case a larger value must be used for TACCR0 to raise the period in real time. This was done in Listing 8.14. The only "penalty" is that there is more resolution than needed. It simply means that TACCRn should be changed by a larger number than 1 to adjust the duty cycle. I assume that we can choose a convenient value for the range of the counter, set by TACCR0, and tolerate an error in the frequency of the PWM wave. Inevitably this is not always true. Suppose that the LED should be driven at 110 Hz to avoid being too close to harmonics of the AC mains at 50 or 60 Hz. If the timer is supplied from ACLK without division, the period should be 32 KHz/110 Hz = 297.9 counts, which could be rounded to 300 with a negligible error. The problem is that the duty cycle can no longer be specified directly in percent although the numbers are still convenient in this case. As an example, let us drive an LED so that its intensity varies linearly from 0 to full brightness and back again to 0 with an overall period of 2s. This is often used for the standby indicator on electronic equipment. Take fPWM = 128 Hz, which is chosen because
www.newnespress.com
Timers
339
it is a power of 2 (easy) and is fast enough that the modulation should not be visible. Each ramp in intensity up or down takes 1s and therefore includes 128 cycles of PWM. It is therefore convenient to choose a period of 128 counts for each cycle with TACCR0 = 127. The value in TACCR1 should be incremented by 1 after each cycle while the intensity increases and decremented by 1 after each cycle while the intensity decreases. This gives the desired ramp. The frequency of the timer clock should be ftimer clock = 128 Hz 128 counts = 16 KHz = fACLK /2, which is easy. Listing 8.15 shows a program for an active low LED on the Olimex 1121STK. The duty cycle starts from 0 and the LED is initially turned off to avoid an erroneous flash when the program starts. The output mode is Set/Reset (3) because the load is active low. The main difference from the previous examples is that the duty cycle varies. It must be updated during every cycle of PWM and I therefore enable interrupts on CCIFG0; I explain later why I chose this rather than TAIFG or CCIFG1. The interrupt service routine contains a static variable to keep track of the direction of the ramp, up or down. There is no need to keep a copy of the duty cycle because we can safely read TACCR1 at any time in the Compare mode. Its value is incremented or decremented according to the direction of the ramp. The result is compared with the appropriate limit and the direction is reversed for the next update if the duty cycle has hit the limit. Listing 8.15: Program ledpwm1.c to drive an LED on an Olimex 1121STK with PWM so that its intensity ramps linearly from 0 to full brightness and back again with an overall period of 2s.
// ledpwm1 . c - Ramped PWM using Timer_A in Up mode from ACLK // PWM at 128 Hz , 128 counts per cycle from ACLK /2; ramp 1 s up , 1 s down // Pulse width is incremented or decremented by 1 each cycle in ramp // Duty cycle updated by TACCR0 CCIFG interrupt // Olimex 1121 STK , LED1 on P2 .3 = TA1 , active low ; piezo off // J H Davies , 2007 -07 -18; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430x11x1 .h > // Specific device # include < intrinsics .h > // Intrinsic functions # include < stdint .h > // Integers of defined sizes // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer // Set up Timer_A for PWM on active low LED , channel 1 TACCTL1 = OUTMOD_0 | OUT ; // Force output initially high ( LED off ) TACCR0 = 0 x007F ; // 128 Hz from 16 KHz timer clock TACCR1 = 0 x0000 ; // Duty cycle D = 0 initially TACCTL0 = CCIE ; // Enable interrupts on compare TACCTL1 = OUTMOD_3 ; // Set / reset mode for negative PWM // Configure ports 1 and 2; redirect P2 .3 to Timer_A P1OUT = BIT0 | BIT1 ; // Output high for Freq pin and TXD
www.newnespress.com
340
Chapter 8
P1DIR = BIT0 | BIT1 ; P2SEL = BIT3 ; // Route P2 .3 to TA1 P2OUT = BIT3 | BIT4 ; // LEDs off ( active low ); piezo off P2DIR = BIT0 | BIT3 | BIT4 | BIT5 ; // Piezo and LED outputs // Start timer from ACLK /2 , Up mode , clear , no interrupts TACTL = TASSEL_1 | ID_1 | MC_1 | TACLR ; for (;;) { // Loop forever _ _ l o w _ p o w e r _ m o d e _ 3 (); // Enter LPM3 between interrupts } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG // Compiler warning for " if ( TACCR1 > TACCR0 )" can be ignored // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { static enum { up , down } direction = up ; if ( direction == up ) { ++ TACCR1 ; if ( TACCR1 > TACCR0 ) { direction = down ; } } else { -- TACCR1 ; if ( TACCR1 == 0) { direction = up ; } } } // // // // Increasing duty cycle ? Yes : increase TACCR1 Hit 100% limit ? Yes : decrease duty cycle next
// Decrease duty cycle // Hit 0 limit ? // Yes : increase duty cycle next
The compiler warns about the comparison if (TACCR1 > TACCR0) in this program. The reason is that both variables TACCR0 and TACCR1 are declared volatile and the result might therefore depend on the order in which they are read. Fortunately there is no problem here because neither variable is really volatile in the Compare mode: They change only when the program writes a new value to them. It would be different in the Capture mode, where the contents would change whenever a capture occurred. The time of this event would be unpredictable, the registers would definitely be volatile, and the warning should be taken seriously. Example 8.25 Try this program. Does the brightness appear to rise and fall linearly? I will tell you the answer to this one: The brightness does not appear to vary linearly. Instead the LED appears to be fairly bright for most of the time with only brief dark
www.newnespress.com
Timers
(a) response of eye to intensity (b) linear ramp in time power to LED t perceived brightness power to LED (c) exponential ramp
341
Figure 8.11: (a) Brightness perceived by the eye as a function of power supplied to LED. Absolute power and perceived brightness as a function of time for an LED driven by a (b) linear ramp and (c) exponential ramp. intervals. The reason is that the response of the eye is far from linear. In fact it is close to logarithmic, which means that the eye detects changes in the ratio of intensities. For example, a change in absolute intensity from 1 to 2 (arbitrary units) is perceived as the same change as going from 10 to 20. The response is plotted in Figure 8.11(a), with the absolute power and perceived brightness of the LED driven by a linear ramp in (b). Another way of looking at this, which should appeal to electronic engineers, is to imagine that the eye works in decibels. We need to correct for the logarithmic response if we want the variation of intensity to appear linear. This means that each step should give the same ratio of intensities. In other words, the ramp should be an exponential function, as in Figure 8.11(c). The obvious choice of ratio is 2, in which case the values for TACCR1 should be 0, 1, 2, 4, 8, 16, 32, 64, 128. This contrasts with the original linear ramp of 0, 1, 2, 3, 4, . . . , 127, 128. A problem is that there are only 8 values instead of 128, so each duty cycle must be repeated for 16 periods of PWM to give the same period for the ramp. Listing 8.16 shows the interrupt service routine that updates the duty cycle. It is slightly clumsy because a new upward ramp must be started by storing 1 in TACCR1, after which it can be doubled and halved by shifting for the remainder of the pattern. Listing 8.16: An LED driven with an exponential ramp using PWM by ledpwm3.c. The duty cycle changes by a factor of 2 at each step.
// Number of times each duty cycle is performed between updates # define R E P E A T S _ P E R _ C Y C L E 16 # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y
apparent brightness
www.newnespress.com
342
{
Chapter 8
static enum { up , down } direction = up ; static uint8_t repeats = R E P E A T S _ P E R _ C Y C L E ; -- repeats ; if ( repeats == 0) { repeats = R E P E A T S _ P E R _ C Y C L E ; if ( direction == up ) { if ( TACCR1 == 0) { TACCR1 = 1; } else { TACCR1 < <= 1; if ( TACCR1 > TACCR0 ) { direction = down ; } } } else { TACCR1 > >= 1; if ( TACCR1 == 0) { direction = up ; } } } } // // // // // // Count repeats of each duty Time to update duty cycle Restart duty cycle counter Increasing duty cycle ? Yes : Restarting ramp ? Yes : initialize
// No : double TACCR1 // Hit 100% limit ? // Yes : decrease duty cycle next
// Halve duty cycle // Hit 0 limit ? // Yes : increase duty cycle next
Example 8.26 Try this program. Does the brightness now appear to rise and fall linearly? Unfortunately the steps of a factor of 2 in this program are rather coarse and there are too few of them. A smaller ratio is needed to get a smoother variation. The number of steps can be doubled by using a ratio of 2 = 21/2 instead. It is easier to work out the values by dividing the maximum value repeatedly by the ratio, which gives 128, 91, 64, 45, 32, . . . , rounded to the nearest integer. New values are interleaved between the powers of 2 used previously to double the number of values. The list can be stored in a constant array and the index stepped up and down. Finer variations can be obtained by using higher roots. For example, a ratio of 21/4 gives 128, 108, 91, 76, 64, . . . . There is no need to use a root of 2 but it is reassuring to see familiar values such as 64 in the list. The ratio can be chosen to give any desired number of values. A problem is that rounding affects the smaller values badly. The solution is to use a faster clock for the timer to increase the range of counting and the resolution of the duty cycle. There is a straightforward example of PWM to control the speed of a fan in the application note Digital Fan Control with Tachometer Using MSP430 (slaa259). Another example is in the application note Ultra-Low Power TV IR Remote Control Transmitter (slaa175), which
www.newnespress.com
Timers
343
uses PWM to drive an infrared LED in the remote control. Timer_A also determines the overall length of each bit.
8.6.5
Software-Assisted PWM
None of the examples in the previous section works on the eZ430F2013 or on the TI MSP430FG4618/F2013 Experimenter's Board because they do not have LEDs or other output devices connected to pins that can be routed to Timer_A. The timer needs assistance from software to drive a PWM signal onto other pins. Inevitably this abandons the precise timing offered by pure hardware and therefore is satisfactory only for low frequencies. Let us adapt the simple, linear ramp in Listing 8.15 for an eZ430F2013. This has an LED connected active high to P1.0. The timer in the F2013 is Timer_A2, which offers only the single channel 1 for PWM. Its output TA1 can be routed to three pins but these do not include P1.0. We need to emulate positive PWM, which means that the LED should be turned on when TAR returns to 0 and turned off when TAR matches TACCR1. The flags TAIFG and CCIFG1 are set at these two events and the straightforward solution is to turn the LED on and off in the corresponding interrupt service routines. I set up channel 1 for positive PWM in Reset/Set mode to make the purpose clear but the mode is irrelevant because we use only the interrupts. A few more changes are made in Listing 8.17 because there is no watch crystal for ACLK, which must instead be provided by the VLO. I remove the division of the timer clock and reduce the number of counts per cycle to 110, which should give a ramp of 1s each way if the VLO runs at its nominal frequency of 12 KHz. I also raise the frequency of the DCO to its maximum of 16 MHz. This gives over 1000 cycles of MCLK for each cycle of ACLK, which should allow plenty of time for the interrupts to be serviced before the next count of Timer_A. Listing 8.17: Program ezpwm1.c to drive the LED on an eZ430F2013 with a linear ramp up and down using PWM. The LED is not connected directly to Timer_A and must therefore be switched on and off in interrupt service routines.
// ezpwm1 . c - Ramped PWM using Timer_A , Up mode from ACLK = VLO = 12 kHz // 110 counts per cycle from ACLK ; PWM roughly 110 Hz ; ramp 1 s each way // LED turned on by TAIFG and off by CCIFG1 interrupts // Duty cycle updated by CCIFG0 interrupt // Pulse width is incremented or decremented by 1 each cycle in ramp // TI eZ430 , LED1 on P1 .0 , active high , NO direct connection to Timer_A // J H Davies , 2007 -07 -18; IAR Kickstart version 3.42 A // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # include < io430x20x3 .h > // Specific device # include < intrinsics .h > // Intrinsic functions
www.newnespress.com
344
Chapter 8
# include < stdint .h > // Integers of defined sizes // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void main ( void ) { WDTCTL = WDTPW | WDTHOLD ; // Stop watchdog timer BCSCTL3 = LFXT1S_2 ; // ACLK from VLO // Select fastest MCLK (16 MHz ) to ensure time for processing interrupts BCSCTL1 = CALBC1_16MHZ ; // Set range for calibrated 16 MHz DCOCTL = CALDCO_16MHZ ; // Set DCO step and modulation // Set up Timer_A for PWM on active high LED , channel 1 TACCR0 = 110; // Roughly 110 Hz from 12 kHz timer clock TACCR1 = 0 x0000 ; // Duty cycle D = 0 initially TACCTL0 = CCIE ; // Interrupts on compare to update PWM TACCTL1 = OUTMOD_7 | CCIE ; // Reset - set ( positive PWM ) , interrupts // Configure ports 1 and 2 with pull resistors on unused pins P1OUT = 0; // Preclear output buffer P1DIR = BIT0 ; // Set P1 .0 to output , others input P1REN = 0 xFF & ~ BIT0 ; // Pull resistors on P1 [7:1] P2SEL = 0; // Digital input / output rather than crystal P2REN = BIT6 | BIT7 ; // Pull resistors on pins that exist // Start timer from ACLK , no division , Up mode , clear , with interrupts TACTL = TASSEL_1 | ID_0 | MC_1 | TACLR | TAIE ; for (;;) { // Loop forever _ _ l o w _ p o w e r _ m o d e _ 3 (); // Enter LPM3 between interrupts } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for CCIFG1 and TAIFG ; share vector // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 1 _ V E C T O R _ _interrupt void TIMERA1_ISR ( void ) // ISR for CCIFG1 and TAIFG { switch ( _ _ e v e n _ i n _ r a n g e ( TAIV , 10)) { // No interrupt pending case 0: break ; // No action case 2: // Vector 2: CCIFG1 P1OUT_bit . P1OUT_0 = 0; // End of duty cycle : Turn off LED break ; case 10: // Vector A : TAIFG , last value possible P1OUT_bit . P1OUT_0 = 1; // Start of PWM cycle : Turn on LED break ; default : // Should not be possible for (;;) { // Disaster . Loop here forever } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine for TACCR0 . CCIFG // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { static enum { up , down } direction = up ; if ( direction == up ) { ++ TACCR1 ; // Increasing duty cycle ? // Yes : increase TACCR1
www.newnespress.com
Timers
if ( TACCR1 > TACCR0 ) { direction = down ; } } else { -- TACCR1 ; if ( TACCR1 == 0) { direction = up ; } } } // Hit 100% limit ? // Yes : decrease duty cycle next // Decrease duty cycle // Hit 0% limit ? // Yes : increase duty cycle next
345
If you try this, you will discover that it works--almost. The brightness ramps up and down as in the earlier program but there is a bright flash when the LED should be dark. Clearly there is a problem when the duty cycle is 0. Why is this? A normal cycle starts by lighting the LED when TAIFG is raised and ends by extinguishing it when CCIFG1 is raised. Both events occur at the same time when the duty cycle is 0. The two interrupts share the same vector and the order of execution is set by their priorities, which are listed in Table 8.3. This shows that CCIFG1 has the higher priority and its interrupt is therefore serviced first, which turns the LED off. The interrupt for TAIFG is serviced immediately afterward and turns the LED on. This is the wrong way around. Thus the relative priorities of TAIFG and CCIFG1 cause an error for zero duty cycle. Even if the interrupts were serviced in the opposite sequence, the LED would be switched on for a very brief interval, which is undesirable. Example 8.27 Repair this problem. Are there any similar issues for a duty cycle of unity? The disadvantage of driving outputs from interrupt service routines is that there is a delay between the timer raising a flag and the consequent interrupt service routine. This is shown in Figure 6.6, where there is more than 1 s between the directly driven signal OUT0 and P1.0, which is controlled by software. The delay would be shorter if the DCO were always active but would be extended if an interrupt were already being serviced or if an interrupt with higher priority were requested. The delays are longer for Listing 8.17 because its interrupt service routines are more complicated--only a single instruction is needed for the ISR in Figure 6.6. PWM can also be produced from the Continuous mode and an example is shown for LED2 in Listing 8.10. In that case the load is connected directly to Timer_A so we only had to calculate the time for the next compare event in the ISR. It could also switch the load on
www.newnespress.com
346
Chapter 8
and off if a direct connection were not available. Special treatment is needed for values of the duty cycle close to 0 and 1.
8.6.6
When Should the Duty Cycle Be Updated?
This seems a daft question: Surely we can just write a new value to TACCRn to update the duty cycle whenever we wish? Well, no, not if you want a perfectly clean waveform, as the example shows. Example 8.28 Modify the program in Listing 8.15 so that the duty cycle is updated in the interrupt service routine for TAIFG rather than TACCR0. What goes wrong? Here is the answer: You should find that the LED now flashes briefly when it should be dark. The cause of the problem is illustrated in Figure 8.12(a). The period of the
(a) Decrease of duty cycle from t. to t, before update (1)
update
(2)
after update 0 t, t. T
(3) 0 t, t. T t
(b) Increase of duty cycle from t, to t. before update (1)
update
(2)
after update 0 t, t. T
(3) 0 t, t. T t
Figure 8.12: Effect of (a) decreasing and (b) increasing the duty cycle of PWM at three points in the cycle.
www.newnespress.com
Timers
347
<.
PWM waveform is T and I show the length of the high pulse being reduced from > to Look at the detailed effect of updating the duty cycle at different points in the cycle:
1. Update between 0 and < , before the end of the new, shorter pulse. In this case the update happens in time for the output to be switched off at the new time < and no problems arise. 2. Update between < and > , between the ends of the new and old pulses. This is the nasty one. The output is high because the timer has not yet reached the old duration > . The update changes the end point to < , but this has already passed. The output therefore is not turned off at either < or > during the current cycle, which consequently has a duty cycle of unity. The output remains high until it is turned off at < in the following cycle. 3. Update between > , after the end of the old, longer pulse. The output has already been turned off for the current cycle and the new time takes effect in the next cycle. No problems. This shows that the tricky case is where the length of pulse is decreased at a time between the ends of the old and new pulses. The update is performed while TAR = 0 in the program set in Example 8.28, assuming that the interrupt service routine can be completed within one cycle of the timer clock. The problem therefore arises when TACCR1 is reduced from 1 to 0. Start with TACCR1 = 1. The LED is turned on when TAR returns to 0. At this point the TAIFG interrupt is requested and serviced, which updates TACCR1 from 1 to 0. The next compare event does not happen until TAR counts to the value of 0 in TACCR1, which does not occur until the start of the next cycle. This also means than an interrupt is missed, if these have been enabled. Oops! The problem is less dramatic when the duty cycle is increased, as shown in Figure 8.12(b). Again the tricky case is when the duty cycle is updated between the ends of the old, shorter pulse and the new, longer pulse. The old pulse has already ended and the output was turned off at < . The updated value causes a second compare event to occur at > . This has no impact provided that interrupts are not used and the output mode is either Reset/Set or Set/Reset, and it does not request an interrupt. On the other hand, you get a nasty surprise if you use one of the toggling modes 2 or 6 because these cause the output to switch back on again at the second compare event. It will recover in the next cycle. The second interrupt may also be a problem if it is assumed that there is exactly one per cycle. These difficulties can be avoided by updating the duty cycle only during the safe parts of the cycle. In principle this means that you should
www.newnespress.com
348
Chapter 8 Decrease the duty cycle at the end of a pulse, following the CCIFGn flag. Increase the duty cycle at the start of a pulse, following the TAIFG flag.
Inevitably there is a catch: No CCIFGn interrupts are requested if the duty cycle is 100%. The best approach for Timer_A is to update the TACCRn registers following the CCIFG0 interrupt. This is requested when TAR counts to its maximum value of TACCR0, so there are no more compare events before the TAR returns to 0 to start the next cycle. This prevents the problems we just saw. However, it is reliable only if the updates can be completed before TAR changes. In other words, the interrupt service routine must run completely within a cycle of the timer clock. The ISR must therefore be brief (always a good idea) and MCLK should be fast. At this point you are probably thinking, Why is this so troublesome? Surely there must be a better solution? There is, but it requires the more powerful hardware of Timer_B. Example 8.29 Write a program to control the brightness of LED1 on the Olimex 1121STK using the buttons. The brightness should decrease gradually while B1 is down and increase while B2 is down. (What should you do if both are down?) Start with 50% power and use a linear ramp. This sort of program is often used to control the speed of a motor using PWM and is explained in the application note PWM DC Motor Control Using Timer_A of the MSP430 (slaa120). Care is also needed when updating PWM in the Continuous mode to ensure that the length of each cycle is constant. The following interrupt service routine for channel 0 is adapted from the course Tips and Tricks for Timer_A by Andreas Dannenberg and Peter Forstner:
# pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TIMERA0_ISR ( void ) // Flag cleared a u t o m a t i c a l l y { static uint16_t PWM0Low ; // Local copy of low duration if ( TACCTL2 & OUTMOD_4 ) { // TACCR0 += PWM0Low ; // } else { // TACCR0 += DutyCycle0 ; // PWM0Low = TACCR0 - DutyCycle0 ; } TACCTL2 ^= OUTMOD_4 ; // } Was last action reset ? Yes : calculate end of low output No : start new cycle ( high output ) Calculate end of high output // Calc & store low duration now Toggle output mode Set / Reset
www.newnespress.com
Timers
349
The duty cycle is controlled by the external variable DutyCycle0, which is updated in the main routine. The ISR tests whether the output has just been set or reset in the same way as the ISR for channel 2 in Listing 8.10. A new cycle has begun if the output is set and the time to the next compare event is calculated by adding DutyCycle0 to the last value of TACCR0. At the same time, the duration of the low part of the cycle is calculated and stored in a static variable PWM0Low. This value is used after the next reset event, which ensures that the cycle has the correct length even if DutyCycle0 has been changed. (I assume that interrupts are not nested so there is no danger of DutyCycle0 changing inside the ISR.)
8.7
Output in the Up/Down Mode: Centered Pulse-Width Modulation
Figure 8.13 shows the operation of the counter in the Up/Down mode: TAR counts from 0 up to the value in TACCR0, which is 3 here, changes direction and counts back down to 0, which is the start of the next cycle. The period is therefore 2 TACCR0 = 6 counts. The CCIFG0 flag is set when TAR counts to TACCR0 and the TAIFG flag is set when TAR returns to 0. They are equally spaced, so that TACCR0 is set halfway between settings of TAIFG.
1
0
1
2
3
2
1
0
1
2
3 TAR TAIFG CCIFG0 CCIFG1
toggle toggle
reset set toggle period
toggle toggle
OUT1 (Toggle/Reset, 2) OUT1 (Toggle/Set, 6) OUT0 (Toggle, mode 4)
Figure 8.13: Output from channels 0 and 1 of Timer_A in the Up/Down mode. Channel 0 is in the Toggle mode with TACCR0 = 3. The output from channel 1 is shown for both the Toggle/Reset and Toggle/Set modes with TACCR1 = 1.
www.newnespress.com
350
Chapter 8
The CCIFG1 flag is set when TAR counts to TACCR1, which is 1 here. This happens twice per cycle, once on the way up and again on the way down. The output from channel 1, OUT1, is shown for the two output modes that are most useful with the Up/Down mode. Both toggle the output when TAR counts to TACCR1, which means that they switch the output on at one of the matches in each Up/Down cycle and switch it off at the other. Thus all the changes in the output during normal operation occur when TAR counts to TACCR1. The second action, which occurs when TAR counts to TACCR0, sets the phase or polarity of the output as follows: -- The output is cleared at TACCR0 in Toggle/Reset mode 2. This causes the high pulses to be centered on the points when the TAIFG flag is set. A larger value of TACCR1 causes longer pulses, so this is positive PWM. -- Toggle/Set mode 6 does the opposite. The output can be viewed in two ways. It can be negative PWM, meaning active low pulses centered on TAIFG. Alternatively, it can be regarded as positive pulses centered on CCIFG0, midway between the pulses produced by mode 2. As in the Up mode, the only output mode that changes OUT0 periodically is Toggle, mode 4, which gives twice the period of the timer. As in the Up mode, the outputs are driven periodically as long as the timer runs even if the CPU is turned off, interrupts are disabled, and the flags are never cleared. The Up/Down mode is used for centered pulse-width modulation, illustrated in Figure 8.14. Channels 1 and 2 are set up in Toggle/Reset mode 2 to give positive pulses centered on TAIFG. The duty cycle is Dn = pulse width n 2 TACCRn TACCRn = = . period 2 TACCR0 TACCR0 (8.7)
This equation holds for 0 TACCRn TACCR0. The logic is designed to give the correct behavior for the two extreme values of Dn = 0 and 1. However, do not allow TACCRn to become greater than TACCR0 or the toggle events will not occur at all, only the reset when TAR = TACCR0. Thus the output will remain off with Dn = 0 rather than on with Dn = 1, which is probably the aim. Centered PWM has two features that distinguish it from edge-aligned PWM. The first is that all the outputs do not switch on at the same time as in Figure 8.10. This avoids the
www.newnespress.com
Timers
351
large transients that can arise from the simultaneous switching of heavy loads. The second feature is that a dead time can be maintained between outputs. Channels 2 and 3 in Figure 8.14 show this. The pulses for channel 2 are centered on TAIFG as usual but those for channel 3 are centered on CCIFG0 instead. There is an interval between the pulses when neither is high, which is the dead time. This cannot be done for both ends of the pulses in edge-aligned PWM and calls for centered PWM instead. The dead time is needed when the two channels drive an H-bridge or a similar, complementary pair of outputs. Look back at Figure 7.14(e). Transistors A and D might be driven by channel 2 of Timer_A with B and C connected to channel 3. It is vital that the outputs of the two channels should not be high at the same time or the transistors create a short circuit between Vsupply and ground. Bang! The dead time between the outputs gives a brief interval for safety when both transistors are turned off. There is no hardware in Timer_A to preserve the dead time when the duty cycle is changed. This means that the two capture/compare registers, TACCR2 and TACCR3 in Figure 8.14, must be updated in the correct sequence. Suppose that the length of the pulse on channel 2 is to be increased and that on channel 3 is to be decreased. Channel 3 should be updated first, which lengthens the dead time. Channel 2 is updated next to
CCIFG1 CCIFG2 CCIFG3 CCIFG0
TAR TACCR0 TACCR3 TACCR2 TACCR1 0 channel 1 channel 2 channel 3
TAIFG
t Toggle/Reset (mode 2) Toggle/Reset (mode 2) Toggle/Set (mode 6) dead times
Figure 8.14: Centered pulse-width modulation from channels 13 of Timer_A in the Up/Down mode. Channels 1 and 2 drive active high outputs centered about TAIFG with duty cycles of one quarter and one half. The pulses from channel 3 are centered on CCIFG0 to give dead times between channels 2 and 3.
www.newnespress.com
352
Chapter 8
complete the change. The dead time is eliminated if a large change is made in the opposite order, with possible damage to the load under control. It is easier to use Timer_B if available. Example 8.30 Rewrite the program in Listing 8.14 to flash the LEDs with centered PWM rather than edge alignment. First arrange that both LEDs are on at the same time as for channels 1 and 2 in Figure 8.14. Next, modify the program so that the short flash takes place while the other LED is off, like channels 2 and 3 in Figure 8.14.
8.8
Operation of Timer_A in the Sampling Mode
This is really a particular application of the Compare mode. One of the actions that takes place when a compare event occurs is to store a copy of the current capture input CCI in the latch bit SCCI. In other words, the input is sampled at the time of the compare event. This is something like a sample-and-hold circuit but it is purely digital rather than analog. It is used when a digital input needs to be sampled at precise times and the main application is to asynchronous serial communications. The details are covered in the section "A Software UART Using Timer_A" on page 590, but here is a brief description to show how Timer_A is used to receive a byte. A byte of data comprises 8 bits but frames of 10 bits are used in the most common format for asynchronous serial communication. The two extra bits are needed to define the beginning and end of each byte to form a complete frame. The line idles high at the value for a logic 1 between transmissions. A new byte begins with a falling edge on the line, which remains low during the start bit of 0. The 8 data bits are transmitted next, starting with the least significant bit (lsb). Finally, a stop bit of 1 completes the frame and leaves the line high, ready for a falling edge to signal the next start bit. This is shown in Figure 8.15 and the following sequence of operations is used to read the signal using one capture/compare channel of Timer_A in the Continuous mode with interrupts: Between transmissions, the channel waits in the Capture mode for a falling edge on its input. An interrupt is requested when an edge is detected. The channel is switched to the Compare mode and the next event is set for half the length of a bit in time.
www.newnespress.com
Timers
start bit ST 0 lsb sample 1 2 3 4 5 6 7 msb t bit length stop bit SP sample
353
Figure 8.15: Asynchronous reception of a byte using the Capture mode to detect the initial falling edge and Sampling mode to read the bits that follow. The next interrupt occurs after the Compare event. It should be in the middle of the start bit (ST) and the SCCI bit is checked to confirm this. If SCCI = 0 as expected, the next Compare event is set up to occur after a further bit length. If SCCI = 1, the initial edge must have been noise and the channel is switched back to the Capture mode to await the next valid transmission. After the next interrupt, SCCI contains the value of the least significant bit, lsb. This is stored and the next Compare event is set up to occur after a further bit length. This is repeated until all 8 bits of data have been received. A final Compare event for the stop bit (SP) is set up to occur after a further bit length. The final sample is checked to verify that SCCI = 1 as expected for the stop bit. If this is correct, the main routine is alerted that a new byte has been received. If not, a framing error occurred and the byte is discarded. In both cases the channel returns to the Capture mode, ready for the next byte. A different type of communication is described in the application note Decode TV IR Remote Control Signals Using Timer_A3 (slaa134), which shows how to decode both RC5 and SIRC TV IR remote control signals.
8.9
Timer_B
Timer_B is provided on larger devices in all MSP430 families. It is closely similar to Timer_A, with a central timer block and three or seven capture/compare channels. The names of the signals and registers are the same but with a B instead of an A; for example, the counter is TBR instead of TAR. Unused bits in TACTL and TACCTLn are exploited in the corresponding registers of Timer_B to provide its additional functions. There are four differences between Timer_A and Timer_B, of which the first is probably the most important:
capture
1 2
bit length
www.newnespress.com
354
Chapter 8
The capture/compare registers TBCCRn are double-buffered when used for compare events and can be updated in groups. This is explained later. The length of TBR can be programmed to be 8, 10, 12, or 16 (default) bits. This is controlled by the CNTLx bits in TBCTL and allows a range of periods to be selected for the Continuous mode. Do not use this feature for the Up and Up/Down modes. The SCCI bit is not provided, which means that the Sampling mode is not possible. This means that Timer_B is less suitable for receiving asynchronous signals but Timer_A is always available if needed. In any case, devices that include Timer_B are likely to have a dedicated module for communications. All of Timer_B's outputs can be put into a high-impedance state by a high external signal applied to the TBOUTH input pin. This can be done in Timer_A only by using software to reconfigure the pins to be digital inputs. These improvements make it much easier to produce clean, reliable PWM signals with Timer_B, avoiding the problems described in the section "When Should the Duty Cycle Be Updated?" on page 346. Figure 8.16 shows a simplified block diagram of Timer_B for comparison with Timer_A in Figure 8.5. The comparator in the capture/compare channel of Timer_A lies directly between TAR and TACCRn. This means that any changes to TACCRn take effect immediately, which caused the difficulties described earlier in this chapter. Timer_B has an additional compare latch TBCLn in each channel and the comparator detects a match between TBR and TBCLn, not with TBCCRn. These latches are private to Timer_B and cannot be accessed from software, nor are they visible in the debugger. This arrangement is called double-buffering. The latches are updated from TBCCRn at points in the cycle of TBR controlled by the CLLDx bits in TBCCTLn. There are four choices, which are made independently for each channel: Immediate (0): Values are copied to TBCLn as soon as they are written to TBCCRn. Effectively there is no double-buffering. This is the default. TBR counts to 0 (1): TBCLn is updated from TBCCRn when TBR counts to 0, which starts a new cycle. All channels are updated simultaneously. This is an obvious mode to use for edge-aligned PWM.
www.newnespress.com
Timers
TBSSELx TBCLK ACLK SMCLK INCLK 16-bit timer register IDx MCx, CNTLx divider TBR /1/2/4/8 timer clock EQU0 comparator TB1 inputs CCI1A CCI1B GND VCC output mode OUTMODx Timer block TBIFG
355
Capture/compare channel 1
TB1 output OUT1
CCISx
TBCL1
EQU1
CLLDx update load mode
capture CCI mode CMx
CAP TBCCR1 capture
TBCCR1 CCIFG (CCIFG1)
Figure 8.16: Simplified block diagram of Timer_B showing the timer block and capture/compare channel 1. Note the latch TBCL1 between the capture/compare register TBCCR1 and the comparator for the Compare mode.
TBR counts to 0, or to old TBCL0 in Up/Down mode (2): This is the same as mode 1 in the Up and Continuous modes. In the Up/Down mode, TBCLn is updated when TBR counts either to 0 or to the old value of TBCL0. This means that the update occurs at either extreme of TBR, when it is about to change direction. It gives a more rapid update of centered PWM than mode 1. TBR counts to old TBCLn (3): TBCLn is updated from TBCCRn when TBR counts to the current (old) value of TBCLn. This means that the channel is updated at the end of the high pulse in basic, edge-aligned PWM or at both ends of the pulse in centered PWM. Double-buffering is beneficial for PWM but is not generally appropriate for single pulses or delays. In these cases it is important that the channel is updated immediately for the next event, which requires CLLD = 0. A further refinement is that channels can be grouped so that their latches are updated simultaneously. This is particularly useful to maintain the dead time between outputs,
www.newnespress.com
356
Chapter 8
shown for channels 2 and 3 in Figure 8.14. Groups are selected using the TBCLGRPx bits in TBCTL and can be pairs, threes or all channels of Timer_B7. The double-buffering also protects channel 0 when it acts as the limit in the Up and Up/Down modes. This means that TBR counts to the value in TBCL0, not to that in TBCCR0, and TBCL0 is updated in the same way as the other channels. On the other hand, Capture events cause the current value of TBR to be copied directly to TBCCRn and the compare latch is not involved. The application note Using PWM Timer_B as a DAC (slaa116) shows how to create a simultaneous sine wave, ramp, and DC level by smoothing pulse-width modulated signals from Timer_B. Glitches in the output are avoided by updating the channels when TBR returns to 0, which requires CLLD = 1.
8.10
What Timer Where?
With up to five types of timer in the MSP430, an obvious question is which to choose for a particular application. Of course there are so many, widely varying applications that a simple list will not always be helpful, but here is a guide to start your selection: Pulse-width modulation: Use Timer_B if available on your device, otherwise Timer_A. Connect the load directly to an output of the timer so that it can be driven directly by hardware. Less regular outputs: Connect directly to an output of Timer_A or B. Use the Up mode if the intervals between changes are always the same, as in many forms of communication. The Continuous mode is easier if the intervals vary. Inputs to be sampled at regular intervals: Connect directly to an input of Timer_A and use the Sampling mode (the Compare mode with the SCCI bit). This applies mainly to communications. Inputs to be timed: Connect slow inputs directly to a Capture input of Timer_A or B. Fast signals should be connected to one of the timer clock inputs, such as TACLK or INCLK. Interaction with other peripherals: Use the internal connections to other peripherals wherever possible, both for capture and compare events. This gives precise timing and saves power if the CPU need not be restarted.
www.newnespress.com
Timers
357
Periodic software interrupts: A wide range of options are available and the selection is less clear: Try the watchdog timer if it is not needed as a watchdog and if the interval is appropriate; there is a choice of only four intervals for a given clock frequency. These are roughly 2, 16, 250, and 1000 ms from ACLK at 32 KHz, slower if VLO is used instead. Shorter intervals can be obtained by using SMCLK instead of ACLK. The obvious choice in a MSP430x4xx device is Basic Timer1, again provided that the interval is convenient. The typical range is from about 16 ms to 2s. The real-time clock gives further options if available. If neither of these is suitable you use Timer_A or B, which can produce almost any interval desired. The snag is that this may interfere with the use of their more advanced features.
Less regular software interrupts: Use Timer_A or B, preferably in the Continuous mode. The last resort: Use software loops. Avoid these whenever possible except for trivial cases, such as delays while a clock stabilizes.
8.11
Setting the Real-Time Clock: State Machines
Many embedded systems need to perform a number of tasks, whose sequence depends on the state of one or more signals. State machines provide a natural framework for constructing this sort of software and are therefore very widely used. I admit that this might not seem a natural place at which to introduce them but I have two reasons. The first is that I gave several examples of real-time clocks whose time can be set only in the debugger, which is hardly realistic. A state machine is appropriate for handling the inputs to set the time. Second, the periodic state machines that I describe here need to be driven by a periodic tick and therefore rely on interrupts from a timer. There is a formal foundation for state machines, which is often based on the unified modeling language (UML) but I am not going to describe this. You might like to look through reference [20] and items in the section Embedded System in Appendix B: Further Reading. Properly I should use the more precise term finite state machines; they are also known as automata in computer science. More complicated systems may be designed using state charts, which are hierarchical extensions of state machines and share some of the properties of object-oriented languages.
www.newnespress.com
358
Chapter 8
8.11.1
Introduction to State Machines
Electronic engineers probably have encountered state machines in a course on digital design because they grow naturally out of synchronous counters [38]. A counter consists of flip-flops to hold the current value (the state of the counter) and combinational logic to guide the counter to the next state after a clock transition. The distinguishing feature of a synchronous counter is that all flip-flops receive the same clock and therefore change simultaneously. A standard way of designing such counters by hand is to construct a state transition table. I show a simple example in Figure 8.17 for a 2-bit up counter with an active high "enable" input. This is the significance of the columns in the table: The present state is the value of the counter before a clock transition. When a clock transition arrives, the counter changes from its present state to the next state according to the value of the enable input at the clock transition. In this example the counter follows the usual sequence 01230 . . . when the enable input is active (1) but remains in the same state if the enable input is inactive (0). For example, suppose that the present state of the counter is 1. It remains in this state if enable = 0 but changes to 2 if enable = 1. An alternative way of showing the operation of the system, which should really come before the transition table, is a state transition diagram. This is also shown in Figure 8.17: States are represented by circles and labeled with the value held in the counter.
present enable state input 0 1 2 3 0 1 0 1 0 1 0 1
next state 0 1 1 2 2 3 3 0
!enable enable !enable 3 enable !enable 1 !enable 2 enable 0 enable
Figure 8.17: State transition table and corresponding state transition diagram for a 2-bit up counter with an enable input.
www.newnespress.com
Timers
359
Arrows show the transitions between states that occur at each clock cycle. An unlabeled arrow would show a transition that always occurred--if the counter had no enable input, for instance. Here the transitions are labeled to show the influence of the enable input; "!enable" means the inactive value. Suppose again that the counter holds 1 before a clock transition. If the enable input is active, the system takes the transition to state 2. On the other hand, if the enable input is inactive, the transition labeled !enable loops back to the same state 1 so there is no change. It is straightforward to extend this concept from hardware to software. The regular clock signal is replaced by a periodic trigger from a timer, probably as an interrupt. The state of the system is recorded in a state variable and software determines the next state, depending on current values of various inputs. A new and vital feature is that the software can perform a particular action associated with each transition. For a simple example go back to the section "Read Input from a Switch" on page 80, where we illuminated an LED while a button was pressed. Figure 8.18 shows a state transition diagram for this simple system. There are only two states, LedOff and LedOn, and a single input called Button, which is active when the button is pressed. A new feature is that the transitions are labeled with two fields separated by a slash. The first field shows the value of the input required for the transition to be selected and the second shows the action that should accompany the transition. I also introduce the black blob with an arrow pointing to the LedOff state, which shows the initial state. This is how the state machine operates: It enters the LedOff state after initialization. The initial transition should really have an Extinguish LED action to ensure that the LED is initially switched off.
The state machine is called periodically. While the button remains up, the state machine takes the transition labeled !Button and therefore remains in the LedOff
Button/Light LED !Button/ LedOff !Button/Extinguish LED LedOn Button/
Figure 8.18: State transition diagram for an LED controlled by a button. The LED is illuminated when the button is pressed and extinguished when it is released.
www.newnespress.com
360
Chapter 8 state. No action is associated with this transition so nothing follows the slash in the annotation.
If the button is pressed when the state machine is called, it performs the Light LED action and makes a transition to the LedOn state.
The state machine remains in the LedOn state until the button is released, when it carries out the Extinguish LED action and returns to the LedOff state. That is the picture: How is it turned into code? One approach is to set up a state transition table like that in Figure 8.17 with an extra column for the action (a function) associated with each transition. The table can be implemented as an array with the present state and inputs as indexes. This is concise and efficient but not easy for a human to interpret. Simple state machines are usually written in a more elementary style using switchcase and ifelse statements. Listing 8.18 shows the state machine for Figure 8.18 in the interrupt service routine for channel 0 of Timer_A. I use an Olimex 1121STK but the hardware is defined using symbolic constants to keep it general. A Monitor LED flashes while the ISR is active to show when the state machine operates but it is hard to see because the operation is so brief. Listing 8.18: Interrupt service routine for channel 0 of Timer_A from butstmc1.c. The state machine lights the LED while the button is pressed.
# pragma vector = T I M E R A 0 _ V E C T O R _ _interrupt void TA0_ISR ( void ) { static enum { LedOff , LedOn } LedState = LedOff ; Monitor = ON ; switch ( LedState ) { case LedOff : if ( Button == ON ) { LED = ON ; LedState = LedOn ; } else { } break ; case LedOn : if ( Button == OFF ) { LED = OFF ; LedState = LedOff ; } break ; // Acknowledged automatically
// State variable , initialized
// // // //
LED currently off Button pressed ? Yes : light LED Change state
// No action needed // // // // LED currently on Button released ? Yes : extinguish LED Change state
www.newnespress.com
Timers
default : LedState = LedOff ; break ; } Monitor = OFF ; } // Should never happen // Reset to initial state
361
I started by defining the state variable LedState. This must be static so that it retains its value between invocations of the function. I chose an enumeration to give helpful names to the states and initialized the state variable. The top level of the state machine is implemented as a switchcase statement, which is very common usage. It is clearer than the alternative sequence of ifelse ifelse . . . statements but is potentially dangerous: It is painfully easy to omit a break statement, in which case execution falls through into the next case. Unfortunately this is perfectly legal C, so the compiler cannot stop you, but the missing break will probably wreck your intended operation. (It is almost the only aspect of C that I would like to change.) Recheck this if your state machine is misbehaving. Within each case of the switch, the code checks the state of the BUTTON input and does the appropriate operations. For example, if the button is pressed in the LedOff state, the LED is turned on and the state is changed to LedOn. The "transitions" that stay in the same state require nothing to be done at all in this system. I include one empty else statement as a reminder of this but it is hardly necessary. Another feature that looks unnecessary is the final default case of the switch. This is a safety feature. The state variable should never take any value other than LedOff and LedOn in normal operation but the default case is a simple way of restoring normal operation if anything should go disastrously wrong and the value of LedState becomes corrupted. It is always wise to include a default case in any switch. The simple program to light an LED when the button was pressed, back in Listing 4.8, needed no state variable. Effectively it kept track of the current state by the position in the program. That worked correctly when the code was in the main function but could not be used in an interrupt service routine, because different actions must be performed each time it is called. The state machine has memory provided by the state variable, which ensures that the correct action is performed whenever the ISR is called. The state machine in Listing 8.18 is effectively polling the input. You might reasonably point out that it would be more efficient to use interrupts for the slow input from a human
www.newnespress.com
362
Chapter 8
operator. This was done using interrupts on digital inputs in Listing 7.1. The ISR has to perform different actions depending on whether the button is pressed or released and this could also be implemented as a simple state machine. It would be an event-driven state machine rather than the periodic state machine that I describe in this section because the machine is called only when an event (interrupt on change) occurs rather than regularly. We see an example of an event-driven state machine in the section "State Machines for IC Communication" on page 559. I describe one more simple example with the push button and LED before moving on to the clock. In this case the LED is toggled on or off when the button is pressed. (This is set as an example in the section "Read Input from a Switch" on page 80.) We need to use similar structures to control the state machine that sets the time of the clock. The LED should be turned on when the button is next pressed, then turned off after the button has been released and pressed again. Now there are four states of the system instead of two because the LED can be either on or off when the button is either up or down. The transition diagram is shown in Figure 8.19. This seems unexpectedly complicated but the structure is needed to enforce the desired sequence of actions. It is similar in principle to the ratchet mechanism of a ballpoint pen with a button to push the writing tip in or out of the body. After the button has been depressed once to turn the LED on, it must not have any further effect until it has been released and pressed again. Thus four states are needed although there are actions associated with only two of the transitions. The other two states are needed purely to control the state machine.
button/light LED !button/ LED off, button up LED on, button down button/
!button/
!button/
button/
LED off, button down
LED on, button up
!button/
button/extinguish LED
Figure 8.19: State transition diagram for an LED controlled by a button. The LED is toggled on or off each time the button is pressed.
www.newnespress.com
Timers Example 8.31 Extend Listing 8.18 to execute this periodic state machine.
363
8.11.2
A State Machine to Set the Time of the Clock
Now we have the tools necessary to construct a state machine to set the time of a clock on the TI MSP430FG4618/F2013 Experimenter's Board. To be specific, I use a clock with the same display as in Listing 8.3. This showed the hours and minutes in 24-hour format and toggled the colon at 1 Hz, which needs interrupts at 2 Hz. These interrupts are provided by Basic Timer1 and I use them to trigger the periodic state machine in the new design. However, I keep track of the time in software rather than using the real-time clock (RTC) module as in Listing 8.3 because the RTC is available in only a few devices at present. It is often a good idea to start with the user's manual for a task like this. This helps to guide the design of the software so that the product is easy to use. I assume that buttons S2 and S1 are labeled Mode and Advance.
Instructions for Setting the Time
1. Hold down the Mode button until the minutes vanish and the hours flash on the display. 2. Hold down the Advance button until the hours display shows the correct time. 3. Hold down the Mode button until the hours vanish and the minutes flash on the display. 4. Hold down the Advance button until the minutes display shows the correct time. 5. Hold down the Mode button until the full display reappears. The clock restarts at the time shown with 0 seconds when the button is released. This description might be enough for the user but we need to make it more precise before designing the software. For example, the Mode button needs to be released between the stages where it is needed so that the successive presses are distinct. It would be easiest if we require the button to be released before the following action is carried out. Thus the user must release the Mode button after it has first been pressed before the hours can be set. Another complication is that there are four possible combinations of inputs from the
www.newnespress.com
364
Chapter 8
two buttons. In many cases we can ignore one of the buttons but the awkward conditions are when the hours or minutes are flashing after the mode button has been released. Three actions are possible here: The time should be advanced if only the Advance button is down. The state machine should move on to the next state if only the Mode button is down. The display should flash if neither button is down. What if both buttons are down? I chose to give the Advance button the higher priority so that the time is advanced in this case. Another issue is that we should presumably allow the user to hold down the advance button more than once to set either the hours or minutes, in case the time had not advanced enough when he or she first pressed it. With these thoughts in mind, I constructed the state transition diagram shown in Figure 8.20. The nine states make it more complicated than the earlier examples but the principles are the same. Let us review its operation: Running: This is the normal state in which the clock runs, flashes the colon, and updates the display every minute. I also made it the initial state, which is questionable--should the user be alerted that the time has not yet been set? If the mode button is pressed, the state machine makes a transition to the Confirm state. The time is updated as normal even if this happens. The Advance button is ignored. Confirm: I include this as a "guard" state in case the user presses the Mode button briefly by accident. Without it, the clock would move into the time-setting state as soon as the Mode button is found to be down when it was polled in the Running state. The button is checked 0.5s later in the Confirm state and the machine returns to Running if the button is now up. The time is advanced so that there is no loss of accuracy. On the other hand, if the button is still down, the process of setting the time starts by erasing the minutes from the display and the machine moves on to WaitHours. You may worry that the Mode button should be checked for a longer time to avoid accidental presses. This could be done by inserting more states into the machine. Alternatively, use a counter to control the flow. WaitHours: The clock waits for the user to release the Mode button before the hours can be set. The display is toggled on and off to show that the hours are ready to be set.
www.newnespress.com
Timers
365
!mode /advance time
Running mode/advance time Confirm
!mode/advance time
mode/erase minutes mode /flash display WaitHours !mode/flash display advance/show display !advance & !mode /flash display FlashHours SetHours advance /advance hours
!advance/ !advance & mode/erase hours, show minutes mode /flash display WaitMinutes !mode/flash display advance/show display !advance & !mode /flash display FlashMinutes SetMinutes advance /advance minutes
!advance/ !advance & mode/show full time, clear seconds mode / WaitFinished !mode/advance time
Figure 8.20: State transition diagram for setting a clock, controlled by the Mode and Advance buttons. FlashHours: This is one of the complicated cases that I mentioned because the actions depend on both the Advance and Mode buttons: If the Advance button is pressed, the display is turned on and the machine moves to the SetHours state. The Mode button is ignored. If the Mode button is down and the Advance button is up, this shows that the hours have been set to the desired value and the machine moves on for
www.newnespress.com
366
Chapter 8 setting the minutes. The hours are erased from the display and the minutes are restored. If neither button is pressed, the display is toggled on and off.
SetHours: The hours are advanced while the Advance button is down; the machine returns to FlashHours when the button is released. The Mode button is ignored. There is no problem with multiple transitions between FlashHours and SetHours. WaitMinutes: This is similar to WaitHours. FlashMinutes: This is similar to FlashHours except that different actions are taken when only the Mode button is pressed. Now the time has been set completely so both hours and minutes are displayed. The counter for seconds (actually half seconds) is cleared so that a complete minute is taken when the clock resumes. SetMinutes: This is similar to SetHours. WaitFinished: The clock waits for the user to release the Mode button, after which it resumes normal timing. Listing 8.19 shows the interrupt service routine to run the state machine. I put all the actions into functions, even the trivial single-line ones, to avoid cluttering the picture. The overall structure is a large switch and the action within each case depends on the inputs from the buttons. The close correspondence between the diagram and the code makes state machines easy to program, which is another valuable feature. In fact there are tools to automate the process and IAR offer visualSTATE as a companion to EW430, for example. Listing 8.19: State machine in setclk1.c to control both the normal timekeeping and setting of a clock.
# pragma vector = B A S I C T I M E R _ V E C T O R _ _interrupt void B A S I C T I M E R _ I S R ( void ) { static enum { Running , Confirm , WaitHours , FlashHours , SetHours , WaitMinutes , FlashMinutes , SetMinutes , WaitFinished } ClockState = Running ; // Acknowledged automatically // // // // // // // // // // Normal operation of clock Pause : mode butn still down ? Wait for release of mode butn Flash settable hours display Advance hours to set time Wait for release of mode butn Flash settable mins display Advance minutes to set time Wait for release of mode butn State variable
www.newnespress.com
Timers
switch ( ClockState ) { case Running : AdvanceTime (); if ( Mode == DOWN ) { ClockState = Confirm ; } break ; case Confirm : if ( Mode == DOWN ) { EraseMinutes (); ClockState = WaitHours ; } else { AdvanceTime (); ClockState = Running ; } break ; case WaitHours : T o g g l e D i s p l a y (); if ( Mode == UP ) { ClockState = FlashHours ; } break ; case FlashHours : if ( Advance == DOWN ) { A c t i v a t e D i s p l a y (); ClockState = SetHours ; } else if ( Mode == DOWN ) { ShowMinutes (); EraseHours (); A c t i v a t e D i s p l a y (); ClockState = WaitMinutes ; } else { T o g g l e D i s p l a y (); } break ; case SetHours : if ( Advance == DOWN ) { AdvanceHours (); } else { ClockState = FlashHours ; } break ; case WaitMinutes : T o g g l e D i s p l a y (); if ( Mode == UP ) { ClockState = FlashMinutes ; } break ; case FlashMinutes : if ( Advance == DOWN ) { A c t i v a t e D i s p l a y (); ClockState = SetMinutes ; } else if ( Mode == DOWN ) { ShowHours (); A c t i v a t e D i s p l a y (); ClearSeconds (); ClockState = WaitFinished ;
367
// // // //
Always update time Mode button pressed ? Yes : Prepare to set time No : Continue running clock
// Mode button still pressed ? // Yes : Erase display for mins // Prepare to set hours // No : Update time as usual // Return to timekeeping
// // // // // // // // // // // // // //
Flash display ( hours only ) Mode button released ? Yes : Proceed to setting hours No : Continue flashing display Advance button pressed ? Yes : Ensure display is visible Proceed to advance hours No : Mode button pressed ? Yes : Show minutes Erase display for hours Ensure display is visible Prepare to set minutes No : Neither button pressed Flash hours display
// Advance button pressed ? // Yes : Advance hours // No : Return to flash hours
// // // // // // // // // // // //
Flash display ( mins only ) Mode button released ? Yes : Proceed to setting mins No : Continue flashing display Advance button pressed ? Yes : Ensure display is visible Go to advance mins No : Mode button pressed ? Yes : Restore hours to display Ensure display is visible To give clean restart from 0 s Prepare to finish setting
www.newnespress.com
368
Chapter 8
} else { T o g g l e D i s p l a y (); } break ; case SetMinutes : if ( Advance == DOWN ) { A d v a n c e M i n u t e s (); } else { ClockState = FlashMinutes ; } break ; case WaitFinished : if ( Mode == UP ) { AdvanceTime (); ClockState = Running ; } break ; default : ClockState = Running ; break ; } // No : Neither button pressed // Flash minutes display
// Advance button pressed ? // Yes : Advance minutes // No : Return to flash minutes
// // // //
Mode button released ? Yes : Restart update of time Return to running state No : Do nothing
// Should never happen // Emergency recovery
}
This program works satisfactorily although it is rather tedious to update the minutes at only 2 Hz. Many products speed up the rate of change after a few seconds and that could be done here. Another annoyance is that the state machine misses the input from a button if it is pressed for less than 0.5s between calls of the state machine. I am sure that you can think of other features that could be improved. Example 8.32 Suppose that only one button is available to set the clock. Modify the state transition diagram in Figure 8.20 to provide a possible solution.
Checklist--Have You . . .
Put a break statement after each case of a switch in a state machine?
www.newnespress.com
Find millions of documents on Course Hero - Study Guides, Lecture Notes, Reference Materials, Practice Exams and more.
Course Hero has millions of course specific materials providing students with the best way to expand
their education.
Below is a small sample set of documents:
BYU - CS - 124
Mixed-Signal Systems: Analog Input and Output393Schmitt trigger and interrupts on standard digital inputs can be used for the measurement instead of a comparator.9.2Analog-to-Digital Conversion: General IssuesAlthough Comparator_A produces digital va
BYU - CS - 124
Mixed-Signal Systems: Analog Input and Output Example 9.12475Modify Listing 9.12 so that the intensity of the LED responds to the ambient light in the opposite way: The LED gets brighter as the surroundings become darker. It is a lot easier to test this
BYU - CS - 124
534Chapter 10transmission. The results were strange because of the problems that I mentioned in the section "Digital Input and Output: Parallel Ports" on page 208. Enabling the pull resistor removes the full drive from the pin, so the sharp edges in Fig
BYU - CS - 124
Chapter 10Watchdog Timer+The watchdog timer+ (WDT+) is a 16-bit timer that can be used as a watchdog or as an interval timer. This chapter describes the WDT+ The WDT+ is implemented in all MSP430x2xx devices.TopicPage10.1 Watchdog Timer+ Introduction
BYU - CS - 124
Negative Numbers and Binary SubtractionWe have seen how simple logic gates can perform the process of binary addition. It is only logical to assume that a similar circuit could perform binary subtraction. If we look at the possibilites involved in subtra
BYU - CS - 124
Chapter 3RISC 16 Bit CPUThis chapter describes the MSP430 CPU, addressing modes, and instruction set.Topic3.1 3.2 3.3 3.4PageCPU Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-2 CPU Registers
BYU - CS - 124
The 1-to-2 Line Decoder/DemultiplexerThe opposite of the multiplexer circuit, logically enough, is the demultiplexer. This circuit takes a single data input and one or more address inputs, and selects which of multiple outputs will receive the input sign
BYU - CS - 124
The Two-Input MultiplexerOne circuit I've received a number of requests for is the multiplexer circuit. This is a digital circuit with multiple signal inputs, one of which is selected by separate address inputs to be sent to the single output. It's not e
BYU - CS - 124
Prefix b c l n p w dw f fn fp t g u Type BYTE charUsage Signed 8-bit value Signed 8-bit value (usually an ASCII character) long Signed 32-bit value int Signed 16-bit value U16 : pointer Generic prefix for a pointer to the next type U16 : WORD Unsigned
BYU - CS - 124
Chapter 5 MSP430 ISA The Instruction SetTopics to Cover.n n n n n n n nMSP430 ISA Instruction Formats Double Operand Instructions Single Operand Instructions Jump Instructions Addressing Modes Instruction Disassembly Emulated InstructionsBYU CS/ECEnC
BYU - CS - 124
Clock accuracy in ppmCrystal Clock accuracy is defined in terms of ppm or parts per million and it gives a convenient way of comparing accuracies of different crystal specifications. . The following headings give practical calculations showing the typica
BYU - CS - 124
CS/ECEn 124, W2012 Homework #1 Abstraction (Ch 2)Questions:NameSectionScore/ 41Answers:1. (3 points) Can a higher level programming language instruct a computer to compute more than a lower level programming language? Explain.2. (3 points) Name th
BYU - CS - 124
CS/ECEn 124, W2012 Homework #2 Digital Logic (Ch 3)Questions:NameSectionScore/ 38Answers:1. (4 points) How many select lines and how many output lines do the following multiplexers have? a. b. c. d. 32-input multiplexer 16-input multiplexer 5-input
BYU - CS - 124
CS/ECEn 124, W2012 Homework #3 Digital Logic (Ch 3)Questions:NameSectionScore/46Answers:1. (10 points) Identify the type of logic (combinational or sequential) for each of the following: a. ALU b. D-latch c. Decoder d. Driver e. Flip-Flop f. Invert
BYU - CS - 124
CS/ECEn 124, W2012 Homework #4 Von Neumann (Ch 4) MSP430 ISA (Ch 5)Questions:NameSectionScore/ 47Answers:1. (11 points) For each statement below, indicate if a CISC or RISC architecture is best described: a. b. c. d. e. f. g. h. i. j. k. Cheaper pr
BYU - CS - 124
CS/ECEn 124, W2012 Homework #7 Stacks (Ch 8) Interrupts (9)Questions:NameSectionScore/ 36Answers:1. (3 points) Define subroutine cohesion. What properties of cohesion should be found in your subroutines?2. (3 points) Define subroutine coupling. Wh
BYU - CS - 124
An introduction to the TI MSP430 low-power microcontrollers OverviewThe MSP430 is a very clean 16-bit byte-addressed processor with a 64K unified address space, and memorymapped peripherals. The current family includes a variety of on-chip peripherals, a
BYU - CS - 124
AN-1023APPLICATION NOTEOne Technology Way P.O. Box 9106 Norwood, MA 02062-9106, U.S.A. Tel: 781.329.4700 Fax: 781.461.3113 www.analog.comFall Detection Application by Using 3-Axis Accelerometer ADXL345by Ning JiaINTRODUCTIONADXL345 MEMS ACCELEROMETE
BYU - CS - 124
AN-1025APPLICATION NOTEOne Technology Way P.O. Box 9106 Norwood, MA 02062-9106, U.S.A. Tel: 781.329.4700 Fax: 781.461.3113 www.analog.comUtilization of the First In, First Out (FIFO) Buffer in Analog Devices, Inc.Digital Accelerometersby Christopher
BYU - CS - 124
3-Axis, 2 g/4 g/8 g/16 g Digital Accelerometer ADXL345FEATURESUltralow power: as low as 40 A in measurement mode and 0.1 A in standby mode at VS = 2.5 V (typical) Power consumption scales automatically with bandwidth User-selectable resolution Fixed 10-
BYU - CS - 124
AccelerometersFantasy & RealityBy Harvey Weinberg [harvey.weinberg@analog.com]As applications engineers supporting ADIs compact, low-cost, gravity-sensitive iMEMsaccelerometers, we get to hear lots of creative ideas about how to employ accelerometers i
BYU - CS - 124
Adding Binary NumbersA key requirement of digital computers is the ability to use logical functions toperform arithmetic operations. The basis of this is addition; if we can add twobinary numbers, we can just as easily subtract them, or get a little fa
BYU - CS - 124
ASCII Chart012345670NULDLESP0@P`p1SOHDC1!1AQaq2STXDC2"2BRbr3ETXDC3#3CScsOr here's another chart:4EOTDC4$4DTdt5ENQNAK%5EUeu6ACKSYN&6FVfv7BELETB'7GWgw8BSCAN(8HXhx9HT
BYU - CS - 124
Basic Logical Functions andGatesWhile each logical element or condition must always have a logic value ofeither "0" or "1", we also need to have ways to combine different logical signalsor conditions to provide a logical result.For example, consider
BYU - CS - 124
Boolean AlgebraOne of the primary requirements when dealing with digital circuits is to findways to make them as simple as possible. This constantly requires that complexlogical expressions be reduced to simpler expressions that nevertheless produceth
BYU - CS - 124
PrefixbclnpwdwffnfptguTypeBYTEcharUsageSigned 8-bit valueSigned 8-bit value (usually anASCII character)longSigned 32-bit valueintSigned 16-bit valueU16 : pointerGeneric prefix for a pointer to thenext typeU16 : WORDUnsigned 16
BYU - CS - 124
Node:Top, Next:Preface, Previous:(dir), Up:(dir)C Programming Tutorial (K&R version 4)This is a C Programming Tutorial for people who have a little experience with aninterpreted programming language, such as Emacs Lisp or a GNU shell.Edition 4.02Copy
BYU - CS - 124
Formatting with printfThe example program above does not produce a very neat layout on the screen. Theconversion specifiers in the printf string can be extended to give more information.The % and the character type act like brackets around the extra in
BYU - CS - 124
Arrays as ParametersWhat happens if we want to pass an array as a parameter? Does the program copy theentire array into local storage? The answer is no because it would be a waste of timeand memory. Arrays can be passed as parameters, but only as varia
BYU - CS - 124
feof()This function returns a true or false result. It tests whether or not the end of a file hasbeen reached and if it has it returns `true' (which has any value except zero); otherwisethe function returns `false' (which has the value zero). The form
BYU - CS - 124
Listing Cref.c123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354/*//**//* C programming utility : variable referencer*//**//*//* See notes abov
BYU - CS - 124
Clock accuracy in ppmCrystal Clock accuracy is defined in terms of ppm or parts per million and it gives a convenient way ofcomparing accuracies of different crystal specifications..The following headings give practical calculations showing the typica
BYU - CS - 124
/- CRC functions -/u_long crc32_table[256];/* Initialized first time "crc32()" is called. If you prefer, you can* statically initialize it at compile time. [Another exercise.]*/u_long crc32(u_char *buf, int len)cfw_u_char *p;u_long crc;if (!crc32
BYU - CS - 124
INTRODUCTION TO COMPUTING SYSTEMSCS/ECEn 124 Course SyllabusWinter 2012Instructor: Paul RoperOffice: 3370 TMCB, 422-8149Office Hours: MWF 9:00-10:50 AMEmail: proper@cs.byu.eduSection 001Section 002Help SessionsMWF 1:00-1:50 PMMWF 3:00-3:50 PMM
BYU - CS - 124
A Guide to DebouncingAugust 2004Rev 1: April, 2006Rev 2: April, 2007Rev 3: June, 2008Jack G. Gansslejack@ganssle.comThe Ganssle GroupPO Box 38346Baltimore, MD 21231(410) 504-6660fax (647) 439-1454 2004 The Ganssle Group. This work may be used
BYU - CS - 124
Derived Logical Functions andGatesWhile the three basic functions AND, OR, and NOT are sufficient toaccomplish all possible logical functions and operations, some combinations areused so commonly that they have been given names and logic symbols of th
BYU - CS - 124
Deriving the XOR FunctionOn the previous page we stated that the Exclusive-OR, or XOR function can bedescribed verbally as, "Either A or B, but not both." In the realm of digital logicthere are several ways of stating this in a more detailed and precis
BYU - CS - 124
Digital LogicDigital or binary logic has fascinated many people over the years. The very idea that atwo-valued number system can possibly be the basis for the most powerful andsophisticated computers seems astounding, to say the least. Nevertheless, it
BYU - CS - 124
The replacement parts/accessories for the eZ430X Development Board are available at the BYU ECEn StockRoom (416 CB) as follows:DescriptionLCD DisplayServoTransponderJumperThermistorDevelopment BoardSound ModulePartLCD - NHD-C160100DiZ-FSW-FBWT
BYU - CS - 124
MSP-FET430 Flash Emulation Tool (FET) (for Use With Code Composer Essentials for MSP430 Version 3.1)User's GuideLiterature Number: SLAU157I May 2005 Revised February 20092SLAU157I May 2005 Revised February 2009 Submit Documentation FeedbackContentsP
BYU - CS - 124
FM24CL6464Kb Serial 3V F-RAM MemoryFeatures64K bit Ferroelectric Nonvolatile RAM Organized as 8,192 x 8 bits Unlimited Read/Write Cycles 45 year Data Retention NoDelay Writes Advanced High-Reliability Ferroelectric ProcessFast Two-wire Serial Int
BYU - CS - 124
The Function Pointer TutorialsIntroduction to C and C+ Function Pointers, Callbacks and Functorswritten by Lars HaendelJanuary 2005, Bochum, Germanyhttp:/www.newty.deemail: Have a look at the web page pleaseversion 2.07Copyright (c) 2000-2005 by La
BYU - CS - 124
REV C (February 22, 2010)Secondary (L)HDEFIIIOPin SignalSW_2ADXL345 INT1/2SW_4LCD BacklightPrimary (R)P2.0 (ACLK/A0)P2.1 (SMCLK/A1)P2.2 (TA0/A2)P2.3 (TA1/A3)P2.4 (TA2/A4)P2.6P2.7XIN - GDO0 (O)XOUT - GDO2 (O)IIIOOOOP3.0 (UCB
BYU - CS - 124
Hex File FormatsThis appendix describes the Intel hex object format and the TI-txt file format.The Intel hex object format supports 16-bit addresses and consists of a 9-character (4-field) prefix that defines the start ofrecord, byte count, load addres
BYU - CS - 124
How to Access FRAM.The FM24CL64 is a 64-kilobit nonvolatile memory employing an advancedferroelectric process. A ferroelectric random access memory or F-RAM is nonvolatileand performs reads and writes like a RAM. It provides reliable data retention for
BYU - CS - 124
How to Change Clock Speed.The MSP430 addresses the conflicting demands for high performance, lowpower, and a precise frequency by using three internal clocks, which can bederived from up to four sources. These are the internal clocks, which are thesam
BYU - CS - 124
How To Change The Processor Clock.The MSP430 was designed to power-up quickly from low-power mode withouthaving to wait a long time for the clock to settle. The Digitally ControlledOscillator (DCO) of the MSP430F2274 is an accurate and stable clock tha
BYU - CS - 124
How to Condition an Analog Signal.An analog-to-digital converter (abbreviated ADC, A/D or A to D) is a device whichconverts continuous analog signals to discrete digital numbers. Due to the finiteresolution and the unavoidable imperfections in all type
BYU - CS - 124
How To Control a Servo.What is a Servo?A servo is a small device that has a rotating output shaft. This shaft can bepositioned to specific angular positions by sending the servo a coded signal.As long as the coded signal exists on the input line, the
BYU - CS - 124
How To Debounce A Switch.A switch is a mechanical device that "opens" and "closes" a circuit. Theswitches on the eZ430X board are normally open, letting the inputs be pulledhigh (a logic one) by the internal MSP430 port pull-up resistors. A closed ord
BYU - CS - 124
How to Determine the Length of a String.A C string can be created from a binary data type by using the sprintf() function. Byincluding the standard C I/O library ("#include <stdio.h>" and defining a characterarray of sufficient size for the string, the
BYU - CS - 124
How to Draw a Continuous Line.A continuous line is drawn if neither the delta x nor delta y is greater than 1 pixel. Inthe following example, the x value always increments by 1 while the y value onlydecrements by 1 when needed to conform to the "ideal"
BYU - CS - 124
How to Draw Images and Text on theLCD.The New Haven NHD-C160100DiZ-FSW-FBW LCM (Liquid Crystal DisplayGraphic Module) is a 160 column x 100 row, 16-level dot, reflective, LiquidCrystal Display with a side white LED backlight. Display data and commands
BYU - CS - 124
How to Draw Various Shapes.Draw a circle on the LCD by calling:void lcd_circle(int x0, int y0, int r0, int pen);wherex0, y0 = coordinates of the center of the circler0 = radius of circlepen = pen value (0 = erase, 1 = single line)Example:lcd_circl
BYU - CS - 124
How To Generate A Random Number.Use the following routines found in random.asm to initialize, set, and getrandom numbers:FunctioninitRand16rand16setRandSeedgetRandSeedParametersIN: r12 = random seedOUT: r12 = random number (0-32767)IN: r12 = ra
BYU - CS - 124
How To Implement An Event Driven ProgrammingModel.In your introductory programming courses and our labs thus far, you mostlyused the basic program structures of sequential, conditional, and iterativecode associated with a procedural program. If you lo
BYU - CS - 124
How To Interface With eZ430XLibraries.eZ430X Librariesadc.h, adc.co int ADC_init(void);o int ADC_read(int channel);adxl345.h, adxl345.co int ADXL345_init(void);o unsigned char ADXL345_read(unsigned char regaddr, unsignedchar *buf, int count);o v
BYU - CS - 124
How To Link Programs.Part of systemic decomposition usually involves dividing a program intoseparate files. Smaller files are easier to maintain and support ObjectOriented Programming (OOP) by "hiding" or encapsulating data. The problemof coming up wi
BYU - CS - 124
How To Make Subroutines Callee-Save.Any register used by a subroutine MUST be saved and restored from thestack! The following routine illustrates this procedure:;-; SUBROUTINE: GENERATES NEXT RANDOM NUMBER;;OUT:r12 = 0-32767;random seed updated;
BYU - CS - 124
How To Setup MSP430 Ports.The most straightforward form of input/output of the MSP430 is through thedigital input/output ports. The following table summarizes the control registersfor ports P1, P2, P3, and P4 of the MSP430F2274:PxDIR Port Px Direction
BYU - CS - 124
How to Use C Struct's.Tidy programs are a blessing to programmers. Tidy data are just as important. Asprograms become increasingly complex, their data also grow in complexity andsingle, independent variables or arrays are no longer enough. What one the