Search Microcontrollers

Wednesday, August 1, 2012

MSP430G2 - (USCI) SPI interface 2

Time to check what's happening on the other end of the bus.
Nope, it's not a school-bus, it's a SPI bus which I started discussing here, if you missed the first part, you will probably not be able to follow this one (but we have a solution for that, right? Uhm, let me think... yeah! how about you check that one first?).

In the last post I showed how to output some data on the SPI bus of a MSP430G2553 MCU, but I did not really check if the data was correct or not for the connected device (MCP4131).
Turns out it was not :)

Particularly the procedure :

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);

}

had a few issues.
The first one was immediately visible from the oscilloscope output : apparently the level byte was sent BEFORE the command.

That happened because the "wait for TX ready" was placed before the instruction that fills the TX buffer, so the wiper level of the previous command was sent right before the command zero (you can verify that in the video of the previous post).

void setPotValue(unsigned char dataOut)
{
   UCA0TXBUF = 0; // Send command 0
   while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
   UCA0TXBUF = dataOut; // Send wiper level
   
while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
}

Ok, that's an easy fix, I also removed the delay which is not really needed, it was there just for debug purposes.
The result is this :


Much better, using the cursor I was also able to check that all the 8 bits of the command were sent before the value : it works.

The other thing I had to check was the clock polarity (also discussed in the previous post), and for this the MCP4131 Datasheet provides the needed information.


It seems it can work either way (called mode 1.1 and 0.0), I decided to go for 1.1, clock polarity high.
We should be all set, time to probe what's happening with the wiper.
Since oscilloscopes can measure voltages, but not directly resistance, I created a simple voltage divider using the digital pot terminals, so that the voltage on the wiper varies based on its position.

Now, lowering considerably the scan frequency of the scope, I would expect to see a ramp from 0 to 127 and from 127 down to 0, basically a triangle waveform with low frequency.
Instead this is what I got.


Doh!
Not quite a triangle eh?
The issues appears quite obvious : see the signal is going to zero every other change?
That means that the highest bit is changing at each iteration, but in a normal increment  that should happen to the lowest bit... unless you reversed your byte.
Yop, SPI can send MSB first or LSB first and, guess what, the slave needs MSB first and I am sending LSB first instead.
That did not affect the command ( 0 ) since it is symmetric, but it did absolutely affect the wiper level byte.

USCI has a bit to configure MSB /LSB settings, so I updated the spiConfig procedure : 
UCA0CTL0 |=   UCCKPL +UCMST + UCMODE_0 + UCSYNC + UCMSB;

Did it work?
Nope!! I got a flat steady value on the wiper level, so I checked again what was going through the SPI bus and I found this : 


  
Oh! This is NOT nice.
Turns out (don't know the reason yet) that if the last bit sent is high, the spi bus is maintained high instead of falling back to low.
This happens only if you set the UCMSB flag in the configuration (Control Register 0 ).
Not sure what's happening there, I might be using the wrong constant, will digg deeper, anyhow there is an easy workaround : reverse the byte via software while keeping the hardware settings to LSB.

I did a simple test first, while in LSB first mode, I replaced the ramp generation with a single bit output step with the following sequence :
128 , 64, 32, 16, 8, 4, 2, 1

And this was the result (red line) :

And this confirms that the bits are reversed, so the setPotValue proc becomes
void setPotValue(unsigned char dataOut)
{
unsigned char revData;
UCA0TXBUF = 0;  // Send command
revData=0;
int i;
unsigned char bit;
for (i=0;i<8;i++)
{
bit = dataOut & 1;
dataOut = dataOut >> 1;
revData = revData << 1;
if (bit==1) revData++;
}
while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
UCA0TXBUF = revData;//dataOut; // Send wiper level
while (!(IFG2 & UCA0TXIFG)); // wait for TX buffer ready
}

The ramp generation is reactivated again,and... tadaaa!


The workaround is a temporary solution until I figure out how to fix this MSB issue, however it's not too bad as it's not affecting the code execution performance since the byte reversal is done while waiting for the SPI TX buffer to be ready to receive another byte.

I am now (almost) ready to plug this circuit into the rest of my experiment, which involves 80V, 5.5Amps, some FET, a Hall effect current sensor... and probably some "magic smoke" :)

Before working on that one, however, I thought it might be useful to consider an interesting detail of the USCI serial :
It actually has two ports (A and B) and while port A supports UART,SPI, I2C, port B supports "only" SPI and I2C.
It would probably make sense in my case to switch my spi bus to port B and free up port A to drive a RS485 uart.
It's probably a good idea, if you are using a single SPI or I2C, to drive it with port B. 

No comments: