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#pragma vector=TIMER0_A1_VECTOR __interrupt void AccelerometerISR(void) { if (TA0IV & TA0CCR4 != TA0CCR4) { return; } }
Your variable receivedIndex should be declared volatile. It is changed inside the ISR and (perhaps) indended to be later used in your main code (currently, received[] and receivedIndex seem to be write-only :)
I don't know what framework you use, and known none of them good enough to know what your initializations do and whether they are correct.However, what do you mean wiht 'the data received is always empty'? Is usciA3_Module.inputBuffer always 0x00 or 0xff?YOu say teh RX event arrives, but the interrupt does not occur? What do you mean by that? The RX event usually is the interrupt. Either you check for the RXIFG bit manually (with RXIE clear) or lett the ISR be called (with RXIE set).
Adam Zehavi packet[0] = XL346_DATA_FORMAT; packet[0] |= 0x40;
However, for each byte youe send, you'll receive a ybte. Whether it was intentionally sent by the slave or not. YOu will receive a byte even if yo udo not connect the pins at all. The SPI hardware cannot possible know whether incoming data on the SOMI (or MISO) line is valid or not. It just samples the current state of the line at the clock edges.Your slave can of course only answer to a command once it has received the command. But when you're done with sending the command, you stop sending comletely. So the slave cannot send its answer as no clock is generated.At the moment you put packet[5] into TXBUF, paket[4] has just started to be sent.You must wait until both are really send (e.g. by checking the UCBUSY bit).Then you can send one (or more) dummy byte(s) that allows the slave to send its answer. (you may simply send one mroe byte, but be sure to pick teh correct answer byte then - your sending process is always 1 byte and 7 bits ahead of any incoming answer.)
_____________________________________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.
Sorry for posting that much of code without comments, I have assumed that the SPI issue would be apparent... :(
Jens-Michael GrossThis doesn't make much sense.TA0IV is a register that returns the number of the highest pending TA= interrupt. The value is 0,2,4,6...Doign an AND with the value in TA0CCR4 register makes no sense, and the result won't be ever equal to TA0CCR4. Hwoever, this ISR make snoting at all, whether the (impossible) condition is met ot not. And the tiemr is not actrivated at all, at least in the code you posted.
I've copied this text, but the condition does not matter, I've only wanted to catch the interrupt with the debugger to see that it actually been invoked by the Accelerometer.
Jens-Michael GrossYour variable receivedIndex should be declared volatile. It is changed inside the ISR and (perhaps) indended to be later used in your main code (currently, received[] and receivedIndex seem to be write-only :)
I've changed that in the code already to volatile, and would update it later.
Jens-Michael GrossI don't know what framework you use, and known none of them good enough to know what your initializations do and whether they are correct.However, what do you mean wiht 'the data received is always empty'? Is usciA3_Module.inputBuffer always 0x00 or 0xff?YOu say teh RX event arrives, but the interrupt does not occur? What do you mean by that? The RX event usually is the interrupt. Either you check for the RXIFG bit manually (with RXIE clear) or lett the ISR be called (with RXIE set).
I don't use any framework, I've just refactored the original code in the demo app into a structured types.The data I see in the received[] if always 0x00.The RX events arrives to the under interrupt, but there are no interrupts in the at the timer, I thought that the component needs to invoke that, since one of its pins, are connected to the TA0CCR4 pin. (Perhaps I'm naive)
Jens-Michael GrossAdam Zehavi packet[0] = XL346_DATA_FORMAT; packet[0] |= 0x40; Each second line overwrites the previous one. Or is this intentional (then you should comment the first line of each pair, to make obvious that it is intentional).
The macros defined for all the i%2==0 are 6bit long, which leaves the last two empty, where the |=40 is meant to change the 7th bit to 1, I have asked specifically here.I will add comments from now on!
Jens-Michael GrossYour slave can of course only answer to a command once it has received the command. But when you're done with sending the command, you stop sending comletely. So the slave cannot send its answer as no clock is generated.At the moment you put packet[5] into TXBUF, paket[4] has just started to be sent.You must wait until both are really send (e.g. by checking the UCBUSY bit).Then you can send one (or more) dummy byte(s) that allows the slave to send its answer. (you may simply send one mroe byte, but be sure to pick teh correct answer byte then - your sending process is always 1 byte and 7 bits ahead of any incoming answer.)
Ahhhaaa, I've missed that one... will give it a try and post back later!
Adam ZehaviI've only wanted to catch the interrupt with the debugger to see that it actually been invoked by the Accelerometer.
Adam ZehaviI've just refactored the original code in the demo app into a structured types.
Adam Zehavi |=40 is meant to change the 7th bit to 1
Jens-Michael GrossSince the pragma denotes this ISR as the timer A0 interrupt function, it will be called on a tiemr A0 interrupt. But in your code you nowhere configure the tiemr to cause any interrupts
Added a timer initialization.
Jens-Michael GrossHowever, directly adding the or's value to the previous line results in smaller and tighter code, as the compile rthen generates only one access to the register instead of two.
Yes I'm aware of that, but I wanted to be able to remove it with the Ctrl+/ short key, in case I misunderstood the Multi-Byte feature... Which I guess I have... misunderstood it...
I've attached the refactored master demo project.
I assume posting the typedefs and functions would be long and less readable...
Thanks again for the support :)
6507.SPI Master Demo.rar
Adam ZehaviAdded a timer initialization.
Your USCI_A3_ISR does not have a break condition for sending/receiving. So once it gets called at all, it will produce an endless stream of outgoing bytes and also endlessly read the return values until your ram is full, teh stack has been overwritten and the MSP crashes on ISR exit.
Jens-Michael GrossNow you should get an interrupt each time the tiemr counts to CCR0 abd rolsl over to 0. But since you don't program TACCR0, it is 0 and the timer constantly rolls over on every timer tick. Generating an endless stream of interrupts as soon as you set GIE at the end of your main code. And your USCI_A3_ISR is probably never called due to its lower interrupt priority.
Actually, the TA0CCR4 register is connected to the accelerometer, if I understood the component specification, then the component should invoke the interrupt on that pin.
Jens-Michael GrossYour USCI_A3_ISR does not have a break condition for sending/receiving. So once it gets called at all, it will produce an endless stream of outgoing bytes and also endlessly read the return values until your ram is full, teh stack has been overwritten and the MSP crashes on ISR exit.
As I've said, I've been really really careful while refactoring the demo code, so the USCI_A3_ISR does work.
How ever I've wanted to change an approach, I've been told to try and read a value from the component in order to check whether the component is connected correctly, so in order to eliminate one uncertainty, I'm trying to achieve that, and as I hate uncertainties, I want to eliminate the other. After looking at some SPI example if I got it correctly (and lately it feels like I have the tendency not to...), I can also perform the SPI without an interrupt, so I'v sketched this up:
void main(void) { packet[0] = 0xC0; // Read bit high + MB bit high + Address = 0x00 WDTCTL = WDTPW + WDTHOLD; setupRegister(redIndicationLed); setupRegister(greenIndicationLed); writeValueToRegister(greenIndicationLed, REGISTER_LOW); initializeAccelerometer(accelerometer); while (!(*(usciA3_Interrupt.flags) & usciInterruptFlags.transmiter)) ; writeValueToRegister(spiAccelerometer.slaveTransmitEnable, REGISTER_HIGH); // STE HIGH *(usciA3_Module.outputBuffer) = packet[0]; // Send read request __delay_cycles(100); // delay ?? received[receivedIndex++] = *(usciA3_Module.inputBuffer); // Read response byte writeValueToRegister(spiAccelerometer.slaveTransmitEnable, REGISTER_LOW); // STE LOW *(usciA3_Module.outputBuffer) = 0xFF; // Send dummy byte __delay_cycles(100); // delay ?? received[receivedIndex++] = *(usciA3_Module.inputBuffer); // Read response byte *(usciA3_Module.outputBuffer) = 0xFF; // Send dummy byte __delay_cycles(100); // delay ?? received[receivedIndex++] = *(usciA3_Module.inputBuffer); // Read response byte while (1) ; }
The (previous code was the old one... and now with comments :) ) rest of the code is the same as in the ZIP file.
This seems to get the same result as the interrupt implementation... I can see the registers values (in the USCI_A3 SPI Mode view in IAR) change upon reading and writing.
Also as before the UCA3RXBUF value remains 0x00.
Is this valid?
I'm rethinking the configuration values for the baudrates and the modulation of the USCI, I've used the default ones from the demo application, would they fit using on a real component? if I need to configure them differently what should I take under consideration while setting them?
Thanks,Adam.
Adam ZehaviI'm rethinking the configuration values for the baudrates and the modulation of the USCI, I've used the default ones from the demo application, would they fit using on a real component?
Adam ZehaviActually, the TA0CCR4 register is connected to the accelerometer, if I understood the component specification, then the component should invoke the interrupt on that pin.
Adam ZehaviI've been really really careful while refactoring the demo code, so the USCI_A3_ISR does work.
Adam ZehaviI can see the registers values (in the USCI_A3 SPI Mode view in IAR) change upon reading and writing.Also as before the UCA3RXBUF value remains 0x00.
P.s.: in your new code, instead usign a fixed delay of 100 MCLK cycles (which may or may not be enough, dependign on the SPI clock speed), you should wait for teh UCRXIFG bit, which indicates that a byte has been received., This bit is automatically cleared when you read the received byte from UCRXBUF.
P.p.s.: the STE signal is only used for controllign the USCI in multi-master or slave mode. If the MSP is master, STE is not necessary at all. YOu can use this pin, as you do now, as normal GPIO pin for selecting the slave, but you shouldn't call it STE then. And this pin shouldn't be switched to module use. (it's acutally the only pin that works as intended right now)
Jens-Michael GrossIt seems you forgot a very important thing throughout your project: port pins used by a hardware module needs to be configured for module usage by setting the proper PxSEL bits. Teh 'setupRegister) funciton, however, only sets the direction, it doe snot switch the pin usage form GPIO to module usage.Unless you do so, the pins remain in GPIO mode and the module is sewered form the outside. Even if you see the register values change, it has no effect on the outside world.As I said, it is very difficult to figure out what your code does or not, due to the multi-layer abstraction (which also seems to add to the code size and execution time).
This one hit the Jackpot... I'm dumb as a rock thinking that things would just work, I have to accept the fact that in this world of microprocessor development, I have to actually configure everything to the last detail!!!
And as for been very very careful I've deleted the following line from the original example:
// P3SEL |= 0x31; // P3.5,4,0 option select
But the demo application still worked, thus I've missed it!! :(
Jens-Michael GrossWhere? I don't see you setting up TACCR4 for capture mode or setting its CIE bit. Maybe I missed it in the jungle of abstraction layers you put around the components, but from a quick glance, TA0CCR4 is never configured to do anything.
Yes, that as well... was not set... although I didn't expect it to work now, since I understood I've miss-configured the SPI...
The moment I've configured the selection pins correctly, all the pieces fell in place, and the component behaves as expected.
Thanks for all your help.
I'm not sure what I'm experiencing now...
Well, after long while of debugging I've got to the conclusion that the bits are shifted:
(The under table continues the upper one)
The first and third row are the output from the RX, aggregated in one sequence, while the second and fourth row, are the expected data...
The values market in red are the values that do not match the reset values in the data sheet page 22, the ones in green are values that do match, but only if I shift the stream 1 bit to the left...
Does this make any sense?
The component data sheet specify that:
but once I've removed the CPHA/CKPH I receive the data just fine!
Thanks again.
Adam Zehavi, I have to accept the fact that in this world of microprocessor development, I have to actually configure everything to the last detail!!!
Adam ZehaviJens-Michael GrossTA0CCR4 is never configured to do anything.Yes, that as well... was not set... although I didn't expect it to work now, since I understood I've miss-configured the SPI...
Jens-Michael GrossTA0CCR4 is never configured to do anything.
Adam Zehaviafter long while of debugging I've got to the conclusion that the bits are shifted:
Thanks for all your help Jens-Michael, I'm finally able to communicate with the accelerometer, and write and read data from its registers.
Jens-Michael Grossthe timer interrupt should be independent of the SPI, except if you first have to configure the slave to pull the trigger.
Actually, if I understood the component datasheet, the component is the one that triggers the interrupt, it is configured to P1.5, one of the AppRegisters of the Accelerometer struct. I've went through all the msp430x54xA_ta3_XX.c examples in the code examples zip, but I did not see a matching example(or did I miss it), is there one?
Jens-Michael GrossThere are only four and one will work perfectly while one or two others might work, but rather coincidentally and with flaws (timing, racing conditions) and one won't work at all.
Do you mean that in some combination, I might experience these "flaws" in corrupted data sometime into the running of the application, or these would be obvious from the beginning and force me to try a new combination?
Thanks...
Adam Zehavithe component is the one that triggers the interrupt
Adam ZehaviDo you mean that in some combination, I might experience these "flaws" in corrupted data sometime into the running of the application, or these would be obvious from the beginning and force me to try a new combination?
To be on the safe side, see in the slave datasheet, which operation (SOMI bit output, SIMO bit input) is done at which edge of the clock signal and pick the corresponding mode on the master side.
Jens-Michael GrossRight. What I meant that sometimes the component only outputs this signal (if there is such a signal at all, msot SPI slaves won't have one) after it has been instructed to do so.Some, of course, start outputting their interrupt signal right after power-on.
According to the data sheet and Analog support I'm configuring the component properly, thus it should invoke the interrupt, but it does not work.
I'm failing to see the configuration of the Timer so interrupts could be intercepted... could you point me in the right direction?