29 July 2010

Tutorial 08-b: Configuring the DCO

The MSP430's Digitally Controlled Oscillator is one of the most important peripherals to be able to manage.  It's the easiest of the clock sources to configure, as it requires no external parts and is controlled completely by software.  The DCO (within the BCS+) is configured with two registers: DCOCTL and BCSCTL1.  Look up the maps of these two registers in the x2xx Family User's Guide, page 5-14.

This image comes directly from the User's Guide, and explains how to select the different possible frequencies for the DCO.  The total range of frequencies is divided into 16 overlapping ranges.  So Range 7's low end overlaps Range 6's high end, and Range 7's high end overlaps Range 8's low end.  Make sense?  Ok, each range is further subdivided into 8 possible frequencies.  If you plot all of these out, you get something similar to the above image, but with 16 lines instead of the three representative lines shown here.  There are then 128 distinct frequencies available to the DCO, with some overlap of course.


Registers
We are able to select the frequency we use with the RSELx bits in BCSCTL1 (select range 0 to 15) and the DCOx bits in DCOCTL (select step 0 to 7).  How do we know which pair we want?  The device-specific datasheet should give you some help in that regard.  For the G2x11, we see the DCO frequency table on page 26.  For any device, this table will either list the typical value for a sample of selections, or a set of min/max values for the selection set.  (Note: As of this writing, the current revision of the G2x11 datasheet (SLAS695B) is printed incorrectly, giving typical values for some selections and min/max values for others.  In future revisions of the document it will likely be changed to only one or the other.)  TI uses an ordered pair notation to describe the DCO setting, such as (8,2) for Range 8, Step 2 (or DCO 2 to use their term).  We'll use that notation from now on to describe DCO settings with the convention DCO(r, s) where r is the range value and s is the step value.

As an example, note that DCO(6,3) is typically 0.80 MHz (or 800 kHz), while DCO(13,3) is 7.8 MHz.  Note that the table only lists DCO(r,3) pairs for each range, and also values for DCO(0,0) and DCO(15,7), the low and high end of the DCO values.  What about all the others?  At the bottom of the table, there are two values called SRSEL and SDCO.  These values give the ratio of subsequent ranges and steps as SRSEL = (r+1,s)/(r,s) and SDCO = (r,s+1)/r(s), allowing you to get a value for any of the possible combinations.  For example, DCO(6,4) would be SDCO * DCO(6,3), or in the case of the G2211, 1.08*0.80 Mhz = 0.864 MHz.  DCO(6,7) would be SDCO * DCO(6,6) = SDCO * (SDCO * DCO(6,5)) and so on, so a general formula would be DCO(r,s+n) = SDCO^n * DCO(r,s).  (Note that you can get the lower step values with negative n, eg. DCO(6,1) = DCO(6,3-2) = SDCO ^ (-2) * DCO(6,3).)

One final topic before getting to the code; each MSP430 device comes with at least one calibrated value for the DCO.  These values are found for each individual chip at the fabrication facility and programed into the information memory section of the flash memory.  The values are not necessarily the same for my G2211 as it is for yours, but if you ever accidentally (or intentionally) erase the calibration values, TI does provide a program that will recalibrate the DCO and write the values to the memory for later use.  (You should be able to find this program in the code examples you can download for your device.  The G2xx code can be obtained here, and the program is called msp430x20xx_dco_flashcal.c.)  The G2x11 devices only have one calibrated frequency at 1 MHz.

Example
Now for the practical part.  Fire up CCS with your launchpad, and enter the debugging mode with any program you have.  We're not going to actually run the program, just look at some of the registers as they come up by default.  The MSP430 x2xx devices supposedly default to a 1.1 MHz DCO.  What value actually comes up? In the debugger, open up a view of the registers, and find the BCS+ registers under System_Clock.  For the G2211, the registers start with values of 0x60 for DCOCTL and 0x87 for BCSCTL1.  Expand the registers to look at the individual bits, and we can see that RSEL is 0b0111, or 7, and DCO is 0b011, or 3.  The default setting for the DCO is DCO(7,3), which according to the datasheet is between 0.80 MHz and 1.50 MHz.  The actual value depends on your unit, the temperature, pressure, phase of the moon... ok, not really the moon phase, but you get the idea.  If you happen to have an oscilloscope, we can actually look at the frequency by using the alternate function for P1.4-- the SMCLK can be driven by the DCO and put on this pin for external use.

