c programming for MSP430

Constants and variables

When defining constants to make more readable programs or to allow for easy changes of constants used in multiple places use #define, rather than const . The constant declaration forces the compiler to create a variable in RAM at runtime. Define simply creates a code in the program memory this is much more efficient.

#define PIN3 3      //define PIN3 = 3

int pin3 = 3;  // create a 16 bit variable in RAM and store 3 in it.

Variables change during runtime so are required to be stored in RAM.

Typical variable types:


unsigned char myvariable; // 8 bit unsigned integer (0,... 255)

char myvariable;          // 8 bit unsigned integer (0,... 255)

signed char myvariable;   // 8 bit signed integer (-128,... 127)

unsigned int myvariable;  // 16 bit signed integer (0,... 65535)

signed int myvariable;    // 16 bit signed integer (-32768,... 32767)

int                       // 16 bit signed integer (-32768,... 32767)

Static and Volatile variables are different. A static variable is simply a variable that exists for the lifetime of the application. Static variables can be global: defined outside of a function and accessible everywhere, or local: defined within a function and only accessible from within that function. Local static variables are created on the first invocation of the function and remain in memory for the function to use when next called. The static keyword enforces the compiler to ensure that the RAM for the variable is always reserved and not reused for other variables.

static int state = 0;

So if you need a variable that is only used within a function and only updated by that function and you need the value of that variable to remain for the next time that you call the function then you need to define it as a static variable.

