SPI Slave not reading correct data

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello,

 

I am trying to read data from ADC. If I probe signal lines with a logic analyzer, I can see actual samples (with their respective headers). If I look at data read by SPI slave peripheral using a debugger, I just see some meaningless numbers. Do I have a mismatch on CPOL/CPHA?

CY_ISR_PROTO(RxC1);
CY_ISR_PROTO(RxC2);
void SendData();

uint8_t Data1[32] = {0};
uint8_t Data2[32] = {0};
uint8_t Ptr1 = 0;
uint8_t Ptr2 = 0;
volatile uint8_t Ready1 = 0;
volatile uint8_t Ready2 = 0;
uint32_t Channels[10] = {0};
uint8_t Usb[40] = {0};

void ADCData_Init()
{
    Data1_RxC_StartEx(RxC1);
    Data2_RxC_StartEx(RxC2);
    SpiData1_Start();
    SpiData2_Start();
}

CY_ISR(RxC1)
{
    while(SpiData1_ReadRxStatus() & SpiData1_STS_RX_FIFO_NOT_EMPTY)
    {
        uint8_t dummy;
        if(Ptr1 < 32)
        {
            Data1[Ptr1++] = SpiData1_ReadByte();
        }
        else
        {
            dummy = SpiData1_ReadByte();
            if(dummy < 0xFF)
            {
                Data1[0] = dummy;
                Ptr1 = 1;
            }
        }
        if(Ptr1 == 32)
        {
            Ptr1++;
            Ready1 = 1;
            if(Ready2)
            {
                Ready1 = 0;
                Ready2 = 0;
                SendData();
            }
        }
    }
}
CY_ISR(RxC2)
{
    while(SpiData2_ReadRxStatus() & SpiData2_STS_RX_FIFO_NOT_EMPTY)
    {
        uint8_t dummy;
        if(Ptr2 < 32)
        {
            Data2[Ptr2++] = SpiData2_ReadByte();
        }
        else
        {
            dummy = SpiData2_ReadByte();
            if(dummy < 0xFF)
            {
                Data2[0] = dummy;
                Ptr2 = 1;
            }
        }    
        if(Ptr2 == 32)
        {
            Ptr2++;
            Ready2 = 1;
            if(Ready1)
            {
                Ready1 = 0;
                Ready2 = 0;
                SendData();
            }
        }
    }
}
0 Likes
1 Solution
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 , Since you are invoking the call to the UART (or USB) from the SPI ISR, you need to ensure that the interrupt priority of the UART (or USB) should be greater than that of the SPI , otherwise they will never be called. And any blocking code called in a while () that are interrupt dependent  will never return. Either set the interrupt priority of UART (or USB) to a higher value or set a flag in the SPI interrupt and call the transfer functions in the main loop.

Let us know if this solves your issue.  

Regards,
Bragadeesh

View solution in original post

0 Likes
23 Replies
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

I have found timing mismatch. Now I have selected CPHA0/CPOL1.  Changes on rising edge, sampling on falling edge, first bit sampled 1/2 period after drdy falling edge.

 

But I am still not getting correct data, there is another problem with the algorithm.

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 , Can you ensure that the CPHA/CPOL setting of both the master and slave is aligned? Can you share your complete PSoC Creator project for review? Instead of seeing the memory, can you place a watchpoint on the variable Data1 and see the value of this variable? Is the SPI Slave ISR getting executed?

Regards,
Bragadeesh
0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello Bragadeesh,

Thanks for reply. Unfortunately, I do not have access to project right now. I will upload later.

I did my best to align CPOL/CPHA with readings using logic analyzer probe. Sometimes it does see correct byte values, but not at correct position within Data1/2. ISR is working, otherwise Data1 would be all zeroed.

What I suspect as problem,  ADC uses unusual behaviour of DRDY and DCLK. DRDY is not true chip select, it is more likely interrupt signal. After all data is transmitted, DRDY stays low, DCLK keeps clocking. On DOUT, ADC is transmitting 0xFFs. Then before DRDY's rising edge, last byte is partially transmitted. Could this corrupt SPIS peripheral's shift register? Or is shift register wiped on CS rising edge?

Regards,

Stanislav

 

