Ah, the MSP430. What a widely used and smartly designed little processor. However, it’s not without it’s quirks. It’s idiosyncrasies. It’s problems. I spent about a week trying to figure out why a piece of code given to me to build off of wasn’t working when I attempted to run the board at a lower voltage. What’s that? A calibrated CPU frequency is not the same frequency at all times? Well I’m dumb… that is a problem.
Running an MSP430 at lower voltages changes the frequency of the internal DCO, and high frequency external crystals have their own entire set of problems. Not to mention clock settings between chips are not transferable, meaning setting the DCO and BCS values for one chip doesn’t not result in the same frequency for another, meaning that every chip needs to be calibrated to run at a custom frequency before running the actual code you’re trying to deploy. Oh bother.
Building off of this excellent piece of work here, I modified it to use the series of chip for this wireless power project. First off, it doesn’t overwrite the default calibrated settings of the board; using the setupClk() macro you can load your custom clock speed in your own application. Secondly, the assumptions: it needs a 32Khz watch crystal, and the timer settings needs SMCLK as the timer clock and ACLK as the capture input, so you’ll have to find the right timer settings for your series of chip. Finally, to get this stuff working at a lower clock speed, you can set your programmers voltage to be lower, calibrate it that way, or, if it can’t go low enough (again, working on wireless power stuff), you’ll just have to guess. Ah, precision.
If this is sufficiently helpful, I can post other workarounds to various MSP430 problems I’ve encountered.
Download link: MSP430 Calibration
//******************************************************************************
// Calibrate DCO settings- based on TI code example
//
// MSP430F20xx Demo - DCO Calibration Constants Programmer
//
// NOTE: THIS CODE REPLACES THE TI FACTORY-PROGRAMMED DCO CALIBRATION
// CONSTANTS LOCATED IN INFOA WITH NEW VALUES. USE ONLY IF THE ORIGINAL
// CONSTANTS ACCIDENTALLY GOT CORRUPTED OR ERASED.
//
// MSP430F20xx
// ---------------
// /|\ | XIN|-
// | | | 32kHz
// --|RST XOUT|-
// | |
// | P1.0|--> LED
// | P1.4|--> SMLCK = target DCO
// Orignal Code By
// A. Dannenberg
// Texas Instruments Inc.
// May 2007
// Built with CCE Version: 3.2.0 and IAR Embedded Workbench Version: 3.42A
//******************************************************************************
#include “msp430x22x4.h”
// calibration is:
// ACLK_Divided * SMCLK = Target Frequency
// SMCLK will be at the DCO frequency
#define DELTA_1MHZ 245 // 245 x 4096Hz = 1003520Hz or 1.04MHz
#define DELTA_2MHZ 489 // 489 x 4096Hz = 2002944Hz or 2.03MHz
#define DELTA_4MHZ 978 // 978 x 4096Hz = 4005888Hz or 4.00MHz
#define DELTA_4237 1035 // 1035 x 4096Hz = 4237499Hz or 4.2375Mhz
#define DELTA_8MHZ 1953 // 1953 x 4096Hz = 7.99MHz
#define DELTA_12MHZ 2930 // 2930 x 4096Hz = 12.00MHz
#define DELTA_16MHZ 3906 // 3906 x 4096Hz = 15.99MHz
#define SMCLK_PIN BIT1
#define ACLK_PIN BIT0
#define INFO_A_START (0x10C0)
#define INFO_A_CALIB (0x10F6)
#define INFOA_ADDR_MBCS (0×0001 + INFO_A_CALIB)
#define INFOA_ADDR_MDCO (0×0000 + INFO_A_CALIB)
#define setupClk() DCOCTL = *((unsigned char*)INFOA_ADDR_MDCO); \
BCSCTL1 = *((unsigned char*)INFOA_ADDR_MBCS); \
unsigned char CAL_DATA[10]; // Temp. storage for constants
volatile unsigned int i;
int j;
char *Flash_ptrA; // Segment A pointer
void Set_DCO(unsigned int Delta_L, unsigned int Delta_H);
int window = 0;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
for (i = 0; i < 0xfffe; i++); // Delay for XTAL stabilization
P1OUT = 0×00; // Clear P1 output latches
P1SEL = 0×10; // P1.4 SMCLK output
P1DIR = 0×12; // P1.0,4 output
P2DIR |= SMCLK_PIN + ACLK_PIN; // set p2.1 to be output
P2SEL |= SMCLK_PIN + ACLK_PIN; // set high for SMCLK
P2OUT = SMCLK_PIN + ACLK_PIN; // 0 = pull down
j = 0; // Reset pointer
unsigned int d_l, d_h;
d_l = DELTA_4237 – window;
d_h = DELTA_4237 + window;
Set_DCO(d_l, d_h); // Set DCO and obtain constants
CAL_DATA[j++] = DCOCTL;
CAL_DATA[j++] = BCSCTL1;
d_l = DELTA_16MHZ – window;
d_h = DELTA_16MHZ + window;
Set_DCO(d_l, d_h); // Set DCO and obtain constants
CAL_DATA[j++] = DCOCTL;
CAL_DATA[j++] = BCSCTL1;
d_l = DELTA_12MHZ – window;
d_h = DELTA_12MHZ + window;
Set_DCO(d_l, d_h); // Set DCO and obtain constants
CAL_DATA[j++] = DCOCTL;
CAL_DATA[j++] = BCSCTL1;
d_l = DELTA_8MHZ – window;
d_h = DELTA_8MHZ + window;
Set_DCO(d_l, d_h); // Set DCO and obtain constants
CAL_DATA[j++] = DCOCTL;
CAL_DATA[j++] = BCSCTL1;
d_l = DELTA_1MHZ – window;
d_h = DELTA_1MHZ + window;
Set_DCO(d_l, d_h); // Set DCO and obtain constants
CAL_DATA[j++] = DCOCTL;
CAL_DATA[j++] = BCSCTL1;
Flash_ptrA = (char *)INFO_A_START; // Point to beginning of seg A
FCTL2 = FWKEY + FSSEL0 + FN1; // MCLK/3 for Flash Timing Generator
FCTL1 = FWKEY + ERASE; // Set Erase bit
FCTL3 = FWKEY + LOCKA; // Clear LOCK & LOCKA bits
*Flash_ptrA = 0×00; // Dummy write to erase Flash seg A
// Set WRT bit for write operation
FCTL1 = FWKEY + WRT;
// Point to beginning of cal consts
Flash_ptrA = (char *)INFO_A_CALIB;
// re-flash DCO calibration data
for (j = 0; j < 10; j++) {
*Flash_ptrA++ = CAL_DATA[j];
}
FCTL1 = FWKEY; // Clear WRT bit
FCTL3 = FWKEY + LOCKA + LOCK; // Set LOCK & LOCKA bit
setupClk();
while (1)
{
P1OUT ^= 0×02; // Toggle LED
for (i = 0; i < 0×4000; i++); // SW Delay
}
}
void Set_DCO(unsigned int Delta_L, unsigned int Delta_H) // Set DCO to selected frequency
{
unsigned int Compare, Oldcapture = 0;
BCSCTL1 |= DIVA_3; // ACLK = LFXT1CLK/8
/*
Timer clock needs to be SMCLK
Timer capture input needs to be ACLK
*/
TACCTL2 = CM_1 + CCIS_1 + CAP; // CAP, ACLK input — only for timerA2, not ta0
TACTL = TASSEL_2 + MC_2 + TACLR; // SMCLK, cont-mode, clear
while (1)
{
// if difference between SMCLK captures == delta
while (!(CCIFG & TACCTL2)); // Wait until capture occurred
TACCTL2 &= ~CCIFG; // Capture occurred, clear flag
Compare = TACCR2;
Compare = Compare – Oldcapture;
Oldcapture = TACCR2;
if (Delta_L = Compare)
break;
else if ((Delta_L + window) < Compare)
{
DCOCTL–;
if (DCOCTL == 0xFF)
if (BCSCTL1 & 0x0f)
BCSCTL1–;
}
else
{
DCOCTL++;
if (DCOCTL == 0×00)
if ((BCSCTL1 & 0x0f) != 0x0f)
BCSCTL1++;
}
}
TACCTL2 = 0; // Stop TACCR2
TACTL = 0;
BCSCTL1 &= ~DIVA_3; // ACLK = LFXT1CLK
}