Why SPI slave's output is shifted to the left?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Why SPI slave's output is shifted to the left?

6,273 Views
kimjunghyun
Contributor III

Hi. I have to change some parts of my question.

I found SPI's slave output was as below.

■ SPI's slave output is shifted to the left 

   - with 1 clock(1bit)

   - only one time

   - whenever SPI starts first in interrupt

   - 1 is added to the last bit (farthest to the right )

   - from second frame, all frames are not shifted (1 frame is 8byte)

ex) original frame(8byte) : 0x01..., 0x82

      shifted frame(8byte) : 0x02..., 0x05

■ I have to send 0x01..., 0x82 first. How could I do it?

   * Current problem is 'SPI's slave output is shifted to the left' but my goal is to send 0x01..., 0x82.

     so I changed the frame value to '0x00, 0x80... 0x41'. (this value will be shifted and changed to '0x01..., 0x82')

     But I missed that 1 is added to the last bit and the frame value became  0x01..., 0x83. I could not make it.

---------------------------------------------------------------------------------------------------------------------------------------------------

Hi.

I am confused because SPI slave's output is shifted to the left.

SPI slave's output is 0x01 but it is changed to 0x02 when I changed some source code.

The code's difference is only whether 'LPSPI_DRV_SlaveTransfer()' is in if() or not

I hope somebody helps me.

<0x01 output>

#define BUFFER_SIZE 8U
uint8_t slaveDataReceive_1[8] = {0x00};
uint8_t slaveDataSend_1_1[8] = {0x01,0x00,0x00,0x00,0x00,0x00,0xfd,0x82};

...

void PORTB_ISR(void)
{
  PORT_HAL_ClearPortIntFlagCmd(PORTB);
  LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend_1_1,slaveDataReceive_1,BUFFER_SIZE);
...
}

20.jpg

<0x02 output>

#define BUFFER_SIZE 8U
uint8_t slaveDataReceive_1[8] = {0x00};
uint8_t slaveDataSend_1_1[8] = {0x01,0x00,0x00,0x00,0x00,0x00,0xfd,0x82};

...

void PORTB_ISR(void)
{
  PORT_HAL_ClearPortIntFlagCmd(PORTB);
  if(c1==1){
      c1=0;
      LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend_1_3,slaveDataReceive_1,BUFFER_SIZE);
      d1=1;
   }
   if(b1==1){
    b1=0;
    LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend_1_2,slaveDataReceive_1,BUFFER_SIZE);
    c1=1;
   }
   if(a1==1){
    a1=0;
    LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend_1_1,slaveDataReceive_1,BUFFER_SIZE);
    b1=1;
   }
...
}

21.jpg

0 Kudos
19 Replies

4,455 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi,

Are you sure it is transmitting slaveDataSend_1_1 and not slaveDataSend_1_2?

Regards,
Daniel

 

0 Kudos

4,455 Views
kimjunghyun
Contributor III

Hi, Diniel.

Yes I am sure that slaveDataSend_1_1 was transmitted.

I tested this status with only 1 frame(8byte) so many times and the result was same always.

Result : First frame's all bits(8byte) are shifted to the left and 1 is added to the last bit(farthest to the right )

              After Second frame, all bits are not shifted. all frames are transmitted correctly.

I added further contents to my question.(I revised it a little while ago)

Please review other points.

Kim.

0 Kudos

4,455 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi,

What triggers the PORTB interrupt? How do you synchronize the slave with a master?

When the master is transmitting, the slave buffer should be already written.

 

Can you attach the whole project?

Regards,

Daniel

0 Kudos

4,455 Views
kimjunghyun
Contributor III

Hi.
Yes I will attach thw whole project tomorrow morning. (Now I am home)

But first would you teach me SPI's slave receive interrupt code?

* When I go to my office tomorrow, you are get off office. So if you teach me slave receive interrupt code, I will try.

* I tried slave receive interrupt many times but I could not make it. so I used PORTB interrupt

  - I put 'LPSPI0_IRQn' into 'INT_SYS_EnableIRQ()', 'INT_SYS_InstallHandler()' but interrupt was not generated.

■ Refer to the link and below code for the PORTB interrupt (I asked this question here)