Update: I have also tried multiple ways to constrain CS pulse length, but nothing was working properly. I have found PulseConvert component. But problem is that on DRDY falling edge, I need to start reading data immediately, or I will corrupt them. I can't afford to wait for beginning of out_clk pulse. Unless delay would be exactly one byte - first byte of transmission is a header byte, which I do not need.

0 Likes
lock attach
Attachments are accessible only for community members.
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Here's my project. I have also included readings from logic analyzer. These can be opened with Sigrok's PulseView software - or refer to logic.png from my initial post.

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 ,

We do not see any issues in your project setup. Can you share the datasheet of the master device for better understanding of the waveform?

As per the SPI component datasheet, a minimum of 0.5 CLK period is required between the negative edge of SS line and the SCLK like for proper operation of SPI Slave. 

Regards,
Bragadeesh
0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello Bragadeesh,

Sure. It’s Analog Devices AD7770.

https://www.analog.com/media/en/technical-documentation/data-sheets/ad7770.pdf

Regards,

Stanislav

0 Likes
lock attach
Attachments are accessible only for community members.
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello @BragadeeshV ,

I have changed my project to read data using master interface of AD7770. Here I need to use interrupts to multitask two ADC ICs.

Problem is that when writing second byte, I get hanged up on write to TX register. SPIM has clock and has been started. What else can cause failure to write data?

Stack trace is Adc.c line 65 -> Adc.c line 21 -> Adc0.c line 562.

Regards,

Stanislav

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 ,

Thanks for the update. I see the following issues in the project/ code:

1.  I believe you want to trigger an interrupt on every byte received. If yes, why is the TX FIFO not empty interrupt chosen in the component and not the RX FIFO not empty interrupt?. In master mode, it is common to enable the SPI done interrupt to indicate the end of present transaction.  Then you can read the bytes from the RX FIFO. 

2. Presently, DRx1 handler will be called when the TX FIFO is empty. but it looks that you are considering that as the RX FIFO not empty interrupt and reading the RX FIFO data. This has to be changed. Also, after you receive the RX FIFO not empty interrupt, it is a good practice to check the number of bytes received and later read those many bytes from the RX FIFO.

