Search Microcontrollers

Thursday, August 2, 2012

MSP430G2 - (USCI) SPI interface 3

I learnt something and that, by itself, its awesome.

In the previous post I discussed an issue I found with my procedure that handles the SPI communication between the launchpad and a digital pot.

Communication was working until I configured the SPI master to send out bits starting from MSB instead of LSB (like it should).
At that point the digital pot was not responding anymore (before it was responding and getting wrong values as bits were reversed).
Digging into the issue I found that with the MSB setting the MOSI line was keeping the value of the last bit sent, even after the communication was over.

I think it was a fair assumption since with LSB first I saw each time the MOSI line drop to low... but it was a wrong assumption.
Since I was sending 7 bit values, when they are serialized starting from LSB, the last bit in a (8 bit) byte is always ZERO, so the mosi line was still keeping the value of the last bit sent, regardless of MSB/LSB settings.

Posted a question about this on the TI forum and that's how I learnt something!
SPI does not define any specific idle state for data (miso/mosi) lines (while it does for the clock signal), so the behavior I found is "normal".
However, since I am experimenting with one single slave on the bus, I disregarded the Chip Select (CS) management and apparently this, in combination with the mosi line changing idle state, caused the problem.
I think this has to do with the SPI mode selection of the mcp4131, but I found the datasheet a bit cryptic on this subject, so I will not quote it here.

So I added the CS management and also switched to the USCI port B.

void setPotValue(unsigned char dataOut)
{
P1OUT &= ~BIT3; // enable slave (CS to Low)
UCB0TXBUF = 0;  // Send command
while (!(IFG2 & UCB0TXIFG)); // wait for TX buffer ready
UCB0TXBUF = dataOut; // Send wiper level
while (!(IFG2 & UCB0TXIFG)); // wait for TX buffer ready
P1OUT |= BIT3; // disable Slave (CS to High)
}

The CS signal has to be driven LOW to enable the chip and high to disable it, this wraps the data transmission.

Still something did not work, and checking with the Oscilloscope I found this :
(In red the clock, in yellow the CS line)


CS is enabled correctly right before the clock starts to oscillate, but then it is disabled BEFORE the last bit is sent.
This is happening because I was just waiting for the TX buffer to complete, which does not ensure that physically the bits have been transmitted on the line.

The UCXxSTAT status register has a bit (UCBUSY) that indicates if a TX or RX operation is in process.
Adding the following line

while (UCB0STAT & UCBUSY); // wait for the tx to complete

before disabling CS fixed the issue


And the digital pot output is (used as a voltage divider between vcc and gnd) :





Finally, the overall code (uses port usci B on pins clk = 1.5 , mosi = 1.7 ) is :


#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
}

void pinConfig()
{
 // set the pin mode 3 for pins 5,6 & 7 of port 1 (USCI mode)
 P1SEL = BIT5 + BIT6 + BIT7; // low bit = 1 for pins 5,6 and 7 BIT 3 is 0 (CS via GPIO)
 P1SEL2 = BIT5 + BIT6 + BIT7; // high bit = 1 for pins 5,6 and 7 BIT 3 is 0 (CS via GPIO)
 P1DIR |= BIT3; // p1.3 set to output to drive CS
 P1OUT |= BIT3; // pull p1.3 to high - CS high -> chip is disabled
}

// USCI
void spiConfig()
{
 UCB0CTL1 = UCSWRST; // reset
 UCB0CTL0 |= UCCKPL +UCMST  + UCSYNC +UCMSB; // synchronous (=SPI) master 3 wire SPI, clock polarity High
 UCB0CTL1 |= UCSSEL_2; //use SCLK : 4MHz (MCP4131 supports up to 10MHz write via SPI)
  // set baud rate = SMCLK, no further division
 UCB0BR0 = 0;
 UCB0BR1 = 0;
 UCB0CTL1 &= ~UCSWRST; // **Initialize USCI **
}

// drives a MCP4131 digital potentiometer via SPI
void setPotValue(unsigned char dataOut)
{
 P1OUT &= ~BIT3; // enable slave (CS to Low)
 UCB0TXBUF = 0;  // Send command
 while (!(IFG2 & UCB0TXIFG)); // wait for TX buffer ready
 UCB0TXBUF = dataOut; // Send wiper level
 while (!(IFG2 & UCB0TXIFG)); // wait for TX buffer ready
 while (UCB0STAT & UCBUSY); // wait for the tx to complete
 P1OUT |= BIT3; // disable Slave (CS to High)
}

void main(void)
{
// setup
 WDTCTL = WDTPW + WDTHOLD;
 clockConfig();
 pinConfig();
 spiConfig();
    // program execution loop
 while (1)
 {
  for (potLevel=0;potLevel<127;potLevel++)
  {
   setPotValue(potLevel);
   __delay_cycles(20000);
  }
  for (potLevel=127;potLevel>0;potLevel--)
  {
   setPotValue(potLevel);
   __delay_cycles(20000); 
  }
 }}


4 comments:

??? said...

I would like to thank you for this very helpful article! It really made setting up SPI easy on the G2553! I am building an RC car using TI RF modules which use SPI protocol with MCUs. The car itself will have a C2000 Launchpad while the controller will have an MSP430 Launchpad. I am currently testing out wireless comm between these two microcontrollers, and this article has been great help! You can find more info about my project, if you'd like, on www.launchpadtimes.com

Thanks!

Franz said...

Hello, glad you found the article useful.
Had a quick peak at your blog and it really looks interesting!
I will dive into it later more in de tail. Hack on!

Unknown said...

Thanks for posting this tutorial. It's been very helpful. I was hoping you could let me know what size resistor you used for the multiplexed SDI/SDO pin set up. The data sheet shows a resistor and I am not sure how to size it.

Take care,

Jon

Franz said...

Hello, glad you liked the tutorial.
Good question : I did not use a resistor as I believe it is used only if you need to READ data from the pot.
In that case a weak pull-up resistor could be (just guessing it) something like 4.7K or 10K

This is what the Datasheet states :

1: The 8-lead Single Potentiometer devices are pin limited so the SDO pin is multiplexed with the SDI pin
(SDI/SDO pin). After the Address/Command (first 6-bits) are received, If a valid Read command has been
requested, the SDO pin starts driving the requested read data onto the SDI/SDO pin.
2: The pin’s “smart” pull-up shuts off while the pin is forced low. This is done to reduce the standby and
shutdown current.
3: The SDO is an open drain output, which uses the internal “smart” pull-up. The SDI input data rate can be
at the maximum SPI frequency. the SDO output data rate will be limited by the “speed” of the pull-up,
customers can increase the rate with external pull-up resistors