* point : CS is input to PORTB

* https://community.nxp.com/thread/453901

[Source code]

  void PORTB_ISR(void) // PCS is input to PTB0 and PORTB generates interrupt for falling edge
 {

   LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend,slaveDataReceive,BUFFER_SIZE);
  }

 

int main(void){

...

INT_SYS_EnableIRQ(PORTB_IRQn);
INT_SYS_InstallHandler(PORTB_IRQn, &PORTB_ISR, NULL); 

LPSPI_DRV_SlaveInit(RECEIVE,&ReceiveState,&Receive_SlaveConfig0);

...

}

Kim.

0 Kudos

4,455 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi,

The thing is, there is no interrupt when the transmission starts.

This is all handled automatically by the LPSPI hardware in the background.

 

When the master starts transmitting, it starts reading the slave FIFO. The data stored in the slave FIFO are being shifted out. The problem in your code is that the slave transmit FIFO is still empty. In fact, when the transmission starts, your code is making decision what to put into the FIFO which is late.

 

Only put the data into the FIFO using the LPSPI_DRV_SlaveTransfer() function and wait for the master. It will be transmitted automatically.

 

At the end of the transmission, an interrupt can be triggered and a new data can be prepared (put into the FIFO) for the next transmission.

 

Regards,

Daniel

0 Kudos

4,454 Views
kimjunghyun
Contributor III

Hi.

I updated some contents with my review results. So please consider below contents.

First, my goal is to transmit some frames of slave as below picture (2~3frame. 1frame is 8byte)

RJ_SPI.bmp

And I have to change frame values of slave based on conditions and send several frames


■ Question: Where should I put 'LPSPI_DRV_SlaveTransfer()' into the source code?

1. between main() and for(;;)

  - when I tested this position, slave transmitted the frame only 1 time.
2. inside for(;;) :

  - when I tested this position, slave responsed to every master's transmission but the frame was different to defined variable.
3. inside interrupt function
  - But there is no interrupt (from you answer)

For you answer, I understood the problem : slave transmit FIFO is empty.

- The point is, When the master is transmitting, the slave buffer should be already written.

So I changed 'LPSPI_DRV_SlaveTransfer()' position to resolve my problem. (SPI slave's output is shifted to the left)

But the shift of first slave frame is not fixed. I think this problem and interrupt might be separate.

■ Test result based on position
1. In the main(), between 'LPSPI_DRV_SlaveInit' and for(;;).
  : Slave's frame is transmitted only one time. Although master transmits frames, slave does not response.

1. 초기화 함수 아래에 송신함수 넣었다.bmp

2. in the main(), inside for(;;)
  : slave transmits frames whenever master transmits frames. but slave's frame is different to defined variable.

2. for(;;) 안에 송신함수 넣었다.bmp

3. In the main(), between 'LPSPI_DRV_SlaveInit' and for(;;). and inside for(;;).

  * 'LPSPI_DRV_SlaveTransfer()' is used 2 times.

  : result is eqaul to No.2

3. 초기화 함수 아래에 송신함수 넣었고, for(;;)안에도 넣었다.bmp

4. in the main(), inside for(;;) and added 'LPSPI_DRV_MasterTransferBlocking()'

  : result is equal to No.2

4. for(;;) 안에 LPSPI_DRV_MasterTransferBlocking 추가했다.bmp

■ Difference based on 'LPSPI_DRV_SlaveTransfer()' position

1. in the main(), inside for(;;)

  : slave responses whenever master transmits frames. but slave' frame is different to defined variable.

2. outside main(), inside PORTB_ISR()

  : slave responses whenever master transmits frames. slave's frame is equal to defined variable. but only first frame is shifted to the left.

   * slave frame : {0x00,0x80,0x00,0x00,0x00,0x00,0x7e,0xc1}

  1) First frame : {0x01,0x00,0x00,0x00,0x00,0x00,0xfd,0x83}

      - first slave frame is shifted to the left and 1 is added to the last bit (farthest to the right)

33.jpg

  2) Second frame : {0x00,0x80,0x00,0x00,0x00,0x00,0x7e,0xc1};

       - after second frame, slave frame is not shifted.

