Timers are extremely useful in various applications, a typical usage is to generate an interrupt every X micro / milli / seconds to perform a given task.
I could create a very simple project to experiment with the timer, but I decided it would have been nicer to add some timer functionality to my last experiment, about SPI communication.
In the last experiment I sent commands to a digital pot (MCP4131) via the SPI line, moving it's wiper to generate a low frequency (~1Hz) triangle waveform.
The idea was to send a value, then delay for a number of cycles (20000), then send the next value etc.
That's ok for a simple experiment, but it is definitely not best practice.
In general you don't want the MCU just counting cycles since in real applications you may have other activities to perform in parallel and their duration would affect your delay.
Even if you are not doing anything else, it would be better to set the MCU in sleep mode (low power) and wake it up just in time to send the new wiper value to the digital pot.
That's were timers come in help, particularly with the MSP430G2 MCUs.
These devices are designed to support very low power (sleep) modes while keeping some functionality active.
The following picture shows the typical current used by the MSP430 mcu (Fxxx for the specific chart, but we can assume a similar profile for the Gxxx ones), you can see how it drops when entering low power modes (LPM).
Ideally we would like to select the most conservative mode (LPM4), however we need to check that the set of functionality we need are active in the mode we choose.
First thing to understand is that in LPM the CPU is NOT active.
It means that code placed in a normal loop in the main function would not be executed.
So, how can we still "do something" with the CPU?
It will respond to interrupts, including those generated by timers, then it will execute the code of the interrupt functions we will provide.
The second important thing to check is that some clock systems are turned off in LP modes and in some cases also the digital oscillators could be turned off.
In all LP modes the Master Clock (MCLK) is not active, it makes sense as the CPU is off too.
However we are using SMCLK for the SPI communication, so we will need that one on.
Moreover our source oscillator is DCO, we will need that one active too.
Using the table 2-2 (from the MSP430 user guide) we identify LPM0 as the only LP mode that supports what we need.
Also, we will need a timer, and timers also use a clock, we will make sure that the selected one is going to be SMCLK.
Our setup procedure will be :
//Timer
void setupTimer()
{
TACCTL0 = CCIE; // Timer A Capture / Compare 0 interrupt enabled
TACCR0 = delCycles; // cycles to count
TACTL = TASSEL_2 + MC_2; // SMCLK, contmode
}
The Timer A Capture / Compare ConTroL 0 (TACCTL0) is used to enable the interrupt generation.
The Timer A Control Register 0 (TACCR0) will hold the number of cycles needed to generate an interrupt (it's a 16 bit value ->0x0000 to 0xFFFF).
Finally the TACTL register selects which clock source is used, an eventual divider and the timer mode.
TACTL register
While the registers TACTL and TAR -the time counter- are related to the timer itself, the TACCxxx registers are related to the capture / compare devices associated to that timer.
There are up to 3 CC devices in timer A (more on timer B) and each one of them can be configured to trigger at a different counter level using their register TACCRx.
They can also be configured in different ways using their TACCTLx control registers, in this first experimet we will just enable for them the Interrupt.
There are four timer modes, probably for our purpose anyone of them could do, but we would need to adjust our code accordingly.
Probably mode 1 is the easiest to use, followed by mode 2.
Mode zero is just used to stop the timer, should not need any further explanation.
Mode 1 "Up" tells the timer to count up to a value stored in TACCR0, fire an interrupt if enabled and the restart from zero.
Mode 2 "Continuous" instead keeps counting up to 0xFFFF but fires the eventual interrupt when the value reaches what we store in TACCR0.
Mode 3 "Updown" acts similarly to Mode 1, but, instead of resetting when reaching TACCR0, it starts counting down to zero.
Since the wiper level will not be managed in a loop now, we need two global variables, one holding the wiper level and one holding the direction of the ramp, so that we can properly increment / decrease the value.
I added a third variable to hold the number of (SMCLK) cycles to wait.
unsigned char potLevel;
unsigned char potDir;
unsigned int delCycles;
...
void main(void)
{
// setup
WDTCTL = WDTPW + WDTHOLD;
clockConfig();
pinConfig();
spiConfig();
potLevel=0;
potDir=1;
delCycles = 20000;
setupTimer();
_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
setPotValue(potLevel);// SPI communication
potLevel += potDir; // increment or decrease
if (potLevel>127) // potLevel is unsigned, below zero will be 255
{
potDir = -potDir;
potLevel +=potDir;
}
// set the next interrupt delay
TACCR0 += delCycles; // we are in continuous mode! (Mode 2)
}
The main function is updated to initialize the global variables and to set up timer A, plus an interrupt function is created to handle the timer interrupt.
Finally the instruction _BIS_SR(LPM0_bits + GIE) sets the CPU in LPM0 with Interrupts enabled.
Notice that we don't need anymore a loop to handle the execution, the CPU will be disabled and will be awaken by the interrupt generated by Timer A.
Timers can also be used for different purposes, with these MCUs they support also PWM functionality with direct output to IO pins.
I will cover these aspects in a future post.
Note : all the images are taken from the MSP430 user guide, by Texas Instruments.
The document can be downloaded here.
No comments:
Post a Comment