Let's write up some code to actually change the DCO now.  We're mostly interested in manipulating the DCOx and RSELx bits, which are bits 5-7 in DCOCTL and 0-3 in BCSCTL1 respectively.  When we've selected the pair of values we want, we can change those bits in code.  For example, if we want to change to DCO(12,2), we want 0b1100 for RSELx and 0b010 for DCOx.  We can set these values in our code with the following:
BCSCTL1 &= ~(BIT0 + BIT1);  // set bits 0 and 1 to 0
BCSCTL1 |= BIT2 + BIT3;     // set bits 2 and 3 to 1
DCOCTL &= ~(BIT5 + BIT7);   // set bits 5 and 7 to 0
DCOCTL |= BIT6;             // set bit 6 to 1

Does the order matter?  Well, keep in mind the frequency will change with every step.  So after the first line, assuming we have the default configuration found above, the bits have changed to 0b0100, putting us in DCO(4,3).  The second line moves us to DCO(12,3).  The third line moves us to DCO(12,2), making the last step redundant.  If we had started by setting bits 2 and 3 in RSELx, we would have stepped from DCO(7,3) to DCO(15,3), putting a really high frequency on everything.  That may not be an issue, but if you have any sensitive components you might consider stepping rather than jumping up to higher frequencies.  If MCLK is driven by DCO (it is by default) you  may find some problems as the higher frequencies may be above the 16 MHz limit for the processor.  Going the other direction, jumping down puts the DCO at a really low frequency, which slows down the operation of the subsequent steps, but shouldn't cause any serious trouble.

Setting to calibrated values is much easier, since CCS provides us with shortcuts to those values:
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;


Those two lines are all that's needed to set the DCO at exactly 1 MHz!  (Within 3%, anyway.)


I've written some code in dcodemo_g2211.c that gives an example of how to use the DCO in a program.  Browse through it and try running it on your launchpad to see how it works.  The program simply flashes the LED with a delay loop and waits for the user to press the button.  The program then changes the DCO frequency, and re-flashes the LED.  Since the frequency increases, however, the LED flash is much faster, even though it uses the same delay loop!  The DCO is controlling the operating speed of the processor, and so the for loop steps through much more quickly.  Another button press slows down the DCO and re-flashes the LED, this time at a slow rate.  Another button press returns the LaunchPad to the original state, and the program runs through again.

We haven't covered all the features of the DCO yet, and will discuss them as we progress further.  Next we'll talk about the use of timers in the MSP430.  As always, feel free to discuss and ask questions about what you've read here!

Reader Exercise:  My G2211 unit that came with my LaunchPad uses DCO(6,5) for the calibrated 1 MHz frequency on the DCO, and sets the MODx bits to 0b00101 (which is also 5).  What does yours use?  Write a program that uses the calibrated value and use the debugger to find out what the register settings are for the 1 MHz calibration on your unit.  Supposedly, these are set individually to the chip, so I'd be interested to hear if anyone actually has a different value than mine!

39 comments:

Anonymous said...

FYI,

the G2231 that came with my Launchpad has the following calibration data for 1 MHz:

DCO(6,4) and MODx = 9

Anonymous said...

I followed your tutorial and have set the clock up to DCO(15,7) and MOD = 0.

However, after doing this debugging seems to be very unreliable. Single stepping is still possible but letting the µC run breaks the season and no further interaction is possible, e.g. stopping or reseting it.

Have you experience a similar behavior?

Unknown said...

At DCO(15,7), you're operating well above 16 MHz, which is all the chip's processor is rated to. I'm betting that's the source of your problem.

I think you can use the high DCO frequencies if it's not running MCLK. If you run MCLK off of a crystal to keep its frequency at or below 16 MHz, I think you could use the ~20 MHz you're getting for peripherals.

Unknown said...

I'm going to add in the comments a way you can get around the four-step setting and reduce it to just two steps. It's not as clean of code, though, and so isn't quite as easy to see as the way I used in the tutorial.

An equivalent way to set DCO(12,2) would be:
BCSCTL1 = (BCSCTL1 | (BIT2 + BIT3)) & ~(BIT0 + BIT1)
DCOCTL = (DCOCTL | BIT6) & ~(BIT5 + BIT7)

Harder to read, but if you want to reduce cycles and avoid the possibility of accidentally turning on a high frequency, this method will work. There are other ways to manipulate bits too.. feel free to send your favorite method to share!

NJC said...

Thanks for mentioning the recovery tool that TI has for recalibrating your uC. Theres so much they have hidden away on their website. If you can, could you link to the page? I know a few people have trouble finding some of TIs links.

Unknown said...

