(blah)
Introduction
asdfInstallation
We need to (1) install the drivers for RF Access Point and eZ430 Emulator, and (2) software to interface with the default program running on the Chronos watch.Chronos Software and Drivers
Code Composer Studio v4 Core Edition
Using eZ430-Chronos Control application
Acceleration data
asdfControl PowerPoint slides
asdfSync the time with PC
adsfProgram Chronos watch wirelessly
adsfTwo paths to start learning
- Programming the watch
- Developing programs on the computer to use the watch's data
Developing Programs on the computer
adfProgramming the watch
I'm going through the book "MSP430 Microcontroller Basics" by John Davies to learn how the details of programming (in C) the MSP430. (TODO)The chronos watch breakdown:
asdf
First program
(Refer to Chapter 4.2)The first program to get used to programming the watch is to turn on the LCD backlight. This can be accomplished by setting P2.3 to high.
Caution: The P2.3 is also hardwired to KEY_BL button. I don't think LCD backlight was meant to be toggled by the program because pressing the button at the same time the microcontroller is trying to pull the pin low might cause a short?? (Need to check!)
Better safe that sorry (for now): Since the watch is disassembled from the strap (and the button interface), we wouldn't be pressing the KEY_BL by accident.
You won't be able to see the backlight too clearly unless you look from the side because the LCD display is still not turned on; we're just enabling the backlight behind it.
// first_program.c
// Turn on the backlight
#include <cc430x613x.h>
#define INITSTATE 0
#define BACKLIGHT BIT3
void main(void){
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
P2OUT = INITSTATE; //May not be necessary? (TODO)
P2DIR = BACKLIGHT; //Set (only the) backlight port as output
P2OUT = BACKLIGHT; //Set P2.3 High
while(1);
//__no_operation(); //TODO: Find out what this macro does
}
First program Updated
Split up the program into .c and .h fileCreate a skeleton so that things stay organized when code gets complicated
Header file:
// backlight.h
// Header file for the updated first program
#ifndef MAIN_H_
#define MAIN_H_
#define INITSTATE 0
#define BACKLIGHT BIT3
//Any debug flags
//Prototypes
void init_variables(void);
void init_configure(void);
#endif /*MAIN_H_*/
Source file:
#include <cc430x613x.h>
#include "main.h"
void main(void)
{
init_variables();
init_configure();
//Turn on the backlight
P2OUT = BACKLIGHT;
while(1)
;
}
void init_variables(void){
}
void init_configure(void){
/* Initialize the stack pointer - done by cstartup.h */
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
P2OUT = INITSTATE; //May not be necessary? (TODO)
P2DIR = BACKLIGHT; //Set (only the) backlight port as output
/* Set up the clock - N/A */
/* Configure other peripherals - N/A */
}
Reading Input
(Refer to Chapter 4.4)Now that we know how to write to an LED, let's try reading from the switch and toggle the LED.
Following is the schematics for the buttons on the Chronos watch.
- We have used P2.3, connected to KEY_BL, as an output to enable/disable the LED backlight.
- Let's use P2.0, connected to KEY_S2 ("Submenu 2" button a.k.a "down-arrow" button), as an input to toggle the LED backlight.
Prerequisites
I am assuming that when the button is pressed, it pulls the pin to Vcc. (TODO: Check. Because I do not see any information for what KEY_S2 connects to!)- The assumption is due to the fact that pressing the KEY_BL enables the backlight; and in our first program, setting P2.3 to High enables the backlight.
Registers for configuring pins
PxIN: port input
Read the values on the pin. Works as long as ports are configured as Digital input/output
PxOUT: port output
A buffer to store the value to be driven to each pin if it's configured as output. Needs to be initialized before pin is configured as output.
PxSEL: port selection
If 0 (default), pin is selected for digital input/output. Otherwise, it's selected for alternative function (eg. Analog input)
PxDIR: port direction
When selected as digital in/output (i.e. PxSEL = 0):
if bit = 0 -> configured as input. If bit = 1 -> configured as output.
PxREN: port resistor Enable
If bit = 1, pull-up/down resistors are activated. 0 is selected by default.
When enabled:
if PxOUT = 1, resistor is pull-up (connected to Vcc). Else resistor is pull-down.
CAUTION: The above configurations can be overruled in some cases by other registers/functions (Page 209)
Masks
To set a bit to 1: (PORT) |= (BIT)
To set a bit to 0: (PORT) &= ~(BIT)
To toggle a bit: (PORT) ^= (BIT)
To configure P2.3 as output:
- Initialize output buffer: P2OUT -> BIT3 to 0. [P2OUT &= ~BIT3]
- Configure pin as output: P2DIR -> BIT3 to 1. [P2DIR |= BIT3]
- [page 215] leaving the others as inputs and leaving them unwired or with no pull-up/down resistors is bad (floating inputs). But whateverrrrrrrrrrrrr... for this example.
- Configure pin as input: P2DIR -> BIT0 to 0 [P2DIR &= ~BIT0]
- Enable internal pull-down resistors:
- P2OUT -> BIT0 to 0. [P2OUT &= ~BIT0]
- P2REN -> BIT0 to 1. [P2REN |= BIT0]
Single Polling method
- Start CCSv4.
- Select a Workspace. This is the folder where CCS stores all your projects. I try to keep one workspace for every chapter I will going through.
- File -> New -> CCS Project..
- Enter desired name for your project
- Select platform as MSP430 and Configurations for 'Debug' and 'Release'. Choose Next.
- Do not select any inter-project dependencies under 'Projects'. Leave the 'C/C++ Indexer' at 'Full C/C++ Indexer'. Choose Next.
- Select the following CCS Project settings. Particularly:
- Device Variant: CC430F6137
- Select Finish to create the new project. This is what the window should look like.
- Right-click on the Project in the C/C++ Projects window -> New -> Source file
- Leave the 'Source Folder' the same (led-toggle-button in this case) and Enter name for the source file (led-button.c). Choose Finish.
- Also create the complementary header file in a similar fashion.
- Right-click on the Project -> New -> Header file
- Enter name for the header file (led-button.h). Choose Finish
- Enter the following code for the source file.
//led-button.c
//Toggle LED backlight depending on whether "down-arrow" button is pressed
#include <cc430x613x.h>
#include "led-button.h"
void main(void){
init_variables();
init_configure();
while(1){
if((P2IN & BL_BUTTON) == BL_BUTTON) { //if button pin == 1
P2OUT |= BACKLIGHT; //turn on LED backlight
} else {
P2OUT &= ~BACKLIGHT; //turn off LED backlight
}
}
}
void init_variables(void){
}
void init_configure(void){
/* Initialize the stack pointer - done by cstartup.h */
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
//configure P2.3 as output
P2OUT &= ~BACKLIGHT; //Initialize output buffer: P2OUT -> BIT3 to 0.
P2DIR |= BACKLIGHT; //Configure pin as output: P2DIR -> BIT3 to 1.
//configure P2.0 as input
P2DIR &= ~BL_BUTTON; //Configure pin as input: P2DIR -> BIT0 to 0.
P2OUT &= ~BL_BUTTON; //Enable internal pull-down resistors: P2OUT -> BIT0 to 0.
P2REN |= BL_BUTTON; //P2REN -> BIT0 to 1
/* Set up the clock - N/A */
/* Configure other peripherals - N/A */
} - Enter the following for the header file.
//led-button.h
#ifndef LEDBUTTON_H_
#define LEDBUTTON_H_
#define INITSTATE 0
#define BACKLIGHT BIT3
#define BL_BUTTON BIT0
//Any debug flags
//Prototypes
void init_variables(void);
void init_configure(void);
#endif /*LEDBUTTON_H_*/ - Choose Project->Build Active Project (Ctrl+Shift+P)
- The project should complete build without any errors. The 'Console' window should show the following message:
- Any errors would be displayed in the 'Problems' window
- Connect the Chronos watch for debugging via the 'eZ430 Emulator for programming'. (Instructions below)
- To program the watch, Select Target -> Debug Active Project or click on the Debug button on the Toolbar. (Looks like a Green bug)
- (Explain the programming process after Debug is selected)
- The debugger runs any startup code and halts at the 1st line in the main() function. Remember that the 1st line hasn't been executed yet.
- We can Run the program and pause at anytime. Or we can 'Step Over..' each line of code
- Watch the results on the Chronos watch.
- Since we cannot select any buttons when the watch is disassembled from the watch strap, we either need to:
- unplug the watch and assemble it into the strap and use the button, or
- emulate the button press by changing the value in the Debugger.(TODO: Wasn't able to change the P2IN register. Probably because it's volatile)
Double Polling Method
The difference between the last method and this is that the LED backlight state isn't being set everytime around the loop. A nested while loop holds the program till the button is pressed. After setting the LED backlight on, another nested while loop holds the program till the button is released.The single polling method code is also preserved in this program. A debug flag SINGLELOOP is set in the header file when I want to use the single polling method instead of the double polling method.
Source file:
//led-button-doublePoll.c
//Toggle LED backlight depending on whether "down-arrow" button is pressed
#include <cc430x613x.h>
#include "led-button.h"
void main(void){
init_variables();
init_configure();
#ifdef SINGLELOOP
while(1){
if((P2IN & BL_BUTTON) == BL_BUTTON) { //if button pin == 1
P2OUT |= BACKLIGHT; //turn on LED backlight
} else {
P2OUT &= ~BACKLIGHT; //turn off LED backlight
}
}
#else
while(1){
//Hold while button is not pressed
while((P2IN & BL_BUTTON) == 0); //button pin == 0
//turn on LED backlight
P2OUT |= BACKLIGHT;
//Hold while button is pressed
while((P2IN & BL_BUTTON) == BL_BUTTOn); //button pin == 0
//turn off LED backlight
P2OUT &= ~BACKLIGHT;
}
#endif
}
void init_variables(void){
}
void init_configure(void){
/* Initialize the stack pointer - done by cstartup.h */
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
//configure P2.3 as output
P2OUT &= ~BACKLIGHT; //Initialize output buffer: P2OUT -> BIT3 to 0.
P2DIR |= BACKLIGHT; //Configure pin as output: P2DIR -> BIT3 to 1.
//configure P2.0 as input
P2DIR &= ~BL_BUTTON; //Configure pin as input: P2DIR -> BIT0 to 0.
P2OUT &= ~BL_BUTTON; //Enable internal pull-down resistors: P2OUT -> BIT0 to 0.
P2REN |= BL_BUTTON; //P2REN -> BIT0 to 1
/* Set up the clock - N/A */
/* Configure other peripherals - N/A */
}
Header file:
//led-button.h
#ifndef LEDBUTTON_H_
#define LEDBUTTON_H_
#define INITSTATE 0
#define BACKLIGHT BIT3
#define BL_BUTTON BIT0
//Any debug flags
//#define SINGLELOOP
//Prototypes
void init_variables(void);
void init_configure(void);
#endif /*LEDBUTTON_H_*/
Toggling LED
(Refer to section 4.5) Toggle the LED backlight using a software loop.Source:
//loops-software.c
//Toggle LED backlight in a software loop
#include <cc430x613x.h>
#include "loops.h"
volatile unsigned int counter;
void main(void){
init_variables();
init_configure();
while(1){
for(counter = 0; counter<DELAY; counter++); //Delay for "a while"
P2OUT ^= BACKLIGHT; //toggle the state of backlight
}
}
void init_variables(void){
counter = 0;
}
void init_configure(void){
/* Initialize the stack pointer - done by cstartup.h */
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
//configure BACKLIGHT pin as output
P2OUT &= ~BACKLIGHT; //Initialize output buffer: P2OUT -> BIT3 to 0.
P2DIR |= BACKLIGHT; //Configure pin as output: P2DIR -> BIT3 to 1.
/* Set up the clock - N/A */
/* Configure other peripherals - N/A */
}
Header:
#ifndef LOOPS_H_
#define LOOPS_H_
#define INITSTATE 0
#define BACKLIGHT BIT3
#define DELAY 50000
//Any debug flags
//Prototypes
void init_variables(void);
void init_configure(void);
#endif /*LOOPS_H_*/
Interrupts
(From section 6.6) Learning how to configure interrupts on the msp430.Prerequisites
ISR
Interrupt Handler/Interrupt Service Routine: Code to handle the interrupt
Flag
Each interrupt has a flag that is set when the condition for the interrupt occurs
Each flag has an enable bit: Allows module to request interrupts
Interrupt Enable bit (GIE): In the status register (SR)
It disables maskable interrupts when GIE = 0
Vectored Interrupts
Address of each ISR is stored in the vector table at a defined location in memory
(Diagram) Table of Interrupt Vector Addresses
Priority
Each of these vectors have a priority associated with them.
Hardwired, so cannot be changed.
Shared vs. Unique
ISRs could be shared or unique.
If Unique: Before executing the ISR, the interrupt request flag is cleared automatically.
If Shared: it remains set for the software to clear it (This way the ISR can check which condition caused this shared ISR)
Some of the code is compiler dependent
For IAR Embedded Workbench:
Tells the compiler that this function is an ISR (For example: It does a reti call rather than ret in assembly)
For Code Composer Studio v4: (Refer to SLAU132)
We can still use the same functions as above because the header file in430.h (which is already called by the cc...file) converts the calls
Important registers
Registers PxIN, PxOUT, PxSEL, PxDIR, PxREN are already mentioned earlier.
Additional registers needed for configuring interrupts:
PxIE: Interrupt Enable
Setting appropriate bits to 1 enables the interrupts on that pin. They are 0 by default
Whole port shares a single interrupt vector
PxIES: Interrupt edge select
0: generate on positive edge
1: generate on negative edge
Needs to be initialized before interrupts are enabled
PxIFG: Interrupt flag
Appropriate bit is set when the interrupt happens.
Can also be set by software for generating software interrupts
Interrupts for port P2 is controlled by the registers P2IE and P2IES
Debouncing
When the button is pressed, the actual input voltage goes through several transitions from low to high. Since we cannot make any hardware modifications, we can implement debouncing in software. Options:
Source:
Header:
[From page 289]
SMCLK
WDTCTL: WDT Control register (16-bits)
upper byte:
For a proper write to WDTCTL, we need to write password WDTPW (0x5A)
Reading the upper byte gives us 0x69 - for protection
reset will occur if wrong password is written - Can be used for forcing a reset by software
lower byte:
Contains bits that control operation of WDT
Bits are reset to 0 upon POR but unaffected by PUC.
WDTCNT: watchdog counter (16-bits)
Not accessible by user
After initial reset, the default clock is SMCLK, derived from DCO, is 1 MHz.
This gives us 32ms before WDT causes a reset. Need to either clear, stop or reconfigure.
Examples
To stop WDT:
To clear WDT: (The bit is cleared after every reset; therefore need to be set again)
Timer_A - General Description
This is the general purpose timer on MSP430.
TI-Chronos has two of these timers, TA0 and TA1
Consists of two main hardware parts:
Timer Block
Interrupt Handler/Interrupt Service Routine: Code to handle the interrupt
Flag
Each interrupt has a flag that is set when the condition for the interrupt occurs
- Example: Timer_A sets the TAIFG flag in the TACTL register when the counter TAR returns to 0.
Each flag has an enable bit: Allows module to request interrupts
- Example: For Timer_A: TAIE
Interrupt Enable bit (GIE): In the status register (SR)
It disables maskable interrupts when GIE = 0
Vectored Interrupts
Address of each ISR is stored in the vector table at a defined location in memory
(Diagram) Table of Interrupt Vector Addresses
Priority
Each of these vectors have a priority associated with them.
Hardwired, so cannot be changed.
Shared vs. Unique
ISRs could be shared or unique.
If Unique: Before executing the ISR, the interrupt request flag is cleared automatically.
If Shared: it remains set for the software to clear it (This way the ISR can check which condition caused this shared ISR)
Intrinsics.h
ISRs in C need intrinsic functions to set up the interrupts properly.Some of the code is compiler dependent
For IAR Embedded Workbench:
- __interrupt
Tells the compiler that this function is an ISR (For example: It does a reti call rather than ret in assembly)
- #pragma vector = ***
//example
# pragma vector = TIMERA0_VECTOR
__interrupt void TA0_ISR(void)
{
P2OUT ˆ= LED1|LED2; // Toggle LEDs
}
- __enable_interrupt()
For Code Composer Studio v4: (Refer to SLAU132)
We can still use the same functions as above because the header file in430.h (which is already called by the cc...file) converts the calls
- __interrupt -> interrupt
//example
interrupt void int_handler()
{
unsigned int flags;
...
}
- #pragma vector = ***
- _enable_interrupt()
Reading Input Revisited
(Refer to chapter 7.2 and 7.3) Repeat the example where the LED backlight is toggled by the down-arrow button. We will use interrupts to process the input rather than polling.Important registers
Registers PxIN, PxOUT, PxSEL, PxDIR, PxREN are already mentioned earlier.
Additional registers needed for configuring interrupts:
PxIE: Interrupt Enable
Setting appropriate bits to 1 enables the interrupts on that pin. They are 0 by default
Whole port shares a single interrupt vector
PxIES: Interrupt edge select
0: generate on positive edge
1: generate on negative edge
Needs to be initialized before interrupts are enabled
PxIFG: Interrupt flag
Appropriate bit is set when the interrupt happens.
Can also be set by software for generating software interrupts
Interrupts for port P2 is controlled by the registers P2IE and P2IES
Debouncing
When the button is pressed, the actual input voltage goes through several transitions from low to high. Since we cannot make any hardware modifications, we can implement debouncing in software. Options:
- wait for a fixed delay of the maximum length of expected bounces. around 10ms
- use timers. More useful for toggle switches
- use counters or shift registers. Recommended way (Also mentioned in 7.3.3)
Source:
//button-interrupt.c
//Toggle LED backlight depending on when down-arrow button is pressed.
//Using interrupts
#include <cc430x613x.h>
#include "button-interrupt.h"
volatile unsigned int counter;
void main(void){
init_variables();
init_configure();
__enable_interrupt();
while(1);
}
void init_variables(void){
counter = 0;
}
void init_configure(void){
/* Initialize the stack pointer - done by cstartup.h */
/* Configure the watchdog */
WDTCTL = WDTPW | WDTHOLD; //stop watchdog timer
/* Configure all ports */
//configure BACKLIGHT as output
P2OUT &= ~BACKLIGHT; //Initialize output buffer: P2OUT -> BIT3 to 0.
P2DIR |= BACKLIGHT; //Configure pin as output: P2DIR -> BIT3 to 1.
//configure BL_BUTTON as input
P2DIR &= ~BL_BUTTON; //Configure pin as input: P2DIR -> BIT0 to 0.
P2OUT &= ~BL_BUTTON; //Enable internal pull-down resistors: P2OUT -> BIT0 to 0.
P2REN |= BL_BUTTON; //P2REN -> BIT0 to 1
//Enable interrupt on BL_BUTTON pin
P2IES &= ~BL_BUTTON; //Interrupt edge select on positive edge
P2IE |= BL_BUTTON; //Enable interrupt on BL_BUTTON
do{
P2IFG = 0; //Clear interrupt flag
} while(P2IFG != 0);
/* Set up the clock - N/A */
/* Configure other peripherals - N/A */
}
#pragma vector = PORT2_VECTOR
__interrupt void PORT2_ISR(void) {
P2OUT ^= BACKLIGHT; //toggle LED backlight
for(counter = 0; counter < DELAY; counter++); //delay for lazy debouncing
do{
P2IFG = 0; //Clear interrupt flag
} while(P2IFG != 0);
}
Header:
//button-interrupt.h
#ifndef BUTTONINTERRUPT_H_
#define BUTTONINTERRUPT_H_
#define INITSTATE 0
#define DELAY 20000
#define BACKLIGHT BIT3
#define BL_BUTTON BIT0
//Any debug flags
//Prototypes
void init_variables(void);
void init_configure(void);
#endif /*BUTTONINTERRUPT_H_*/
Timers
Learn how to configure timers.- Timer is really no more than a counter. It has no direct concept of time. (Except for Real-time clock) [page 289]
- It's the programmer's job to establish a relation between the value in the counter and real time.
- This depends on the frequency of the clock for the timer.
Clock Sources
TODO[From page 289]
SMCLK
- internal
- fast - MHz range
- internal
- slow - MHz range
- typically 32KHz from crystal
- can be taken from the VLO
- external
- external
- usually its connected so that INCLK = ~TACLK
Watchdog Timer
Counts up and resets system- when it counter reaches its limit
- To prevent reset, code should clear the counter before its limit
- power-on reset (POR)
- power-up clear (PUC): Less drastic
- not all registers are reset to default
- WDTIFG flag is set in special function register IFG1
- This bit can be checked to see if reset was caused by WDT.
WDTCTL: WDT Control register (16-bits)
upper byte:
For a proper write to WDTCTL, we need to write password WDTPW (0x5A)
Reading the upper byte gives us 0x69 - for protection
reset will occur if wrong password is written - Can be used for forcing a reset by software
lower byte:
Contains bits that control operation of WDT
Bits are reset to 0 upon POR but unaffected by PUC.
- 'rw' means that its readable/writable
- '(0)' means it's 0 by default and after POR
- 'r0(w)' means bit is always read as 0; can be stimulated to 1 to provoke some action
- for WDTCNTCL - clearing the counter
- WDTSSEL bit: use SMCLK (default) or ACLK (1) for clocking
- WDTISx bits: controls which bit from the watchdog counter is selected for reset.
either bit 6, 9, 13 or 15 (default)
Therefore, the period is 2^(bit# above); For default, thats 32, 768 clock cycles.
- WDTTMSEL bit: Should be set for interval timer mode. Therefore, removes any protective features
- same as the usual WDT except reset doesn't occur when WDTIFG is set
- Interrupt is generated if WDTIE bit in special function register IE1 is set (maskable)
- WDT (in its usual form) does have an interrupt vector (can we write ISR for it?? TODO)
- WDTIFG is cleared automatically here
WDTCNT: watchdog counter (16-bits)
Not accessible by user
After initial reset, the default clock is SMCLK, derived from DCO, is 1 MHz.
This gives us 32ms before WDT causes a reset. Need to either clear, stop or reconfigure.
Examples
To stop WDT:
WDTCTL = WDTPW | WDTHOLD;
To clear WDT: (The bit is cleared after every reset; therefore need to be set again)
WDTCTL = WDTPW | WDTCNTCL;
Timer_A - General Description
This is the general purpose timer on MSP430. TI-Chronos has two of these timers, TA0 and TA1
Consists of two main hardware parts:
- Timer block
- To choose from one of the clock sources
- Choose modes of operation (below)
- Several capture/compare channels
- Each channel can capture, compare, request an interrupt, sample (TODO)
- Name of each channel ends with the channel number
- such as TIMER_A3 for the 3rd channel
- Channel 0 is different for certain reasons (below)
- All channels for the timer use the same timer block
- needs software implementation if channels need to run @different frequencies
- Use hardware of timer for parts of an event that needs precise timing (TODO)
- Use software for less critical parts
Timer Block
TAR - 16-bit timer register i.e. "counter"
- increments on the rising edge of the clock source
TASSELx bits - Choose the clock source
- SMCLK, ACLK, TACLK, or INCLK
- Note: If timer needs to be working in real time, accurate & stable clock source is needed. Crystal (TODO)
- 1, 2, 4, or 8
- slower clock ->
- disadvantage - less resolution of the timer(X)
- advantage - increases the natural period of timer
- Table and example (TODO) pg. 290
- 0 - Stop
- halt the timer
- all the registers retain their value
- can be restarted later
- 2 - Continuous
- run freely from 0x0000 to 0xFFFF -> overflow -> start back from 0
- range period = 2^16 = 65,536 counts
- Used for
- capturing inputs
- running channels at different frequencies
- 1 - Up
- count from 0x0000 to TACCR0 -> return to 0 on the next transition -> repeat
- TACCR0 = (counter/capture register for channel 0) <- Note: One reason why channel 0 is different
- range period = TACCR0 + 1
- Used for
- pulse-width modulation (PWM)
- when all channels provide outputs at same frequency
- 3 - Up/Down
- count from 0x0000 to TACCR0 -> count down to 0 -> repeat
- range period = 2 x TACCR0
- used for
- centered PWM
- specialized situations
- Note: Channel 0 is different - because of Up and Up/Down mode
- (advantage) precise control over the period of timer
- (disadvantage) loss of channel 0 (unusable)
- register TACCR0 is used to hold the modulus i.e. upper limit of the count
TACLR bit - Write a 1 to clear count in TAR ( and in Up/Down mode - resets the direction of counting)
TAIFG bit - flag
To get a correct reading of the counter:
Capture/Compare Channels
Each Timer_A has multiple channels.
TACCTLn: Control register for nth channel
TACCRn: Capture/Compare channel for nth channel
Capture Mode hardware
CMx bits: choose the trigger edge of the input being captured
EQUn (internal): set when TAR matches TACCRn.
- clears itself after use
- Do it when configuring the timer to ensure the first period will be correct
TAIFG bit - flag
- is set when timer counts to 0
- only occurs when count goes to 0 due to normal counting
- is NOT set when value is written directly to the counter.
- Example: TACLR is used to clear counter
- interrupt (maskable) is also requested if TAIE bit is set
- stop the timer before modifying
- do NOT have to stop for:
- interrupt enables
- interrupt flags
- TACLR
To get a correct reading of the counter:
- Do one of the following:
- Stop the timer before reading the value of TAR
- read the timer multiple times and use software to make a majority vote to determine the correct reading.
- NOT an issue when reading values from TACCRn registers after a capture (below)
Capture/Compare Channels
Each Timer_A has multiple channels.
TACCTLn: Control register for nth channel
TACCRn: Capture/Compare channel for nth channel
- In capture mode:
- stores the value of TAR ("time") when an event occurs at the input
- In compare mode:
- specifies the time at which
- output should be changed, and
- an interrupt requested
- Default is 0: Compare mode
- Mode can be switched anytime (TODO)
- PnSEL.x = 1
- configure the pin for this rather than digital in/out
- PnDIR.x = 0 or 1
- for input or output (TODO)
Capture Mode hardware
CMx bits: choose the trigger edge of the input being captured
- rising edge
- falling edge
- either edge
- Check datasheet for details (TODO)
- CCInA
- outside the timer module
- Often connected to external pin TAn
- CCInB
- outside the timer module
- Often connected internally to another module
- advantage of internally connecting - TODO (SoC theme)
- GND or VCC
- allows capture from software
- Procedure:
- CMx = 11 - Capture both edges
- CCIS1 = 1 - Select the pair of internal inputs i.e. either GND or VCC
- Toggling CCIS0 generates the event for capture
- it inverts the "apparent input" - the input switches from GND to VCC (or vice versa) simulating a change at the input
- volatile - can change at any time
- good idea to use a synchronizer (below)
- Capture events are recorded on the next falling edge of timer clock that follows the trigger (TODO)
- set when capture occurs and current value of TAR is copied to TACCRn
- interrupt requested if CCIE bit = 1
- Important to react quickly to a capture because most applications are time-sensitive
- set if another capture occurs before TACCRn is read from previous capture event
- warning that a capture event was missed
EQUn (internal): set when TAR matches TACCRn.
- triggers the actions in compare mode
- Note: all channels can be triggered by EQU0 as well as EQUn. (TODO)
- set when EQUn is set
- Interrupt is requested if it was enabled
- "sample mode of the timer" (TODO)
- i.e. determines, along with the Counter mode, the behavior of channel's output OUTn
- Behavior depends on the Output mode and the Counter mode
- Note: sometimes there is not need for an external output
- just the flag/interrupt is used by software
- Note: sometimes used to in SoC to trigger modules internally (TODO)
Typical/straightforward use of different output modes (# - OUTMODx)
- 0 - Output
- output is controlled directly by OUT bit
- TODO
- 4 - Toggle
- TODO
- 1, 5 TODO
- 7, 3 TODO
- 2, 6 TODO
- Influence of TACCR0
- Uselessness of Channel 0 in modes 2, 3, 6, and 7
- glitches are possible on the output
- Common situations
- check User Guides
- Changes between mode 1 and 5 are safe
- Use mode 7 as an intermediate step to avoid glitches
Initial Configuration
Interrupts in Timer_A
TAIFG: Interrupt flag generated by timer block
TACCRn CCIFG: Interrupt flag generated by c/c channel n
TIMERA0_VECTOR: interrupt vector for TACCR0
- TACCTLn are cleared by POR
- not by PUC reset
- Default state of channel: Compare mode
- Default output mode: Mode 0 - output is determined by OUT bit
- Default value of OUT bit - 0
- OUT bit should be set before configuring the pin for Timer_A
Interrupts in Timer_A
TAIFG: Interrupt flag generated by timer block
TACCRn CCIFG: Interrupt flag generated by c/c channel n
TIMERA0_VECTOR: interrupt vector for TACCR0
- has a higher priority than for the other channels
- CCIFG0 is cleared automatically when interrupt is serviced
- CCIFG is not cleared automatically
- One option: (slow)
- determine the active flag - poll TAIFG and CCIFG bit for all channels
- clear the flag
- service the interrupt
- use TAIV (below)
- used to identify the source of interrupt rapidly
- is loaded with value corresponding to the source w/ highest priority
- Note: more than one flag could be set at a time
- clears the register and corresponding flag when accessed
- use switch() rather than if()
- is reloaded with value of next highest priority source, if any