I managed to build a very simple RS485 interface and test it.
Links to that part here :
http://fortytwoandnow.blogspot.ch/2012/06/msp430g2-serial-communication-1.html
http://fortytwoandnow.blogspot.ch/2012/06/msp430g2-serial-communication-2-rs485.html
Today I am dealing with a SPI device, in my case a MCP4131 digital potentiometer.
If you don't know what SPI is, or you want to dig a bit more into it, I suggest you read here :[wikipedia].
A digital pot is quite a simple device which receives commands on the serial bus (SPI in this case, it's pretty common) and switches a resistor network emulating the wiper of a potentiometer.
It is an handy device used in i.e. hifi equipment to digitally set the volume.
The device I use is from Microchip, it's the quite popular MCP4131 -103
Anyhow, today I will focus on the lancuhpad part, I am not really checking if the values are set correctly on the pot, that will be a task for later.
The MSP430G2 microcontrollers have two peripherals to handle the hardware serial communications : the USI and the USCI.
Depending on which exact device you are using, you may have one or the other.
"High end" devices such as the G2553 will have USCI.
A list of devices with details on which is supporting USCI and which USI can be found here (TI website).
The basic difference is that USCI will support hardware TTL UART on top of I2C and SPI.
Since USCI and USI use different registers (they are actually significantly different implementations of hardware serial interfaces), this post will not be helpful if your device needs to be programmed using USI.
The first thing to find out is where to connect the MISO, MOSI and SCK lines on the microcontroller.
The device specific datasheet reports the various pins with the functionality they support.
If you followed my previous posts (or any other instruction material on these MCUs) you probably know that each pin can be configured to provide different functions.
In this case we find that the pin P1.4 also supports the hardware serial port A0 (UCA0) clock (CLK) signal.
Normally we need to specifically set the "direction" of each pin we select, however in this case the datasheets notifies us that the USCI interface will manage that.
This is because the USCI can be configured as SPI master (this will be our case) or SPI slave and in the first case it will OUTPUT a clock signal, while in the second one it will RECEIVE a signal from the master (the pin direction is different in the two cases)
We also notice that the UCA0CLK is "mode 3", so it is selected setting high the bit 4 in both the P1SEL and P2SEL registers.
In a partially failed attempt to confuse me, TI called the MISO (Master In Slave Out) "SOMI" and the MOSI (Master Out Slave In) "SIMO".
Anyhow we can find them at pin P1.1 and P1.2, still mdoe 3 and direction automatically set by USCI.
The SPI bus also needs a CS (Chip select) which is normally generated via a normal GPIO port.
This is needed when you have many devices connected on the same SPI bus (SPI supports multiple slaves), normally you want to communicate to each one of them individually, so a different CS line will be used to enable only one at a time.
In this experiment we only have 1 device, so we do not need to dynamically "select" it.
First we need to set up the cpu clock, I will go "full throttle" at 16MHz (just because we can, don't think it's needed nor smart for this specific application).
I plan to use SMCLK to generate the clock for the serial interface and a quick look to the MCP4131 tells me it won't work at 16MHz as it is rated for 10MHz maximum.
4MHz sounds reasonable, so SMCLK will be MCLK / 4.
If you don't know how to operate the MSP430G2 clock (and you want to know more about it), you can check here.
/*
* main.c
*/
#include <msp430g2553.h>
unsigned char potLevel;
void clockConfig()
{
// configure the CPU clock (MCLK)
// to run from DCO @ 16MHz and SMCLK = DCO / 4
BCSCTL1 = CALBC1_16MHZ; // Set DCO
DCOCTL = CALDCO_16MHZ;
BCSCTL2= DIVS_2 + DIVM_0; // divider=4 for SMCLK and 1 for MCLK
}
The second thing will be to set mode 3 for pins 1,2 and 4
void pinConfig()
{
// set the pin mode 3 for pins 1 2 and 4 of port 1 (USCI mode)
P1SEL = BIT1 + BIT2 + BIT4; // low bit = 1 for pin 1 and 2 4. BIT 3 is 0 (CS via GPIO)
P1SEL2 = BIT1 + BIT2 + BIT4; // high bit = 1 for pin 1 and 2 4 BIT 3 is 0 (CS via GPIO)
P1DIR |= BIT3; // p1.3 set to output to drive CS
P1OUT &= ~BIT3; // pull p1.3 to low - CS low
}
Finally we have the most interesting part : configure USCI to work as a SPI Master.
Turns out it's pretty easy to achieve this, we will just need to :
- set the Control Register 0 for port UCA0 to be master, mode 0 and synchronous
- Select in the CR 1 the clock source to be SMCLK (UCSSEL_2)
- Set the Baud Rate registers for any additional divider we may need
- Disable any modulation
- Initialize the USCI
void spiConfig()
{
UCA0CTL0 |= UCCKPL+ UCMST + UCMODE_0 + UCSYNC;
// synchronous (=SPI) master 3 wire SPI, clock polarity High
/*SPI mode is selected when the UCSYNC bit is set and SPImode (3-pin or 4-pin) is selected with the UCMODEx bits.*/
//use SCLK : 4MHz (MCP4131 supports up to 10MHz write via SPI)
UCA0CTL1 |= UCSSEL_2;
// set baud rate = SMCLK, no further division
UCA0BR0 = 0;
UCA0BR1 = 0;
UCA0MCTL = 0; // No modulation
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI **
} Clock polarity can be set "high" or "low" using the UCCKPL bit in CR 0, to better understand what that means I suggest you check the video embedded below.
Finally I add a simple procedure to send out the SPI command to the digital pot.
According to the MCP4131 datasheet, to move the wiper (7 bit resolution) of the pot, we need to send command 0 (1 byte) followed by 1 byte with the value of the wiper position (0-127).
I added a small delay between the two bytes as that makes it easier for me to read the signal on the oscilloscope, might need to increase, lower or remove that delay in normal operations.
// drives a MCP4131 digital potentiometer via SPI
void setPotValue(unsigned char dataOut)
{
while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
UCA0TXBUF = 0; // Send command 0
__delay_cycles(50);
while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
UCA0TXBUF = dataOut; // Send wiper level
__delay_cycles(50);
}
and finally a main procedure that puts everything together.
void main(void)
{
// setup
WDTCTL = WDTPW + WDTHOLD;
clockConfig();
pinConfig();
spiConfig();
// program execution loop
while (1)
{
for (potLevel=0;potLevel<128;potLevel++)
{
setPotValue(potLevel);
__delay_cycles(100000); // I know, there are better ways...
}
for (potLevel=127;potLevel>0;potLevel--)
{
setPotValue(potLevel);
__delay_cycles(100000);
}
}
}
Time to build and load into the launchpad.
I connected my digital oscilloscope (100MHz, dual channel) to the clock and to MOSI.
The first thing I wanted to check (after verifying that indeed some clock and some data are actually sent over the SPI bus) is the clock frequency.
Manually placing the measurement cursors at the end of the rising edge of the clock, I found 4.032MHz, which is pretty close to the 4MHz we expected (need to account for some manual error in the way I placed the cursors and some tolerance in the DCO frequency).
Then I checked that the value passed as wiper level was changing over time, this is better visualized in the video I recorded
Now that it seems data is flowing through the SPI bus, I need to check it is correct and see if the MCP4131 receives it correctly, by monitoring he value of it's resistance... and this will be the topic of a future post, probably :)