The program is included in the code examples for each device-- the file in the Value Line code set is called msp430x20xx_dco_flashcal.c. I imagine it works for any value line device regardless of the naming scheme, but you may have to adjust the code accordingly (eg. remove all calibrations but the 1 MHz, since that's all that's available).

OCY said...

The calibrations for higher frequencies may be useful too. Why remove them?

I think Info-A is locked and you need to unlock it before you can erase and re-write it.

The lock is a little tricky. You need to do something like:

if (FCTL3 & LOCKA) FCTL3=FWKEY+LOCKA;

Unknown said...

Hmm.. I guess I'm assuming there's a reason the value line devices only come with one calibrated frequency. Might be worth an experiment; can we configure the other frequencies ourselves using this code? I may send a note to TI and ask about that.

Kenneth Finnegan said...

I ran my set of 2231s through my debugger, but couldn't figure out how to pull register values, so I just copied them into RAM and read that:
2231 that shipped with my 'pad: (6,6) MOD 0

Others from single tube:
(6,6) MOD 17
(6,5) MOD 20
(6,6) MOD 7
(6,6) MOD 4

So yeah, they're certainly being individually set. I don't have access to a scope over summer, so I'll have to take their word that these are actually good values...

Unknown said...

Are you using CCS? I covered viewing the registers in Tutorial 06; have a look there as I think this tool is very helpful!

It's kind of neat that each chip gets individually calibrated-- I'd love to get a tour some day to see how they actually fabricate them!

Unknown said...

I just received a message from TI from an inquiry about calibrating the DCO. They pointed out a tool that looks very useful for anyone interested in (re)calibrating the DCO made by Elprotronic Inc.:

"NEW ! Did you loose the DCO constants in the Info-A of the MSP430F2xx microcontroller? It is not a problem. The GangPro430 programmer can recalibrate the DCO and restore the DCO constants using access via JTAG or Spy-Bi-Wire. No extra hardware is required. Each DCO frequencies are calibrated with tolerance +/-1% to 1, 8, 12 and 16 MHz and DCO constants are saved in memory. Custom defined DCO frequencies from 100 kHz and up can be calibrated if required. DCO in the MSP430F1xx and MSP430F4xx can be calibrated also."

More information and the needed tools are available at http://www.elprotronic.com

_glytch said...

My MSP430G2231 uses DCO(6,5) MODx=17 for the calibrated 1MHz.

I'm curious to see how much variance there is in reliability between chips when running the MCLK over the maximum supported 16MHz (for fun obviously!).

urlaubaer said...

A MSP430G2XX DCO Calibrator:

http://www.mikrocontroller.net/topic/193058#new

This will resemble the INFOA-Constants of the MSP430F20XX-series (1/8/12/16 MHz).

TI-Hex-File and Include-File for IAR included.

Have fun...

Anonymous said...

i think a utility function like the following makes it easier to read:

void
dco(unsigned int r, unsigned int s)
{
// bits 0 to 3
BCSCTL1 &= ~0x0F;
BCSCTL1 |= 0x0F & r;
// bits 5 to 7
DCOCTL &= ~0xF0;
DCOCTL |= 0xF0 & (s<<5);
}

Unknown said...

David,

First of all I would like to thank you for creating this site. This is a resource that is needed for the MCU beginner. I cannot tell you how much this has helped me. I wish you'd write a book. I would like to ask a quick questions. I am new to this so bare with me. In the post you stated:

"For example, if we want to change to DCO(12,2), we want 0b1100 for RSELx and 0b010 for DCOx."

My question is if DCO is controlled by manipulating bits 5-7 in the DCOCTL register and RSEL is controlled by manipulating bit s0-3 in the BCSCTL1 register then wouldn't the binary for DCO(12,2) be 0bXXXX1100 for RSELx and 0b010XXXXX for DCOx? I used the X's since I don't know what the value of those bits are.

Take care,

P.S. I will be working on a project in the near future. Are you open to tutoring/consulting?

Beretta said...

That's an excellent question; what you're describing is correct. The notation I chose was showing just the RSELx and DCOx bits, not the entire register for BCDCTL1 and DCOCTL.

I can't promise I'd have the answers, but feel free to shoot me any questions you have! At the very least, it will force me to learn something new. ;)

Kelson said...

Hi David!

You've done a really great job! Thanks for the tutorial. I got a doubt and hope you can help me. I am using the FG4618 (Family 4) and as I see, the DCO configuration is different from your settings (Family 2), so I am not getting it working for different frequencies like you did.