3. Adc0_GetTxBufferSize is a function and has to to be used with a parenthesis (, ie Adc0_GetTxBufferSize() . Also, what is the intention of using this API? Are you checking if all the bytes are transmitted out of the TX FIFO? In that case, you need to use SPIM_ReadTxStatus and check if the SPIM_STS_SPI_DONE is set to indicate the end of transaction. 

Can you please give us more information on the requirement or what you are trying to achieve so that we can guide you better? I can't understand if you want a tx or Rx interrupt.

Regards,
Bragadeesh
0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello Bragadeesh,

I will describe it in detail. 

During configuration step, I need 16bit frames. First byte is register address, second is register value. This step does not need to be multitasked.

 

During data read step, data are feeded by ADC in 24bit frames. 0x800000 on TX is just dummy data here. So what I need is send 0x800000 and meanwhile read 3 bytes. After third byte was received, I need to deassert CS, and then continue with another frame. This also needs to be multitasked between ICs.

Regards,

Stanislav

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 

Thanks for the update. Can you please let us know if you want to trigger an interrupt on every byte received or based on TX FIFO not empty event. Did you try out the suggestions in the previous response? I was able to reproduce your issue and the stuck at condition was resolved when i used () for the Adc0_GetTxBufferSize  function. But we do not recommend this API to check if the TX FIFO bytes are sent out correctly as explained in previous response.

Regards,
Bragadeesh
0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello Bragadeesh,

Thanks for explanation. I will rework it as soon as I can (this is part of my thesis, but I also have other assignments).

So, where I need trigger. I need to send 0x800000 (as I mentioned), and at this time read three bytes. First byte needs to be read while transmitting 0x80, not after it. After third byte was send/read, I need interrupt. Unfortunately, minimum FIFO size is 4 bytes, so I am trying to overcome this. Should I use SPI done flag? RX not empty does not seem appropriate to me, because after first(and second) byte it is meaningless to fire interrupt.

Now after this transaction, I need to correctly generate a delay, which would cause SPIM to deassert CS. Meanwhile, I also need to do the whole process on other ADC IC. What is a recommended way to generate this delay with non-blocking code? CyDelayUs may work, but will waste time, that may be useful for processing other ADC IC.

Regards,

Stanislav

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 ,

Thanks for the detailed explanation. You can use the following:

1. Use RX FIFO not empty event to read the bytes as and when it is received in the RX FIFO. I do not see any issues if the event is triggered 3 times. You can keep count of which byte is received using an index variable in the ISR. 

2. Configure a Timer block to trigger an interrupt every x times. Where X is the time taken to transfer 3 bytes of data + the interframe delay. Once this interrupt triggers you can send the next 3 bytes of data to receive RX data.

I have tried to represent this idea through the timing diagram I found in the AD7770 datasheet. Let us know if this works for you.

BragadeeshV_0-1614868022679.png

 

Regards,
Bragadeesh
0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello @BragadeeshV ,

I have reworked my code. Now I am getting some frames from ADC. But on logic analyzer, I can't understand them. On PSoC Creator, I am getting 0x00s. So it looks like I got further with using ADC's data output interface and SPIS component.

Let me explain my suspects on why it didn't work. I was getting some data into memory array. On logic analyzer, I am convinced it was actual raw data from adc. Inside PSOC's memory, they are not. What may be reason, timings on data output interface are not exactly same as on standard SPI bus. After last byte transmitted, data line hoes high, drdy line remains low, and clock line keeps clocking. So from SPI protocol's view, I am still getting bytes transmitted. Even worse, last byte is partially transmitted. I am suspecting this corrupts SPIS RX shift register. Could this be cause?

I did my best to derive correct CS signal from DRDY pulses, but everything I was able to invent, either did not compile, or did not behave as required.

 

Regards,

Stanislav

0 Likes
lock attach
Attachments are accessible only for community members.
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

I have tried to reproduce, what happens on bit length mismatch. I have connected a SPI bus internally between SPIM and SPIS components. Clocked from one common clock.

Well, I wasn't even able to readback correct data with bit length and spi mode matched. That "data" I was getting might be similar to what I was getting from ADC's data output interface.

 

Any idea on what is happening here? I have expected that it won't be problem to read back data with all parameters matched between SPIM and SPIS, and also common clock. It is similar to my real setup, ADC is clocked from same source as SPIM and SPIS peripherals. And all ADC's clocks(modulator,data output,...) are derived from it.

 

Regards,

Stanislav

0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Ok, finally I may have a bit of luck.  I have written a verilog component, that may be able to reshape data ready signal to chip select, which I need.

I can't  make cs go low immediately after drdy, but I can generate 8bits delay. This way I will cut channel 0 header (which I don't need), and catch beginning of channel 0 data. After last channel data, cs will go high, and I will cut bits that would otherwise corrupt rx shift register.

 