scope_13.bmp

■ my approach and your answer.

- for your answer, Where does SPI interrupt trigger in the source code? I have to know the code position and then I can put the slave frame value there.

그림1.jpg

Again, my question is as below.

■ Where should I put 'LPSPI_DRV_SlaveTransfer()' into the source code?

   or How can I fix the slave frame's shift to the left?

   or How can I change last bit(1, added to farthest to the right) to 0? (I have to send '0x01,0x00,0x00,0x00,0x00,0x00,0xfd,0x82' at the first slave frame)

 

Thank you.

Kim.

0 Kudos

4,455 Views
Ray_V
Contributor V

Referencing the first frame trace, dated "WED JUL 12 14:52:39 2017"
It seems your Master is at fault.
The CLK seems to Idle Low, but the master asserts SS while CLK is high. When SS gets asserted while clock is high the slave assumes this is the first edge and outputs the first bit. Then CLK goes Idle (Low) and slave "thinks" the bit was read. So when clocking starts data is off by one bit.
The master should have the CLK in the Idle state Before asserting SS.

0 Kudos

4,455 Views
kimjunghyun
Contributor III

Hi. Raymundo Velarde

First thank you for your interest

I don't think Master is at fault.

Actually, Master connects to Hall IC(MLX 90363) which is a slave. When they communicates through SPI, Hall IC transmits a first frame without 1bit left shift and 1 added at last bit. They communicates correctly.

* Hall IC's first frame : 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x82

* 0x82 is 0b1000 0010 and it means that 1 is not added to last bit
And I made a slave with S32K144 as a Hall IC. When S32K144(slave) transmits a first frame, it is shifted to the left with 1 bit.

And what is 'assert'?
I do not know it's concept so I could not understand your answer exactly
Does 'assert' means output high or low?

Kim.

0 Kudos

4,455 Views
Ray_V
Contributor V

I forgot to add. You can't assume that if one slave behaves one way the others will.

One slave (HALL IC) may not shift first bit if SSEL is asserted with clock high while another might. This is hardware dependent.

In order for all slaves to behave properly you need to make sure the clock is idle when SSEL is asserted.

0 Kudos

4,455 Views
kimjunghyun
Contributor III

I  understood your answer. 

For SPI to work correctly, 

clk low -> ss low -> frames sent -> clk low -> ss high.

So how can I make the clk to be low(idle)?

Kim

0 Kudos

4,455 Views
Ray_V
Contributor V

I am not familiar with S32K144 but I am familiar with SPI.

I thought about it and there are a couple of things you can try.

1. Some SPI cells can be configured to output bits on an edge or on a level. Make sure your SPI slave is configured to output bits on a rising edge, not on a high level. This may solve your problem.

2. SPI master should automatically set CLK idle when not sending/receiving. Add a delay after initializing the SPI master before sending/receiving any data, this should give it time to set CLK idle.

0 Kudos

4,455 Views
kimjunghyun
Contributor III

Oh. Thank you. 

I will check the slave(s32k144) outputs bits on a rising edge or on a high level.

And I will find how to configure the slave(S32K144)' clk as low

* I do not make the master. I have to fit it with a slave. 

Kim

0 Kudos

4,455 Views
Ray_V
Contributor V

>> And I will find how to configure the slave(S32K144)' clk as low

This is done by the master. The slave does not control the clock.

You may need to contact the maker of the master. SS is also controlled by the master and it should not be asserted before the clock is at the idle state.

0 Kudos

4,455 Views
Ray_V
Contributor V

SS or SSEL is the Chip Select. It enables the chip that is to be read/written to. Assert means it is active(chip is selected). In your case (and in most cases) this means Low.

Above, by the first frame trace, you wrote:

1) First frame : {0x01,0x00,0x00,0x00,0x00,0x00,0xfd,0x83}

      - first slave frame is shifted to the left and 1 is added to the last bit (farthest to the right)

So the trace shows that 1 is added to the last bit.

For SPI to work correctly you must have Clock Idle -> SSEL asserted(low) -> frame(s) sent -> Clock Idle -> SSEL de-asserted(high).

