Pointers
We've not dealt with pointers up to this tutorial, but they're not really a difficult concept. Let's say we have code that declares the following two items:
int var;
int *ptr;
The first is what we're used to seeing; we declare a variable of type int (16 bits in MSP430), which will store a signed integer value. The second is also a declaration of type int, but rather than a variable, it is a pointer. The stored number in ptr doesn't refer to the value of a variable, but rather the address where a variable of type int is stored. (The actual size of the pointer in an MSP430 is 16 bits, so char *ptr; would also be a 16 bit value, even though it points to memory where an 8 bit value is being stored.)
In the C language, we have a way to refer to the address of a specific variable by using the & character. So from this point, if we assign ptr = &var; then ptr now points to the address where we've stored the int value var. Similarly, we can reference the value stored in a pointer with the * character. int var2 = *ptr; would store the value at address ptr in the new variable var2. If we want to assign a specific address to a pointer, rather than reference the address of a variable, we can use a literal of the form (int *)0x1234.
In the MSP430, an int variable takes 16 bits, while a char variable take 8. When we want to reference a specific word, we'll use a pointer of type int. If we want to reference a specific byte, such as the calibration values for the DCO, we'll use a pointer of type char. To use the DCO values we'll program in SegmentB, we can use the following:
char *CALBC1_UART = (char *) 0x10BE;
char *CALDCO_UART = (char *) 0x10BF;
or, if we'd rather not use extra memory, define in the header:In the MSP430, an int variable takes 16 bits, while a char variable take 8. When we want to reference a specific word, we'll use a pointer of type int. If we want to reference a specific byte, such as the calibration values for the DCO, we'll use a pointer of type char. To use the DCO values we'll program in SegmentB, we can use the following:
char *CALBC1_UART = (char *) 0x10BE;
char *CALDCO_UART = (char *) 0x10BF;
#define CALBC1_UART *(char *) 0x10BE
#define CALDCO_UART *(char *) 0x10BF
The Flash Memory Controller
Ok, now we've got a handle on how to reference portions of the flash memory. Now it's time to learn how to actually write to it. The MSP430 has a peripheral designed specifically to handle managing the flash memory called the Flash Memory Controller. Since it would be dangerous to manipulate the flash memory willy-nilly, it's set up like the Watchdog Timer, so that a key is needed to change any of its four control registers. The key value is 0xA5, conveniently provided in the header files as the value FWKEY. Here are the portions we'll need to know about:
FCTL1
- FWKEY (bits 15-8): The key is read as the value 0x96. Each register has this.
- BLKWRT (bit 7): control bit for block write mode. We will be writing word by word, so we'll save this function for a future tutorial.
- WRT (bit 6): write mode enable. Before we can write to any flash segment, this bit must be enabled.
- MERAS & ERASE (bits 2 and 1): Control the mode of erasure. You can erase a single segment, the entire main memory, or the main memory and information segments both. (Depending on whether SegA is unlocked.)
FCTL2
- FSSELx (bits 7-6): These bits select which clock source to use for the programming.
- FNx (bits 5-0): These bits allow you to divide the source clock by any factor up to 64. (Divide by FNx + 1)
FCTL3
- FAIL (bit 7): We don't want to see this bit set! For our purposes, it could mean the clock source has failed for some reason. If this happens, it needs to be reset by software before anything else can be done.
- LOCKA (bit 6): I won't go into how this works; this bit controls whether SegA can be erased/written. If you find you absolutely must change something in this segment, you can read in the Family User's Guide to learn how to use this bit.
- LOCK (bit 4): When this bit is set, the flash memory is completely locked, and cannot be erased or written.
- WAIT (bit 3): Indicates when the flash memory is being erased or written.
FCTL4
- This register is not available on all devices; it controls the 'marginal read mode'. We won't be using it here.
To do a successful programming of the Information Segment, we first set the clock for the controller. The actual frequency isn't terribly important, as long as it falls between 257 and 476 kHz. The default DCO setting of the MSP430 is about 1.1 MHz, and so a division of 3 (FNx = 2) works well, giving us roughly 370 kHz. We'll calibrate the DCO to 7.3728 MHz, so a division of 20 (FNx = 19) will give us about the same.
If we have anything we want to save in the segment, then the entire contents would need to be saved to a buffer. Likely, your SegB is blank, so we'll just proceed forward. Next, we set the controller to erase mode. We don't want to erase the main memory segments, so we only need the ERASE bit. We then clear the LOCK bit to allow changing the flash memory. When the controller is configured this way, we initiate the erase cycle by writing to any address within the segment. There's nothing magic about what value we write, but it is absolutely crucial that you have your address pointer pointing to somewhere in the segment you actually want to erase!
Once it has erased, we reconfigure the controller to write mode (not block write), and then proceed to write the values we need, either as bytes or words. If you preserved the prior contents, a better method is to change the contents to the new values and use block write mode. For this tutorial, we'll keep it simple.
Finally, when everything is written, we clean up, clearing the write mode bit and locking the flash memory.
The
code I wrote for writing the UART DCO calibration to Segment B using the TLV format is found in uartcalG2231.c. Remember, if you run
this, it requires the watch crystal. If there is anything already
stored in SegB, it
will be lost.
If you'd rather store it in Segment C (0x1040 to 0x107F) or Segment D
(0x1000 to 0x103F), change the address in the assignment instructions
accordingly. If you choose to put in in Segment A (0x10C0 to 0x10FF),
look in the Family User's Guide for instructions on unlocking the
Segment. I do not recommend this, however, since we're calibrating a
non-standard DCO frequency. Wherever you choose to store it, you'll
need to remember the address. If you use my method here, that address
is 0x10BE for the CALBC1 value and 0x10BF for the CALDCO value. Next time we'll write code that pulls these calibration
values from memory and configures the DCO and TimerA to start setting
up our software UART.
This tool will be very useful to you; keep in mind that though the Value Line devices only have one DCO calibration and don't appear to have any calibrations for the ADC, there is space for them! This process is not too difficult, you can upgrade your devices to have better calibrations, essential for accurate scientific work. For more information on the ADC calibration values that are often included in MSP430 devices, see the TLV chapter of the Family User's Guide.
This tool will be very useful to you; keep in mind that though the Value Line devices only have one DCO calibration and don't appear to have any calibrations for the ADC, there is space for them! This process is not too difficult, you can upgrade your devices to have better calibrations, essential for accurate scientific work. For more information on the ADC calibration values that are often included in MSP430 devices, see the TLV chapter of the Family User's Guide.