0
\$\begingroup\$

I am trying to communicate with a display device (master) which sends two type of data packets with 50 kHz clock. One data packet is 16 bytes and one is 32 bytes long. The data format is as follows:

enter image description here

Now for read command, my response bytes should be during the dummy bytes that the display device is sending.

Here is the snippet of code in ISR where I have implemented a state machine to respond to data bytes:

void spiIsr(const tsDevCom *device) {
  tsSpiParams *params = device->parameters;
  const tsSpiConsts *consts = device->constants;
  tsDevComSystem *sys = device->sys;
  static uint8_t txBuffer[] = {0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};    
  static uint8_t command = 0;
  static uint8_t length = 0;
  static uint8_t adress = 0; 
  static uint8_t index = 0;
  static uint8_t crc= 0x8b;

  if (SH_SET == SPI_GetINTStatus(consts->SPIx, SPI_FUNC_RECEIVE)) {
    uint8_t temp = (uint8_t)SPI_ReceiveData(consts->SPIx);

    switch (params->eSPIState) {     
    case _SPIState_Start:
      if (temp == 0xAA) {
        params->eSPIState = _SPIState_Command;
        SPI_SendData(consts->SPIx, 1); // 1
        index = 0;
      }
      else
        SPI_SendData(consts->SPIx, 0); // 0
      break;

    case _SPIState_Command:
      command = temp;
      params->eSPIState = _SPIState_Adress; 
      SPI_SendData(consts->SPIx, 2); //2
      break;
        
    case _SPIState_Adress:
      adress = temp;
      params->eSPIState = _SPIState_Size;
      SPI_SendData(consts->SPIx, 3); // Response in Adress state 
      break;

    case _SPIState_Size:
      length = temp;
      if (command == 0x02) {
        params->eSPIState = _SPIState_Command_RX_Data;
        SPI_SendData(consts->SPIx, 10);}
      else {
        params->eSPIState = _SPIState_Command_TX_Data;
        SPI_SendData(consts->SPIx, txBuffer[index]);
      }
      index++;
      break;

    case _SPIState_Command_RX_Data:
      SPI_SendData(consts->SPIx, txBuffer[index]);
      index++;
      if (index>=length)
        params->eSPIState = _SPIState_Command_RX_Checksum;
      break;

    case _SPIState_Command_TX_Data:
      SPI_SendData(consts->SPIx, txBuffer[index]);
      index++;
      if (index>=length)
        params->eSPIState = _SPIState_Command_TX_Checksum;
      break;

    case _SPIState_Command_RX_Checksum:
      params->eSPIState = _SPIState_Start;
      index= 0;
      SPI_SendData(consts->SPIx, 0x55);
      break;

    case _SPIState_Command_TX_Checksum:
      params->eSPIState = _SPIState_Start;
      index= 0;
      SPI_SendData(consts->SPIx, crc);
      break;

    default:
      params->eSPIState = _SPIState_Start;
      SPI_SendData(consts->SPIx, 0x89);
      index= 0;
    }

    SPI_ClearINTStatus(consts->SPIx, SPI_FUNC_RECEIVE);   
  }
}

Here are the pictures of response bytes for both data packets:

enter image description here

enter image description here

Now in both packets we can see that 0x03 on MISO is seen twice, even though it should be once, and then the first byte of Txbuffer.

When I try to debug and put break point at this line

SPI_SendData(consts->SPIx, 3);  // Response in Address state

then it captures everything correctly.

Why is it happening at this specific place and how it can it be solved?

The microcontroller I am using is from Sinowealth and I am not using the SS pin at the slave side.

Is there any better way to do it? Because I think doing that much stuff in an ISR is never recommended. So what other option should I use in this scenario?

\$\endgroup\$
1
  • \$\begingroup\$ Are you sure that "SPI_enable" should be released between the bytes? \$\endgroup\$ Feb 24 at 10:19

2 Answers 2

1
\$\begingroup\$

You made great progress in this project. Cooking each byte to send directly after having received one is a good solution.

I assume the response time of your code is at the boundary. I guess from the screen shot that you have only around 10..20 µs time to transfer the byte to send to the SPI transmit buffer.

This works most of the time, but in the _SPIState_Size case you have one additional if condition and an indexed array access. So the code execution may just need some more time and the transfer comes too late. In this situation the SPI hardware repeats the last content of the TX register.

You can precalculate the byte to send in the previous case statement right after the SPI_SendDate call, where you have time. So you can avoid the array access, which may help to improve the response time.

Sinowealth MCUs seam to be 24 MHz 8051 systems, there is not much margin to solve this task using high language code.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks @Jens for your valuable response. But I have already tested this by skipping the if statement and replacing array with a number. It still behave similarly. One thing is, you can see the master is configured in mode 0 but when I configure my board in SPI mode 0, it echo back on MISO what receive from MOSI. But works like this when I choose Mode 3. Is there a chance that mode has something to do with this? \$\endgroup\$ Feb 26 at 11:33
  • \$\begingroup\$ I recommend to use a spare I/O bit, set it at the start of the ISR routine and clear it on the exit. With this bit connected to a channel of the analyser you can see the response time of the code. I am not familiar with Sinowealth MCUs, but I don't think that the SPI mode produces this kind of error. There may be other system interrupts, that can delay the response of this ISR. \$\endgroup\$
    – Jens
    Feb 26 at 16:01
0
\$\begingroup\$

The problem has been resolved by doing two things.

  1. My MCU was configured as spi slave in mode 3 but master was in SPI mode 0, although it was able to communicate but were having some issues as I described in question. Why I was using mode 3 in first place? because in mode 0 it was choing data on MISO line. The issue was with wrong configration of SS/CS pin. apparently SS pin was not needed to be confgured in mode 3 but was must for mode 0. So when I configured SS pin as alternate function as SPI SS pin, mode 0 started to work.

  2. But getting some byte twice was resolved by changing this case condition,

    case _SPIState_Size: length = temp; SPI_SendData(consts->SPIx, txBuffer[index]); if (command == 0x02) { params->eSPIState = _SPIState_Command_RX_Data; } else { params->eSPIState = _SPIState_Command_TX_Data; } index++; break;

responding before checking condition. Apperently if else condition inside case was making reponse slow and MCU was not unable to load new data in TDR before new clock. So now communication works fine.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.