Hi,
I've been looking at SPI implementations for the last two days, and again, I feel like this is too technical, plus with my limited knowledge about embedded, I just feel lost.
I've read a couple of posts here, and copy pasted some code, but I have no idea where to start.
I'm using the MSP430F5438 microprocessor on our prototype, and if I got it right from the datasheet, it has 4 SPIs?
My situation is such, I have two serial slave components I need to program on initialization, and read data from while the application is running, to be more specific, temperature and accelerometer.
So I'm wondering, according to this on page 57, it seems that the same SPI can be used for multiple devices, is my understanding correct?
I've also had trouble with understanding which registers control what from the datasheet, there is a long register list, and it seems that most of these have many functions, how can distinguish what I need from the list?
Thanks,
Adam.
Java Developer & ArchitectNu-Art Software
-----LinkedInTwitter-----
To be a good student, you must first be a good teacher.
Adam Zehavi I've been looking at SPI implementations for the last two days, and again, I feel like this is too technical, plus with my limited knowledge about embedded, I just feel lost.
Do you understand the basic premise of SPI operations and how the physical interface is intended to operate? I don't want to assume anything.
Adam Zehavi I'm using the MSP430F5438 microprocessor on our prototype, and if I got it right from the datasheet, it has 4 SPIs?
The MSP430F5438 has 4 instances of the USCI module, which each module has 2 parts (A and B). As the datasheet indicates, each part of the USCI module can support SPI operations. Therefore, technically the MSP430F5438 can support up to 8 unique SPI interfaces if each part of the USCI modules are configured for that mode of operation.
Adam Zehavi My situation is such, I have two serial slave components I need to program on initialization, and read data from while the application is running, to be more specific, temperature and accelerometer. So I'm wondering, according to this on page 57, it seems that the same SPI can be used for multiple devices, is my understanding correct?
Yes, that is true. The document you reference is suggesting to use 2 I/O pins to act as chip selects, a unique signal for each slave device. Your software then needs to comprehend which device you are interacting with by controlling the I/O directly. This means the USCI module does not control the chip select I/O itself, but rather you higher level driver code running on the processor needs to control this.
Using this method does save on the number of pins consumed for connecting to SPI slaves. If you assume only 3 signals are used for a specific SPI slave (CLK, SOMI, SIMO) and the chip select is held active on the slave, to support 2 slave with 2 separate SPI interfaces, you would consume 6 signals (ie. 2 * (CLK, SOMI, SIMO)).If you use the chip select method, then 2 chip selects and the common CLK, SOMI, SIMO woudl be 5 signals.The trade off is some complexity with the software to manage this.
Adam Zehavi I've also had trouble with understanding which registers control what from the datasheet, there is a long register list, and it seems that most of these have many functions, how can distinguish what I need from the list?
You will need to consult the MSP430x5xx Family User's Guide for specifics about the various modules functionality and register definitions. There are also code examples to help illustrate concepts that you could consult as well.
Brandon
Thank you for your response...
BrandonAzbellDo you understand the basic premise of SPI operations and how the physical interface is intended to operate? I don't want to assume anything.
Well, I'll go and assume that this is like a Java Input output stream, where data is been queued, and then a flush signals the other side it has content to read, from the proper port, or something similar.
BrandonAzbellYou will need to consult the MSP430x5xx Family User's Guide for specifics about the various modules functionality and register definitions. There are also code examples to help illustrate concepts that you could consult as well.
I'm using MSP430F5438, and the confusion I'm having reading the datasheet is the fact that each register has multiple functions, for example:
General-purpose digital I/O
Slave transmit enable – USCI_B0 SPI mode
Clock signal input – USCI_A0 SPI slave mode
Clock signal output – USCI_A0 SPI master mode
Slave in, master out – USCI_B0 SPI mode
I2C data – USCI_B0 I2C mode
Slave out, master in – USCI_B0 SPI mode
I2C clock – USCI_B0 I2C mode
Clock signal input – USCI_B0 SPI slave mode
Clock signal output – USCI_B0 SPI master mode
Slave transmit enable – USCI_A0 SPI mode
This is the setup from the datasheet of the micro-controller.
Why does each have multiple names?
P3.0, and P3.3 confuse me, because of the similarity in their name though 'A' and 'B' are opposite...!?
Why does each have a few descriptions which seem completely different (for the untrained eye)?
What do I need to look for, when reading the datasheet of the HW component now connected to the microcontroller?
Thanks in advance,
Adam ZehaviI'm using MSP430F5438, and the confusion I'm having reading the datasheet is the fact that each register has multiple functions
In the case of P3.0 and P3.3 Table 44. Port P3 (P3.0 to P3.7) Pin Functions has some notes about UCA0CLK function takeing precedence over UCB0STE function.
Adam Zehavi I'm using MSP430F5438, and the confusion I'm having reading the datasheet is the fact that each register has multiple functions, for example: P3.0/UCB0STE/UCA0CLK 33 35 General-purpose digital I/O Slave transmit enable – USCI_B0 SPI mode Clock signal input – USCI_A0 SPI slave mode Clock signal output – USCI_A0 SPI master mode P3.1/UCB0SIMO/UCB0SDA 34 36 General-purpose digital I/O Slave in, master out – USCI_B0 SPI mode I2C data – USCI_B0 I2C mode P3.2/UCB0SOMI/UCB0SCL 35 37 General-purpose digital I/O Slave out, master in – USCI_B0 SPI mode I2C clock – USCI_B0 I2C mode P3.3/UCB0CLK/UCA0STE 36 38 General-purpose digital I/O Clock signal input – USCI_B0 SPI slave mode Clock signal output – USCI_B0 SPI master mode Slave transmit enable – USCI_A0 SPI mode DVSS3 37 39 Digital ground supply This is the setup from the datasheet of the micro-controller. Why does each have multiple names?
For the pins you have listed above, each has multiple possible functions that can be multiplexed out. As Chester mentioned either Digital I/O (eg. P3.0) or a peripheral functional signal (eg. UCB0STE or UCA0CLK). Only one of these is available on the pin at any given time and the configuration is description in the Input/Output Schematics section of the datasheet.
Adam Zehavi P3.0, and P3.3 confuse me, because of the similarity in their name though 'A' and 'B' are opposite...!?
Recall my comment that each USCI module has 2 parts (or sub-units), the USCI_A and USCI_B sub-modules. This is described in the MSP430x5xx Family User's Guide. The A and the B denote which of the subunits is being referenced.
Adam Zehavi Why does each have a few descriptions which seem completely different (for the untrained eye)?
Again, each description is related to a particular function which can multiplexed on the pin. Only 1 of the functions can be active at any given time.
Adam Zehavi What do I need to look for, when reading the datasheet of the HW component now connected to the microcontroller?
Don't know what you are asking here. You should look at both the datasheet of the microcontroller to get an idea of what functionality is available on the specific device you are using and use the Family User's Guide for details about a module that is instantiated on the device.
Hi Adam Zehavi,
Basic knowledge of SPI protocol (like what is the functionality of CS, MOSI , MISO , CLK ) and taking a look into the code samples from TI (you will get the library & examples when you install CCS) will be the starting point.
When reading through the code, you can better understand how a pin is "configured" to work as an SPI pin for eg. Interfacing slaves like accelerometer, temperature sensor etc are posted throughout the forum. A little search would help there.
ATB,
-Venkat.
Adam ZehaviWell, I'll go and assume that this is like a Java Input output stream, where data is been queued
An SPI interface is barely more than two shift registers and a shift clock generator and some control circuitry.
After configuring the baudrate and the port pins, the SPI hardware takes a byte you write to its transmit buffer and shifts it out bit by bit together with a clock signal. At the same time, a second shift register is shifting in data from the opposite direction and when 8 bits have been received, the resulting byte is moved into the receive buffer register.However, this is just a one-byte buffer. You have to check whether it is empty. (dedicated interrupt flags will allow you to either read the state or let an interrupt function to be called when the state changes)However, you cannot simply put a bunch of bytes into a stream queue - unless you write this queue yourself.
Besides this low-level handling (and the even lower-level initialization) you'll have to deal with the higher-level things such as (software-controlled) chip select signals to notify the slave that you want to send it data or receive from it.
I don't recommend to start with writing an SPI slave. IT requires tight, timing-critical code and is the most demanding of all serial communications tasks.
There are lots of threads about SPI in this forum. You should take a look at them.
Adam ZehaviWhy does each have multiple names?
In case of P3.1, the pin can carry the Port 3 Bit 1 GPIO (P3.1) or USCI B0 module (UCB0) signal. In case of UCB0, this module can operate in I2C mode (then this pin is the serial data signal) or in SPI mode. In SPI mode, it is either slave output (if the mode is in SPI slave mode) or master input (if in SPI master mode). Since USCI B0 can only be in one mode at a time, only one of the different signals is routed to the pin at a time, depending on configuration.
Adam ZehaviP3.0, and P3.3 confuse me, because of the similarity in their name though 'A' and 'B' are opposite...!?
Adam ZehaviWhat do I need to look for, when reading the datasheet of the HW component now connected to the microcontroller?
_____________________________________Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.
First, thank you all for your responses, this is tone of info, and it took a while to process it.
I've read everything, and tried to make sense of the example SPI master code supplied in a previous link:
//****************************************************************************** // MSP430F54x Demo - USCI_A0, SPI 3-Wire Master Incremented Data // // Description: SPI master talks to SPI slave using 3-wire mode. Incrementing // data is sent by the master starting at 0x01. Received data is expected to // be same as the previous transmission. USCI RX ISR is used to handle // communication with the CPU, normally in LPM0. If high, P1.0 indicates // valid data reception. Because all execution after LPM0 is in ISRs, // initialization waits for DCO to stabilize against ACLK. // ACLK = ~32.768kHz, MCLK = SMCLK = DCO ~ 1048kHz. BRCLK = SMCLK/2 // // Use with SPI Slave Data Echo code example. If slave is in debug mode, P1.1 // slave reset signal conflicts with slave's JTAG; to work around, use IAR's // "Release JTAG on Go" on slave device. If breakpoints are set in // slave RX ISR, master must stopped also to avoid overrunning slave // RXBUF. // // MSP430F5438 // ----------------- // /|\| | // | | | // --|RST P1.0|-> LED // | | // | P3.4|-> Data Out (UCA0SIMO) // | | // | P3.5|<- Data In (UCA0SOMI) // | | // Slave reset <-|P1.1 P3.0|-> Serial Clock Out (UCA0CLK) // // // W. Goh // Texas Instruments Inc. // November 2008 // Built with CCE Version: 3.2.2 and IAR Embedded Workbench Version: 4.11B //****************************************************************************** #include "msp430F5438.h" unsigned char MST_Data,SLV_Data; void main(void) { WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer P1OUT |= 0x02; // Set P1.0 for LED // Set P1.1 for slave reset P1DIR |= 0x03; // Set P1.0-2 to output direction P3SEL |= 0x31; // P3.5,4,0 option select UCA0CTL1 |= UCSWRST; // **Put state machine in reset** UCA0CTL0 |= UCMST+UCSYNC+UCCKPL+UCMSB; // 3-pin, 8-bit SPI master // Clock polarity high, MSB UCA0CTL1 |= UCSSEL_2; // SMCLK UCA0BR0 = 0x02; // /2 UCA0BR1 = 0; // UCA0MCTL = 0; // No modulation UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt P1OUT &= ~0x02; // Now with SPI signals initialized, P1OUT |= 0x02; // reset slave __delay_cycles(100); // Wait for slave to initialize MST_Data = 0x01; // Initialize data values SLV_Data = 0x00; // while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = MST_Data; // Transmit first character __bis_SR_register(LPM0_bits + GIE); // CPU off, enable interrupts while(1); } #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR(void) { switch(__even_in_range(UCA0IV,4)) { case 0: break; // Vector 0 - no interrupt case 2: // Vector 2 - RXIFG while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready? if (UCA0RXBUF==SLV_Data) // Test for correct character RX'd P1OUT |= 0x01; // If correct, light LED else P1OUT &= ~0x01; // If incorrect, clear LED MST_Data++; // Increment data SLV_Data++; UCA0TXBUF = MST_Data; // Send next value __delay_cycles(10); // Add time between transmissions to // make sure slave can process information break; case 4: break; // Vector 4 - TXIFG default: break; } }
(WOW took me forever to find a good code converter - I'm fixing it... it didn't look like this on the editor!!!)
So if I understand this example correctly, then I was off by a mile with the impression I had of SPI when I started.
while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?CA0TXBUF = nextValue; // Send next value__delay_cycles(40);
There think there might be a bug in the forum posting editor... if I've copied the text, and changed the font to (in my case) courier new, it gets messed up, and if I don't set any font, then I guess you can see the result...
Adam ZehaviDo I only need to write the byte to the proper TX, and the system would execute the bit by bit transaction?
However, this code uses soem implicit mechanism: When you sent a byte, you already received a byte, which causes an interrupt. (case 2). So an initial write to TXBUF is necessary.However, the 'usual' way would be enabling the interupt for TX (case 4) and when it comes wither write to TXBUF inside the ISR (since you got the interrupt, you know that TXBUF is ready for the next byte, no need to do busy-waiting inside an ISR) or (if no more bytes to be sent) clear the TXIE bit so no more interrupts come. The default case in the ISR is superfluous too: since the switch was wrtten with '__even_in_range(xxx,4)' there will be case 0,2,4 only, by definition. No need for a default.
Adam ZehaviAnd in order to write a 16 bit data to the SPI, I would have to add a:...
If you do busy-waiting of some sort inside an ISR, you could as well move all the ISR code into main() and execute it sequentially.
Adam ZehaviIf I need to write some parameters to the component and program it, then I need an SPI Master, but later I need to get data back, so do I need also an SPI slave?
The master is the peer who decides when and how fast to transfer data. The transfer itself is bi-directional. If you e.g. want to write to an external memory, the MSP is the master and the memory is the slave.
To write, the master sends a write command, the address to write to, the number of bytes to write, and the data. At the same time, the memory is sending dummy values back. After the last data byte has been written, the master may continue sending dummy bytes while the slave responds with a 'busy' response, until the slave sends a 'done' or 'failure' byte.When reading data, the MSP is still the master. It sends a read command, an address and (maybe, as this is superfluous) how many bytes to read. Then it continues sending dummy bytes and the slave will return one data byte for each. Once the last byte has been received, the master will simply stop sending bytes and deselect the slave.
In both cases, the MSP has controlled when to do a transfer and at which speed (by controlling the SPi clock signal). However, the slave in this example did have a way to tell the master that it is not done yet (writing to flash memory takes some time).
Don't forget that the answer to data sent can arrive soonest after the next byte sent (possibily a dummy byte). This, and the double-buffering of the USCI (one byte currently being sent in the output shift register, and one byte waiting in TXBUF), can make complex protocols a challenge.
Jens-Michael, thank you for your help, and your time.
Things are much clearer now regarding the SPI communication protocol implementation, but some things are still a miss, so I would try to be more accurate.
As for transferring 8*x bit, if I understand correct, I would have to know the x byte final values I want to send, and write them to the TX, one by one, on the SPI mater ISR iterations, and since the interrupt is defined to be invoked on RX completion, I can safely assume that the buffer is ready for the next TX byte, is this correct?
Jens-Michael Grossthe master may continue sending dummy bytes while the slave responds with a 'busy' response, until the slave sends a 'done' or 'failure' byte
I guessed that while configuring an SPI component, I would require some sort of a feedback from it, and sending dummy bytes, until an ending response is received seems like a nice technic.
Jens-Michael GrossWhen reading data, the MSP is still the slave.
I'm assuming you mean "MSP is still the master"?
Jens-Michael GrossAdam ZehaviIf I need to write some parameters to the component and program it, then I need an SPI Master, but later I need to get data back, so do I need also an SPI slave?Well, it depends. It depends on who is controllign the timing.
Due to my previous question in this post, I think I need to rephrase, I need to configure an Accelerometer, and later while the application is running (once a remote BT peer connects) I want the Accelerometer to start output its values and aggregate these to the application, and later send the acceleration data via Bluetooth to a remote BT peer.
So upon startup I configure the Accelerometer, supply parameters, and set the component activation to "pause".
Later when the application is running, and a BT peer is connected to the MSP, I set the component activation to "resume", how can I then define a sampling rate to read the acceleration values? Should I setup a timer to start reading the acceleration values from the component, at whatever rate allowed/supported by the component?
I've failed to understand the meaning of the following lines:
P1OUT |= 0x02; // Set P1.1 for slave reset
.... USCI (SPI) configuration ....
P1OUT &= ~0x02; // Now with SPI signals initialized, P1OUT |= 0x02; // reset slave
What is the reason for toggling the bit?
Adam ZehaviI've failed to understand the meaning of the following lines: P1OUT |= 0x02; // Set P1.1 for slave reset
Normally, a master won't reset the slave. it will only select it and communicate. However, some slaves might need a reset. Not because of the SPI, but because of their main function. E.g. if it is a display controller and the communication protocol does not cover a (software) reset command.
Adam ZehaviI'm assuming you mean "MSP is still the master"?
Adam ZehaviAs for transferring 8*x bit, if I understand correct, I would have to know the x byte final values I want to send, and write them to the TX, one by one, on the SPI mater ISR iterations, and since the interrupt is defined to be invoked on RX completion, I can safely assume that the buffer is ready for the next TX byte, is this correct?
Well, getting that tight to the max is the 'high art' of embedded programming :)
Adam ZehaviShould I setup a timer to start reading the acceleration values from the component, at whatever rate allowed/supported by the component?
Thank you.
I can now say, that together with your expert help and the demo app, I'm able to wrap my mine around the entire concept. My next goal is to write a mechanizem that would accomplish this, and communicate to multiple components, to start with, each component with different USCI.
Jens-Michael GrossThis line seems to be connected to the slaves RESET pin in this specific demo setup. It is poorly documented for an example code. Also, the following code makes some implicit assumptions about slave startup time. Not a good habit for demo code too, as any change in the code may break the timing.
How could a code change break the timing?
Assuming that the code was meant as a demo application for a strip MSP, checking the MSP P1.1 states:
Why was this port chosen for reset?
FYI, the link at the bottom is broken...
Adam ZehaviHow could a code change break the timing?
However, if you add some functionality to the slave, or reorder the master code, without taking this relation into account, the amster wills tart querying the slave when the slave isn't ready to answer. Since there is no error handling (or even error-detection) in the demo code, this will make th ewhole applicaiton to no longer work. And you don't know why, as this is not really documented.
Adam ZehaviWhy was this port chosen for reset?
I've completed a simple setup for the accelerometer SPI.
I've taken the demo application and integrated to it some infrastructure, carefully changing the code while verifying the original application behavior remains!
/** * Modules Configuration. */ USCI_Configuration accelerometerUSCI_Configuration = { UCSWRST, UCSYNC + UCMST + UCMSB + UCCKPL + UCCKPH, UCSSEL__SMCLK, 0x02, 0, 0} ; USCI_Module usciA3_Module = { &UCA3TXBUF, &UCA3RXBUF, &UCA3CTL1, &UCA3CTL0, &UCA3BR0, &UCA3BR1, &UCA3MCTL }; Interrupt usciA3_Interrupt = { &UCA3IE, &UCA3IFG, &UCA3IV }; /** * Acceleroemter definition. */ SPI_Component spiAccelerometer = { { &port10, BIT0, PORT_OUT }, { &port10, BIT3, PORT_OUT }, { &port10, BIT4, PORT_OUT }, { &port10, BIT5, PORT_IN } }; Accelerometer accelerometer = { &spiAccelerometer, &usciA3_Module, &accelerometerUSCI_Configuration, { &port1, BIT5, PORT_IN }, { &port1, BIT6, PORT_IN } }; UCHAR packet[6]; int index = 0; volatile UCHAR received[100]; int receivedIndex = 0; void sendNextPacket(void) { // *(usciA3_Module.outputBuffer) = MST_Data; // Send next value if (index < 6) { *(usciA3_Module.outputBuffer) = packet[index++]; // Send next value } } void main(void) { packet[0] = XL346_DATA_FORMAT; packet[0] |= 0x40; packet[1] = XL346_RANGE_8G; packet[2] = XL346_POWER_CTL; packet[2] |= 0x40; packet[3] = XL346_MEASURE; packet[4] = XL346_INT_ENABLE; packet[4] |= 0x40; packet[5] = XL346_DATAREADY; WDTCTL = WDTPW + WDTHOLD; setupRegister(redIndicationLed); setupRegister(greenIndicationLed); writeValueToRegister(greenIndicationLed, PORT_OFF); initializeAccelerometer(accelerometer); *(usciA3_Interrupt.controller) |= usciInterruptControls.receiver; // Enable USCI_A0 RX interrupt while (!(*(usciA3_Interrupt.flags) & usciInterruptFlags.transmiter)) ; sendNextPacket(); __bis_SR_register(LPM0_bits + GIE); while (1) ; } #pragma vector=TIMER0_A1_VECTOR __interrupt void AccelerometerISR(void) { if (TA0IV & TA0CCR4 != TA0CCR4) { return; } } #pragma vector=USCI_A3_VECTOR __interrupt void USCI_A3_ISR(void) { switch (__even_in_range(*(usciA3_Interrupt.vector), 4)) { case 0: break; // Vector 0 - no interrupt case 2: // Vector 2 - RXIFG received[receivedIndex++] = *(usciA3_Module.inputBuffer); sendNextPacket(); break; case 4: break; // Vector 4 - TXIFG } }
I'm pretty positive I've configured the component, and its SPI registers properly, and also the RXIFG event arrives, but the data received is always empty, and the interrupt that is supposed to be invoked by the component does not occur, am I sending the data wrong, and therefore I get no feedback?