Look at the second frame, clock is Idle (low) when SSEL is asserted and it transmits correctly.

0 Kudos

4,455 Views
danielmartynek
NXP TechSupport
NXP TechSupport

I think you should first read the reference manual, so that you can understand what it does.

To your last comment. When you put the transfer function before the infinite loop, the function will fill the FIFO only once, obviously. If you put the function in the infinite loop, the function will try to write the FIFO continuously. But the maximum number of words in the FIFO is 4 (8 bytes). Therefore, you must wait for the master. After the FIFO has been shifted out, it is empty and you can call the transfer function again.

You should use an interrupt. But not at the beginning of a transfer, as you did. All LPSPI interrupts are listed in Chapter 45.4.4. For instance, the interrupt can be triggered when the FIFO is empty. The problem is I don’t know how to do it using SDK right now. There should be implemented a callback function. Let me check it. This might take some time.

Regards,

Daniel

0 Kudos

4,455 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi,

 

The callback feature is available only in EAR 0.8.4 SDK version which will be released probably at the end of this month. Using older version of SDK, it is impossible to implement this feature.

 

I think the first byte is shifted because there is not enough time between SS assertion and the first CLK.

If the master is S32K144 as well, you can try changing PCSSCK in CCR register.

 

PCS to SCK Delay: Configures the delay in master mode from the PCS assertion to the first SCK edge. The delay is equal to (PCSSCK + 1) cycles of the LPSPI functional clock divided by the PRESCALE configuration, and the minimum delay is 1 cycle.

 

Regards,

Daniel

0 Kudos

4,455 Views
Ray_V
Contributor V

>> I think the first byte is shifted because there is not enough time between SS assertion and the first CLK.

I don't think this is the case. Data is going one bit early, not one bit late.

Also, the transfer function can be in a loop. The FIFO level needs to be checked before writing and wait for next loop if FIFO is full.

0 Kudos

4,455 Views
kimjunghyun
Contributor III

I understood your answer

Not enough time between ss low and first clk causes the first byte is shifted.

But master is not S32K144 so I can not change this time. 

- Master ; renesas mcu (maybe RH850)

- slave ; mlx 90363

And I have to make a slave with S32K144 for some test. 

At this test, mlx 90363 is removed and S32K144 acts as a slave.

Do you have some other solution?

Kim

0 Kudos

4,455 Views
kimjunghyun
Contributor III

Hi. Daniel.

I did not understand your answer.

Relate to your answer

'there is no interrupt when the transmission starts. This is all handled automatically by the LPSPI hardware in the background'

'At the end of the transmission, an interrupt can be triggered and a new data can be prepared (put into the FIFO) for the next transmission.'

1. I should remove the interrupt code which I made.

[Source code]

  void PORTB_ISR(void) // PCS is input to PTB0 and PORTB generates interrupt for falling edge
 {

   LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend,slaveDataReceive,BUFFER_SIZE);
  }

2. I should put the code to transmit the data of the slave FIFO in the interrupt code. I do not know where it is.

Where should I put the data of the slave FIFO in the code?

 1) Do you mean the position is above of the for(;;) in the main?

int main(void){

...

LPSPI_DRV_SlaveInit(RECEIVE,&ReceiveState,&Receive_SlaveConfig0);
LPSPI_DRV_SlaveTransfer(RECEIVE,&slaveDataSend_1_1,slaveDataReceive_1,BUFFER_SIZE);

...

for(;;)
{

...

}

 2) Or do you mean the position is inside while(1) in the main? (from LPSPI_Example)

int main(void){

...

LPSPI_DRV_MasterInit(SEND, &masterState, &Send_MasterConfig0); //  Is this code required for slave?
LPSPI_DRV_SlaveInit(RECEIVE, &slaveState, &Receive_SlaveConfig0);

while(1)
  {
...
          LPSPI_DRV_SlaveTransfer(RECEIVE, &slaveDataSend, &slaveDataReceive, BUFFER_SIZE); // Is this interrupt?
          LPSPI_DRV_MasterTransferBlocking(SEND, &masterDataSend, &masterDataReceive, BUFFER_SIZE, TIMEOUT);

..

  }

Thank you

Kim.

0 Kudos