Can you please give me an example of how to setup the DCO for FG4618 and how change its frequency? I would appreciate if you copy the answer to my email: kelsonbatista@yahoo.co.uk.

Thanks in advance

Kelson

Urgentissimo! said...

I guess most of us just need three frequencies: 1MHz (slow), 6MHz (low voltage) and 16MHz (fast). Too bad they did not define anything other than CALDCO_1MHZ/CALBC1_1MHZ.

Unknown said...

Hi Kelson! Sorry it took me so long to get your comment published and a response written. Unfortunately, I've only used the x2xx and x5xx families, but I would imagine that the differences are not very significant. The best advice I can give is to get a copy of the x4xx Family User's Guide (found on any of the pages of TI's website for any of that family of devices). Find the section on the DCO and Clocks, and check there. The next thing to do would be to download the sample code for your device; there is certain to be an example that configures the DCO in that collection.

If you're still having trouble, send me a note and I'll see if I can't help decipher it!

Unknown said...

Urgentissimo: True that there's only the one provided on the G2xx devices, but the F2xx devices have more calibrated frequencies. That includes devices such as the F2012, which works right in the LaunchPad directly! The F2012 has 1, 8, 12, and 16 MHz already calibrated for you; it may suit what you need, so check it out!

Cris said...

Hi, first I wanted to thank you for taking the time to document your work and sharing it with the world!! It has been a great guide for me as I have no experience with micro controllers and a light background in programming. Also I have been jumping around a lot on the site picking up whatever I understand as I go along. Just had one question so far to see if I understand if what I am doing is correct.

So I wanted to create a watchdog timer interrupt and also I wanted the watchdog timer to be running as slow as possible, mind you this is all just for testing and to see if I can adjust to the logic needed to program for this.


Bellow is my code, its a simple 7-segment display which I made by hand and wanted to test by outputting the numbers 0-9 every few seconds. Thought using the WDT interrupt and some of your example code would do the job, hope this is correct.

#include "io430.h"
#define _F P0
#define _G P1
#define _A P2
#define _B P3
#define _C P4
#define _D P5
#define _E P6

char i = 0; // Counting variable
char j = 0;
char _7segment[10] = {
_B+_C,
_A+_B+_D+_E+_G,
_A+_B+_C+_D+_G,
_B+_C+_F+_G,
_A+_C+_D+_F+_G,
_C+_D+_E+_F+_G,
_A+_B+_C,
_A+_B+_C+_D+_E+_F+_G
};
int main( void )
{
WDTCTL = WDT_ADLY_1000; // Set the WDT Clock to 1000ms.

P1OUT = 1; // Set all outputs to High initially.
P1DIR = _A+_B+_C+_D+_E+_F+_G; // Set which pins are going
//to be outputs

IFG1 &= ~WDTIFG; // Clear the WDT Flag.
IE1 = WDTIE; // Enable the WDT Flag.

for(;;)
{
LPM0;
} // Making the statement cry :(
}
// Interrupt Service Routines.
#pragma vector = WDT_VECTOR
__interrupt void WDT_ISR (void) {
IFG1 &= ~WDTIFG; // Clear the WDT Flag.
if (++i == 3){
i = 0;
P1OUT = _7segment[j];
if(j++ == 10)
j = 0;
}
}

Unknown said...

Hi Cris, I'm glad to hear that the tutorials have been helpful to you. You've described yourself as just what I hoped the audience I was trying to write to would be, so if you've found a lot of help here, I would call that a success. Thanks for the encouragement!

Debugging your code may be better suited for one of the online forums. Since this is a tutorial on configuring the DCO, there is one thing I'll point out related to that:

The 1000 ms delay you are coding in to the WDT assumes you are using ACLK with the 32kHz watch crystal attached; you probably will not get your expected behavior as your code doesn't have any clock configuration. The WDT by default is sourced by the DCO, which has a default frequency of ~1.1 MHz, so your delay will be more like 30 ms rather than 1 s.

I haven't yet done a tutorial on the WDT, but it is in the plans; it will come after I finish the current set on UART, so look for it sometime around the new year.

PS: You might look at Tutorial 3 once more. P1OUT = 1; won't set all of the outputs to logic high as you want, only P1.0 will be set.

Unknown said...

Quick correction-- a glance in the header file reveals that setting the WDT delay as you have done also selects the clock source, so it should be configured to use ACLK for you. If your watch crystal is attached, you'll need to make sure its capacitance is matched properly. You might find tutorial 16c in that regard!

Cris said...

Wow!!! Thank you for the quick reply, as you suggested I will take a look at tutorial 16c. Also, my watch crystal is attached...strangely it was one of the first things I did when I received my launchpad since I do happen to have a bit of experience soldering even small components. Again, thanks!!

Dany said...

The code you posted doesnt work for me (Im using the 2231). Also, when I use the debugger I dont see any changes in the relevant registers while stepping through. Instead of flashing the LED, the LED is turned on when I have the button depressed -- there is not flashing involved.

EDIT: I have found that if I hold down the button and pause the debugger, I can see that the registers have changed. So my problem remains that I cant get the light to blink!

Any help would be greatly appreciated

Unknown said...

Have you tried running the code without the debugger?

As you found, sometimes interrupts behave strangely when using the debugger. This is because the debugger holds the CPU clock intentionally. Other clocks may still be free to run (you can't really stop the crystal from oscillating, for example!), and so timer interrupts have always given me trouble in debugger too. At the very least, since the code uses for loops for the delay, if you haven't changed the value in the loop, you'll have to step 60,000 times to get the led to change.

Can you give me any more information on just what you're doing, particularly if you've tried letting the code just run?

Dany said...

First of all thanks for all your effort, David, this whole site is really great!

Embarrassingly enough, I don't know how to run the code without the debugger... ALSO, I've noticed that when I first run the code that does not use interrupts, the red led barely flashes once then when I have the button depressed the LED is on and it is off otherwise. As for the code with uses interrupts, the red LED turns on when the code is run and stays on regardless of any button press.

Thanks a lot once again and in the meantime Ill try to figure something out!

Unknown said...

The green "play" button should start the MCU clock on its own. The other way would be to close the debugger window (or even CCS entirely), and just plug the board in. Once it's programmed, powering it up will start running the code.

I'll take a look at the code again this week and see if there's an error in it somewhere.

Sunil Shahu said...

hello,
i need to blink an LED at every 1 sec. i am using (6,3)at 0.8Mhz. I want to know that how to calculate delay count in for() loop to generate delay.
Thank you,
sunil.

Magyar Máté said...

Hi!

A warning for those who own the 1.5 version of the launchpad!
The new boards does not have external pull up resistors, so you have to turn on the internal ones like this:

P1REN = BIT3;
P1OUT = BIT3;


Also the delay function is not working(ccs 5,1), i don't know why so i had to replace every delay function with the actual for loop.

Unknown said...

Magyar, thanks for the warning! Another commenter mentioned that the more recent versions of CCS are optimizing out some delay loops; I'll have to take a look into that and figure out the best way to approach it.

Peter said...

Hi David,

Your tutorial is great. It is helping me a lot on understanding how to use the msp430. Thank you very much!

I've made two different functions and one macro for setting the DCO. You can see it: https://github.com/petersenna/msp430/blob/master/dcodemo.c

Anonymous said...

to comment and reply to Magyar:

I worked out the button pullout issue about a sec before reading your comment. Still it is good to know why I have to do it like that :)

I also have the problem you have with the delay. I found out that if I had a nop() command to be executed then the delay work fine:

for (n=0; n<60000; n++) {nop();}

/Philippe

Unknown said...

Hi David,

You mentioned in this tutorial that you can check the frequency on an oscilloscope, and that you just needed to use P1.4. Could you tell me how?

Unknown said...

Rodrigo: just make sure that P1SEL has bit 4 set (ie. P1SEL |= BIT4;), and the SMCLK will be available right there. Just hook up your oscilloscope, and you should see a square wave signal, letting you measure the frequency of the clock.

Miguel said...

Hi David,

I was also having problems with the delay() - in debbug, running step by step, it wouldn't execute. I tried declaring the "n" variable as global (outside the main loop) and it worked!
Thank you for your tutorials, they are great!

xmk@otenet.gr said...

CCS Version: 5.4.0.00091
for the delay had to use
for (n=0; n<60000; n++) {_nop();}

NOTE: _nop(); with underscore at the beginning

Anonymous said...

Hey can some one help me .Im trying to understand how long the delay is for when the DCO is calibrated at 1 MHz.

60000/1MHz should give me 0.06 s right that's just enough for the naked eye to perceive it . but my led is blinking at about 1 or 1/2 a second

Unknown said...

The delay isn't setting a number of clock cycles. Even a direct for loop takes more than one clock cycle per step, plus in this example it's embedded in a function call.

In practice, a timed delay needs to be done with a timer and interrupts. Using loops to delay is inaccurate and inconsistent. If you have to do it in code, the __delay_cycles(n) intrinsic function may be of better use to you.