`include "cypress.v"

module CSHelper(
    input wire clock,
    input wire csin,
    output wire csout
);

localparam [15:0] MinCount = 16;
localparam [15:0] MaxCount = 48;
reg [15:0] Counter;
reg Enabled;

assign csout = (Counter < MinCount) | (Counter >= MaxCount) | (!Enabled);

always @ (posedge clock)
begin
    if (csin == 1)
    begin
        Counter <= 0;
        Enabled <= 0;   
    end
    else
    begin
        Enabled <= 1;
        if (Counter < MaxCount)
        begin
            Counter <= Counter + 1;
        end
        else
        begin
            Counter <= Counter;
        end
    end
end

endmodule

 

0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello @BragadeeshV ,

Thanks for your assistance. I am nearly done with programming MCU. Using above Verilog, I was able to correctly read data inside MCU.

One last problem, when sending second data packet over USB, I get hanged on USB_CDCIsReady. Seems that the first packet was not actually sent.

 

Usb_Start(0,Usb_3V_OPERATION);
while(!Usb_GetConfiguration());
Usb_CDC_Init();

while(!Usb_CDCIsReady());
Usb_PutData((uint8_t*)Usb,40);
0 Likes
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hello @user_1570836 

There are a few things that might be causing the issue :

1. It is possible that Host double sends the SET_INTERFACE request. In your code you need to keep checking if there is a change in configuration and initialize the USB CDC class again if the request as been sent again. You can use the following code structure:

 

 

USBUART_Start(USBFS_DEVICE, USBUART_3V_OPERATION);
    
    for(;;)
    {
        /* Host can send double SET_INTERFACE request. */
        if (0u != USBUART_IsConfigurationChanged())
        {
            /* Initialize IN endpoints when device is configured. */
            if (0u != USBUART_GetConfiguration())
            {
                /* Enumeration is done, enable OUT endpoint to receive data 
                 * from host. */
                USBUART_CDC_Init();
            }
        }
}

 

2. Can you let me know the amount of data that you are transferring in the first transfer? what is the maximum endpoint size of the In endpoint?

If the last sent packet is exactly the maximum packet size, it is followed by a zero-length packet to assure that the end of the segment is properly identified by the terminal.

You can refer to USB CDC Code example for the proper code structure required: https://www.cypress.com/documentation/code-examples/ce95396-usb-uart-psoc-35lp

In case if this does not work, kindly attach you USB project and we will try and recreate issue at our end to provide you with a solution.

Best Regards

Ekta

0 Likes
lock attach
Attachments are accessible only for community members.

Hello Ekta,

I have tried your loop at the beginning (break; after CDC init?). Unfortunately, this has not solved issue.

Amount of data is 8ksps, 40 bytes per frame(10 channels, 4 bytes per channel). 320 kBps total.

Endpoint settings are:

EP1, IN, INT, 10ms interval, 8 bytes

EP2, IN, BULK, 10ms interval, 64 bytes

EP3, OUT, BULK, 10ms interval, 64 bytes

 

Now I think about it. What exactly do those 10ms mean? I need to send 8000 40-byte frames per second. Does this require smaller interval, or larger packets? 

 

Regards,

Stanislav

0 Likes
user_1570836
Level 4
Level 4
50 replies posted 25 replies posted 25 sign-ins

Hello everyone,

 

 

There is a more serious problem than just USB malfunction. I have changed transport method to simple uart. After two frames of data (80 bytes total), I get stuck between these lines (part of Uart_PutChar).

 

 

 

locTxBufferWrite = Uart_txBufferWrite;
            locTxBufferRead  = Uart_txBufferRead;

        #if ((Uart_TX_BUFFER_SIZE > Uart_MAX_BYTE_VALUE) && (CY_PSOC3))
            /* Enable interrupt to continue transmission */
            Uart_EnableTxInt();
        #endif /* (Uart_TX_BUFFER_SIZE > Uart_MAX_BYTE_VALUE) && (CY_PSOC3) */
        }
        while( (locTxBufferWrite < locTxBufferRead) ? (locTxBufferWrite == (locTxBufferRead - 1u)) :
                                ((locTxBufferWrite - locTxBufferRead) ==
                                (uint8)(Uart_TX_BUFFER_SIZE - 1u)) );

 

 

 

I hope this has some simple reason, that will be fixed soon. 

Regads,

Stanislav

 

UPDATE: Slowing down ADC sample rate does not help. Nor does enclosing send code to critical section. What is going on here?

UPDATE2: Disabling both USB and UART makes rest of code work correctly. Enabling either uart or usb causes this peripheral to fail. Reducing amount of data does not help - or do I really have to go under 1ksps?

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 , Can you please share your project here so that we can try to debug what the issue is?

Regards,
Bragadeesh
0 Likes
lock attach
Attachments are accessible only for community members.

Here you are.
To simulate ADCs, you can apply 2MHz to DCLK pins, and 1-8kHz to DRDY pins.

 

Regards,

Stanislav

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @user_1570836 , Since you are invoking the call to the UART (or USB) from the SPI ISR, you need to ensure that the interrupt priority of the UART (or USB) should be greater than that of the SPI , otherwise they will never be called. And any blocking code called in a while () that are interrupt dependent  will never return. Either set the interrupt priority of UART (or USB) to a higher value or set a flag in the SPI interrupt and call the transfer functions in the main loop.

Let us know if this solves your issue.  

Regards,
Bragadeesh
0 Likes

Hello @BragadeeshV ,

Many thanks. I have increased USB interrupts priority to 6, and now I can see data on the pc.

Regards,

Stanislav