char debounce(void)
{
static char count=0;
if (PINB & (1< 10) return 1;
else return 0;
}

where the value of count needs to be kept for the next time the function is called.

A volatile variable is one the can be changed without the compilers knowledge for example by an interrupt. This means that whenever this variable is accessed by the program it must be reread from the actual memory location in case it has changed, rather than simply read from the cache or registers on the ALU if it has already been used recently by the program.

volatile int state = 0;

A variable can be both static (always present) and volatile (changed from anywhere).

static volatile int state = 0;

Arrays

Arrays allow for the storage and retrieval of multiple values using indexing.

int myArray[5];                   // define an array of 5 integers
chars myArray2[5];                // define an array of 5 bytes

int myArray3[3] = { 1, 2, 3};     // define an array of 3 integers and store (1, 2, 3)

int x = myArray3[2];              // copy the 3 third value from the myArray3 into x

#define ARRAYSIZE 100             // define array size constant
int myArray4[ARRAYSIZE];          // initialise array
int i;                            // initialise indexing variable
for (i=0; i<ARRAYSIZE; i++){      // loop through all array indices
  myArray4[i] = i;                // store the current index value in the array
}

Conditional statements: If, Switch

if (seconds == 59) {
minutes++;
seconds=0
} else seconds++;

switch(state) {
case 1:
{expression;}
break;
case 2:
{expression;}
break;
default:
{expression;}
break;
}

Loops: while, for

while ( condition ){
expression;
}

// Example of a while loop the iterates 1000 times
int x = 0;
int n = 1000;
while ( n-- ){   // Post decrement n
x = x+n;
}

int i;
for (i=0,i<10,i++) {
expression;
}

Functions

Functions allow you to reuse and encapsulate code. to use functions in c it is first necessary to define the function prototype as the top of your code before the main function. This necessary so that the compiler knows that the functions exist. The function prototype is simply the function declaration terminated with a semicolon.

// Function protoypes
void myfunction(int number, char code);
int myotherfunction(int number, char code);

The function itself can then appear anywhere after your main function. The main function should be the first function that appears in your code and does not require a function prototype. Functions do not need to return any values or have any inputs. the key word void is used. A function can only have one output with is returned using the return keyword.

// A function with no inputs and no output.
void myfunction(void)
{
Expressions;
}

// A function with two inputs and no output.
void myfunction(int number, char code)
{
Expressions;
}
// A function with two inputs and one output.
int myotherfunction(int number, char code)
{
int result;
result=number*code;
return result;
}

If you wish to change more than one variable then you will need to use pointers.

Pointers and Arrays

Pointers are simply special variables that contain the address of a particular memory location where you store data i.e. they point to that memory location.

To create a pointer variable (myPointer) that points to a particular variable (myVariable) we could use the following code

int myVariable = 1000;    // define variable myVariable assign it a value of 1000

int *myPointer;           // define a pointer variable
myPointer = &myVariable;  //Create a pointer that contains the address of myVariable

The ‘int’ type definition when defining a pointer specifies the size of the memory location that the pointer points to in this case the size of myVariable. The size of the pointer itself depends on the size of the address used by the computer or micro controller and is taken care of by the compiler. The ‘*’ indicates that we are defining a pointer variable. We then assign the memory location of the variable myVariable to the pointer myPointer using the & operator.

We can now use the pointer to access the value stored at the memory location it points to.

int x;           // Define new variable x
x = *myPointer;  //Copy the value stored in the location that the pointer points to into x

Here the ‘*’ indicates that we want the contents stored at the memory location (in this case 1000) not the value of the pointer itself which simply contains the memory address.

These examples seem a little pointless as here we are only dealing with variables that refer to a single memory location. In fact the variable myVariable is really already a pointer to the memory location where the value is stored. However pointers become really useful when you have arrays. Consider an array (myArray) that is able to hold 5 integers.

int MyArray[5] = { 2, 5 , 3, 14, 300 };  // Define new array MyArray
int *myPointer;                          // Define pointer to integer type
myPointer = &MyArray[0];                 // Create a pointer variable that points to the first value in the array.

Now whilst the code above is correct in practice you will never see this because for arrays you can drop the ‘&’ that is required to indicate you want the address of the Variable not its value. Note for variables that are not arrays i.e. only single values you must use the ‘&’ notation. For arrays the simplified code as shown below

int MyArray[5] = { 2, 5 , 3, 14, 300 };  // Define new array MyArray
int *myPointer;                          // Define pointer to integer type
myPointer = MyArray;                     // Create a pointer variable that points to the first value in the array.

You can now use the pointer to index the array consider the example given in the array section above

#define ARRAYSIZE 100             // define array size constant
int myArray[ARRAYSIZE];           // initialise array
int i;                            // initialise indexing variable
for (i=0; i<ARRAYSIZE; i++){      // loop through all array indices
  myArray[i] = i;                 // store the current index value in the array
}

Using pointers

#define ARRAYSIZE 100             // define array size constant
int myArray[ARRAYSIZE];           // initialise array
int *myPointer = myArray;         // initialise array pointer to first element of myArray
for (i=0; i<ARRAYSIZE; i++){      // loop through all array indices
  *myPointer = i;                 // store the current index value in the location pointed to by myPointer
  myPointer++;                    // increment pointer
}

This can be further reduced to

#define ARRAYSIZE 100             // define array size constant
int myArray[ARRAYSIZE];           // initialise array
int *myPointer = myArray;         // initialise array pointer to first element of myArray
for (i=0; i<ARRAYSIZE; i++){      // loop through all array indices
  *myPointer++ = i;               // store the current index value in the location pointed to by myPointer

}

Pointers really come into their own when you want to define functions that operate on variables and arrays without requiring the function to first make a local copy of the variable to use within the function. They also allow you to update multiple variables from within a function and make these accessible after the function has completed. You first need to define the variables that you wish to operate on as volatile variables before the main function. You then define the inputs to the function as pointers. In this way the function only creates a local copy of the pointer within the function not the variable itself. You can see that if your function operates on arrays this is going to be much more efficient as the pointer to the array is a single address, whereas to create a local copy of a large array would require the same amount of memory as the original array again. In this way when you pass the variables to the function only the pointer to the variable is passed. Inside the function no new variables are created and the function simply writes or changes the variables in the location pointed to by the pointer. Thus when the function finishes your original variables will have been changed.

As an example consider a function that takes in two char inputs and swaps them.

volatile char A;
volatile char B;

void SwapChars(char *a, char *b);   // Function prototype

void main(void){
  x1 = 'A';
  x2 = 'B';
  SwapChars(&x1, &x2);     // Pass the pointers to variables x1 and x2 to the function
}

void SwapChars(char *a, char *b){
  char c = 0;      // initialise temporary variable c
  P1OUT |= BIT6;            // Set P1.0
  c = *a;      // copy the value in memory location a in variable c
  *a = *b;     // copy the value stored in memory location b into memory location a
  *b = c;      // copy the value temporarily stored in c into memory location b
}

You can see that the function definition defines the inputs as pointers to chars. Inside the function the value stored at the location pointed to by a is temporarily stored in a variable c that is created within the function. The value stored at the location pointed to by b is then copied into location pointed to by a and finally the value of c is copied into the location pointed to by b. Note also when we call the function from within main it is necessary to use the & notation to ensure we pass the address to the variables not the variables themselves as these variables are not arrays.

The UARTSendArray function below is an example of using pointers to pass an array to a function.

char myString[5] = "Hello";
int data[2]={1023, 235};

void main(void){
UARTSendArray(myString, 5);   // Send character string in myString
UARTSendArray("Bye", 3);      // Send character string "Bye"
UARTSendArray(data, 4);       // Send integer in array data as bytes
}

void UARTSendArray(char *TxArray, int ArrayLength){
    while(ArrayLength--){             // Loop until StringLength == 0 and post decrement
    while(!(IFG2 & UCA0TXIFG));      // Wait for TX buffer to be ready for new data
    UCA0TXBUF = *TxArray++;           //Write the character at the location specified by the pointer and post increment
  }
}

This example also highlights another nice feature of pointers because the UART is only able to transmit 8 bit bytes the type that the TxArray pointer points to is defined as a char. However we can use this function to send the contents of an integer array one byte at a time. This works because when we call the function UARTSendArray(data, 4); the pointer TxArray created by the function simply points to the first address of data. Now when the value of the pointer address pointer is incremented by TxArrray++ it is only incremented to the address of the next byte as the data type that the pointer was defined as pointing to was char, even thought the original data array was defined as an integer. In order to transmit the entire data array as bytes it is therefore necessary to specify the array length as twice that of the integer array.

Pointers code full example

/* Example code demonstrating the use of pointers.
 * It uses the hardware UART on the MSP430G2553 to receive
 * and transmit data back to a host computer over the USB connection on the MSP430
 * launchpad.
 * Note: After programming using CCS it is necessary to stop debugging and reset the uC before
 * connecting the terminal program to transmit and receive characters.
 * This demo will transmit the characters AB in response to a S been sent each time S is sent the
 * order of the characters is swaped. If agen any other character is sent and unknown command response is sent.
 */

#include "msp430g2553.h"
void UARTSendArray(char *TxArray, int ArrayLength);
void UARTSendChar(char *TxChar);
void SwapChars(char *a, char *b);

volatile char data;
volatile char x1;
volatile char x2;

void main(void)

{
  WDTCTL = WDTPW + WDTHOLD; // Stop WDT

  P1DIR |= BIT0 + BIT6;    // Set the LEDs on P1.0, P1.6 as outputs
  P1OUT = BIT0;            // Set P1.0

    BCSCTL1 = CALBC1_1MHZ;   // Set DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;    // Set DCO to 1MHz

  /* Configure hardware UART */
  P1SEL = BIT1 + BIT2 ;    // P1.1 = RXD, P1.2=TXD
  P1SEL2 = BIT1 + BIT2 ;   // P1.1 = RXD, P1.2=TXD
  UCA0CTL1 |= UCSSEL_2;    // Use SMCLK
  UCA0BR0 = 104;           // Set baud rate to 9600 with 1MHz clock (Data Sheet 15.3.13)
  UCA0BR1 = 0;             // Set baud rate to 9600 with 1MHz clock
  UCA0MCTL = UCBRS0;       // Modulation UCBRSx = 1
  UCA0CTL1 &= ~UCSWRST;    // Initialize USCI state machine
  IE2 |= UCA0RXIE;         // Enable USCI_A0 RX interrupt

  x1 = 'A';
  x2 = 'B';

  __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, interrupts enabled
}

// Echo back RXed character, confirm TX buffer is ready first
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
  data = UCA0RXBUF;
  switch(data){
  case 'S':
    {
      SwapChars(&x1, &x2);     // Pass the pointers to variables x1 and x2 to the function

      UARTSendChar(x1);
      UARTSendChar(x2);
      UARTSendArray("\n\r", 2);
      P1OUT &= ~BIT6;            // Set P1.0
    }
    break;
  default:
    {
      UARTSendArray("Unknown Command: ", 17);
      UARTSendChar(data);
      UARTSendArray("\n\r", 2);
    }
    break;
  }
}

void UARTSendArray(char *TxArray, int ArrayLength){
  // Send number of bytes Specified in ArrayLength in the array at using the hardware UART 0
  // Example usage: UARTSendArray("Hello", 5);
  // int data[2]={1023, 235};
  // UARTSendArray(data, 4);  // Note because the UART transmits bytes it is necessary to send two bytes for each integer hence the data length is twice the array length

    while(ArrayLength--){             // Loop until StringLength == 0 and post decrement
    while(!(IFG2 & UCA0TXIFG));      // Wait for TX buffer to be ready for new data
    UCA0TXBUF = *TxArray++;           //Write the character at the location specified by the pointer and post increment
  }
}

void UARTSendChar(char *TxChar){
  // Send number of bytes Specified in ArrayLength in the array at using the hardware UART 0
  // Example usage: UARTSendArray('A');
  while(!(IFG2 & UCA0TXIFG));      // Wait for TX buffer to be ready for new data
  UCA0TXBUF = TxChar;           //Write the character at the location specified py the pointer
}

void SwapChars(char *a, char *b){
  char c = 0;      // initialise temporary variable c
  P1OUT |= BIT6;            // Set P1.0
  c = *a;      // copy the value in memory location a in variable c
  *a = *b;     // copy the value stored in memory location b into memory location a
  *b = c;      // copy the value temporarily stored in c into memory location b
}

10 thoughts on “c programming for MSP430

  1. this post is very nice put together. could you please explain why you declare the variables A and B as volatile in your swap example?

    • There is actually no need to declare A and B as volatile as in this code the values of A and B are only changed within the swapChars function. If however the values of A and B could also be changed within an interrupt then they would need to be declared volatile.

      • I believe that’s not what he meant… instead of declaring
        volatile char A;
        volatile char B;
        you should have declared
        volatile char x1;
        volatile char x2;
        since we dont use any valiable A nor B, but x1 and x2.

Leave a comment