Author Topic: SAM C21: SPI problem receiving  (Read 1300 times)

0 Members and 1 Guest are viewing this topic.

Offline YipeeTopic starter

  • Contributor
  • Posts: 10
  • Country: cz
SAM C21: SPI problem receiving
« on: April 15, 2021, 01:35:36 pm »
Hi, I'm here again with the same problem. I can't receive three bytes sent via SPI. The communication runs at 12.5MHz, transaction appears about every 500 microseconds. Each transaction contains three bytes with 8 microseconds between bytes. The receiver uC is Microchip SAM C21 on Xplained board (exactly SERCOM5 on EXT2 header).

My best implementation was using DMAC and SPI drivers from ASF. It was working, but then I was developing other parts of application and now when I tested whole thing together I found that it does not work. Actually it received first few transactions okay and then next transactions was with 3rd byte always set to 0.

I assume that the uC is able to receive the communication without problems when it could receive few transactions correctly. I started to develop own SPI driver, but I need to keep the ASF due to the rest of application uses it (and there is no time to change it). So I make an empty ASF project where I made implementation based on example: https://microchipdeveloper.com/32arm:samc21-code-gcc-sercom-spi-slave-example. According to LED it could receive something. The strange thing was that the LED changed state when 2nd or 3rd byte was received but it ignored first byte but it could be caused by clock configuration because I kept the default. I wanted to know what it received so I added CAN driver from ASF, but I also edited clock configuration. Since that time I can't receive anything from SPI even when I load previous application  :-BROKE

Wire connection is still the same. Now I also tried to load app with SERCOM5 connected to SERCOM1 where SERCOM5 is master. It works better because it triggers receive interrupts, but received data are empty.

Is there a nice example how to periodically receive data via SPI (and could you share it with me please)? Or is it possible that the chip (shift register) is broken?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11228
  • Country: us
    • Personal site
Re: SAM C21: SPI problem receiving
« Reply #1 on: April 15, 2021, 07:54:24 pm »
Do you actually program SPI to receive all 3 bytes?

12 MHz is not that fast. There is no reason it should miss them.
Alex
 

Offline YipeeTopic starter

  • Contributor
  • Posts: 10
  • Country: cz
Re: SAM C21: SPI problem receiving
« Reply #2 on: April 16, 2021, 10:05:57 pm »
Yes I think.

The DMA implementation has the same configuration as SPI DMA example on asf.microchip.com site. I made size of buffer_rx three bytes big which should be enough since the size of copied memory is computed from it. Or did I miss anything?

When I did own driver, I created three uint8_t variables for data and one uint8_t for counter. When slave select switches to low, it should clear the counter to zero and with every received byte it should increment. I don't know how it could skip first byte. But it was just a prototype.

Unfortunately today I worked on different project and I don't have access to sources from home so I can provide parts of code on Monday if required.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11228
  • Country: us
    • Personal site
Re: SAM C21: SPI problem receiving
« Reply #3 on: April 16, 2021, 11:44:34 pm »
At least provide clock configuration and SPI configuration details. And also the code that starts the DMA transfer.

If the project does not contain anything proprietary, share the whole project.
Alex
 

Offline YipeeTopic starter

  • Contributor
  • Posts: 10
  • Country: cz
Re: SAM C21: SPI problem receiving
« Reply #4 on: April 20, 2021, 12:50:24 pm »
I'm sorry for the delay...

Unfortunately the project contains proprietary things. So I will share everything related to SPI:

I uploaded the required files to Google drive. Files mentioned below are in SPI_orig_proj.zip and in SPI_HW.zip there is a test project where I tried to create own driver before implementing it to main project. Link:https://drive.google.com/drive/folders/1HZlJX4PGAzB0Tq4RgGGzRb-SKkYCUjyk?usp=sharing

spi_slave.h:
Code: [Select]
#ifndef SPI_SLAVE_H_
#define SPI_SLAVE_H_

#define SPI_BUF_LENGTH 3

/************************************************************************/
/* DMA                                                                  */
/************************************************************************/

#define CONF_PERIPHERAL_TRIGGER_RX   SERCOM5_DMAC_ID_RX

/************************************************************************/
/* SPI                                                                  */
/************************************************************************/

#define CONF_SLAVE_SPI_MODULE SERCOM5
#define CONF_SLAVE_MUX_SETTING SPI_SIGNAL_MUX_SETTING_E
#define CONF_SLAVE_PINMUX_PAD0 PINMUX_PB02D_SERCOM5_PAD0
#define CONF_SLAVE_PINMUX_PAD1 PINMUX_PB03D_SERCOM5_PAD1
#define CONF_SLAVE_PINMUX_PAD2 PINMUX_PB00D_SERCOM5_PAD2
#define CONF_SLAVE_PINMUX_PAD3 PINMUX_PB01D_SERCOM5_PAD3

void spi_slave_init(void);
bool spi_read_data(uint8_t *buffer_rx);

#endif /* SPI_SLAVE_H_ */


spi_slave.c:
Code: [Select]
#include <asf.h>
#include "spi_slave.h"

struct spi_module spi_slave_instance;

struct dma_resource example_resource_rx;
DmacDescriptor example_descriptor_rx SECTION_DMAC_DESCRIPTOR;

static volatile bool transfer_complete_spi_slave = false;
static volatile bool transfer_rx_is_done = false;

COMPILER_ALIGNED(32)
static uint8_t dma_buffer_rx[SPI_BUF_LENGTH];

/************************************************************************/
/* DMA                                                                  */
/************************************************************************/

static void transfer_rx_done(struct dma_resource* const resource )
{
transfer_rx_is_done = true;
}

static void configure_dma_resource_rx(struct dma_resource *rx_resource)
{
struct dma_resource_config rx_config;

dma_get_config_defaults(&rx_config);

rx_config.peripheral_trigger = CONF_PERIPHERAL_TRIGGER_RX;
rx_config.trigger_action = DMA_TRIGGER_ACTION_BEAT;

dma_allocate(rx_resource, &rx_config);
}

static void setup_transfer_descriptor_rx(DmacDescriptor *rx_descriptor)
{
struct dma_descriptor_config rx_descriptor_config;

dma_descriptor_get_config_defaults(&rx_descriptor_config);

rx_descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
rx_descriptor_config.src_increment_enable = false;
rx_descriptor_config.block_transfer_count = sizeof(dma_buffer_rx)/sizeof(uint8_t);
rx_descriptor_config.source_address = (uint32_t)(&spi_slave_instance.hw->SPI.DATA.reg);
rx_descriptor_config.destination_address = (uint32_t)dma_buffer_rx + sizeof(dma_buffer_rx);

dma_descriptor_create(rx_descriptor, &rx_descriptor_config);
}

/************************************************************************/
/* SPI                                                                  */
/************************************************************************/

static void configure_spi_slave(void)
{
struct spi_config config_spi_slave;
/* Configure, initialize and enable SERCOM SPI module */
spi_get_config_defaults(&config_spi_slave);

config_spi_slave.mode = SPI_MODE_SLAVE;
config_spi_slave.mode_specific.slave.preload_enable = true;
config_spi_slave.mode_specific.slave.frame_format = SPI_FRAME_FORMAT_SPI_FRAME;

config_spi_slave.mux_setting = CONF_SLAVE_MUX_SETTING;

config_spi_slave.pinmux_pad0 = CONF_SLAVE_PINMUX_PAD0;
config_spi_slave.pinmux_pad1 = CONF_SLAVE_PINMUX_PAD1;
config_spi_slave.pinmux_pad2 = CONF_SLAVE_PINMUX_PAD2;
config_spi_slave.pinmux_pad3 = CONF_SLAVE_PINMUX_PAD3;

spi_init(&spi_slave_instance, CONF_SLAVE_SPI_MODULE, &config_spi_slave);

spi_enable(&spi_slave_instance);
}

void spi_slave_init(void)
{
configure_spi_slave();

configure_dma_resource_rx(&example_resource_rx);
setup_transfer_descriptor_rx(&example_descriptor_rx);
dma_add_descriptor(&example_resource_rx, &example_descriptor_rx);

dma_register_callback(&example_resource_rx, transfer_rx_done, DMA_CALLBACK_TRANSFER_DONE);
dma_enable_callback(&example_resource_rx, DMA_CALLBACK_TRANSFER_DONE);

dma_start_transfer_job(&example_resource_rx);
}

bool spi_read_data(uint8_t *buffer_rx) {
if (transfer_rx_is_done) {
transfer_rx_is_done = false;

memcpy(buffer_rx, dma_buffer_rx, SPI_BUF_LENGTH * sizeof(uint8_t)); // copy data out
//memset(dma_buffer_rx, 0, SPI_BUF_LENGTH * sizeof(uint8_t)); // clear buffer - not necessary but to be sure

dma_start_transfer_job(&example_resource_rx); // start reading SPI bus again

return true;
}
return false;
}


conf_clocks.h
Code: [Select]
/**
 * \file
 *
 * \brief SAM C21 Clock configuration
 *
 * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip
 * software and any derivatives exclusively with Microchip products.
 * It is your responsibility to comply with third party license terms applicable
 * to your use of third party software (including open source software) that
 * may accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * \asf_license_stop
 *
 */
#include <clock.h>

#ifndef CONF_CLOCKS_H_INCLUDED
#define CONF_CLOCKS_H_INCLUDED

/* System clock bus configuration */
#define CONF_CLOCK_FLASH_WAIT_STATES            3
#define CONF_CLOCK_CPU_DIVIDER                  SYSTEM_MAIN_CLOCK_DIV_1

/* SYSTEM_CLOCK_SOURCE_OSC48M configuration - Internal 48MHz oscillator */
#define CONF_CLOCK_OSC48M_FREQ_DIV              SYSTEM_OSC48M_DIV_1
#define CONF_CLOCK_OSC48M_ON_DEMAND             true
#define CONF_CLOCK_OSC48M_RUN_IN_STANDBY        false

/* SYSTEM_CLOCK_SOURCE_XOSC configuration - External clock/oscillator */
#define CONF_CLOCK_XOSC_ENABLE                  false
#define CONF_CLOCK_XOSC_EXTERNAL_CRYSTAL        SYSTEM_CLOCK_EXTERNAL_CRYSTAL
#define CONF_CLOCK_XOSC_EXTERNAL_FREQUENCY      12000000UL
#define CONF_CLOCK_XOSC_STARTUP_TIME            SYSTEM_XOSC_STARTUP_32768
#define CONF_CLOCK_XOSC_AUTO_GAIN_CONTROL       true
#define CONF_CLOCK_XOSC_ON_DEMAND               true
#define CONF_CLOCK_XOSC_RUN_IN_STANDBY          false
#define CONF_CLOCK_XOSC_FAILURE_DETECTOR_PRE    SYSTEM_CLOCK_XOSC_FAILURE_DETECTOR_PRESCALER_1
#define CONF_CLOCK_XOSC_FAILURE_DETECTOR_EVENT_OUTPUT_ENABLE  false
#define CONF_CLOCK_XOSC_FAILURE_DETECTOR_ENABLE  false
#define CONF_CLOCK_XOSC_FAILURE_SWITCH_BACK_ENABLE  false

/* SYSTEM_CLOCK_SOURCE_XOSC32K configuration - External 32KHz crystal/clock oscillator */
#define CONF_CLOCK_XOSC32K_ENABLE               true
#define CONF_CLOCK_XOSC32K_EXTERNAL_CRYSTAL     SYSTEM_CLOCK_EXTERNAL_CRYSTAL
#define CONF_CLOCK_XOSC32K_STARTUP_TIME         SYSTEM_XOSC32K_STARTUP_65536
#define CONF_CLOCK_XOSC32K_ENABLE_1KHZ_OUPUT    false
#define CONF_CLOCK_XOSC32K_ENABLE_32KHZ_OUTPUT  true
#define CONF_CLOCK_XOSC32K_ON_DEMAND            false
#define CONF_CLOCK_XOSC32K_RUN_IN_STANDBY       true
#define CONF_CLOCK_XOSC32K_FAILURE_DETECTOR_PRE    SYSTEM_CLOCK_XOSC32K_FAILURE_DETECTOR_PRESCALER_1
#define CONF_CLOCK_XOSC32K_FAILURE_DETECTOR_EVENT_OUTPUT_ENABLE  false
#define CONF_CLOCK_XOSC32K_FAILURE_DETECTOR_ENABLE  false
#define CONF_CLOCK_XOSC32K_FAILURE_SWITCH_BACK_ENABLE  false



/* SYSTEM_CLOCK_SOURCE_OSC32K configuration - Internal 32KHz oscillator */
#define CONF_CLOCK_OSC32K_ENABLE                false
#define CONF_CLOCK_OSC32K_STARTUP_TIME          SYSTEM_OSC32K_STARTUP_130
#define CONF_CLOCK_OSC32K_ENABLE_1KHZ_OUTPUT    true
#define CONF_CLOCK_OSC32K_ENABLE_32KHZ_OUTPUT   true
#define CONF_CLOCK_OSC32K_ON_DEMAND             true
#define CONF_CLOCK_OSC32K_RUN_IN_STANDBY        false


/* SYSTEM_CLOCK_SOURCE_DPLL configuration - Digital Phase-Locked Loop */
#define CONF_CLOCK_DPLL_ENABLE                  true
#define CONF_CLOCK_DPLL_ON_DEMAND               false
#define CONF_CLOCK_DPLL_RUN_IN_STANDBY          true
#define CONF_CLOCK_DPLL_LOCK_BYPASS             false
#define CONF_CLOCK_DPLL_WAKE_UP_FAST            false
#define CONF_CLOCK_DPLL_LOW_POWER_ENABLE        false

#define CONF_CLOCK_DPLL_LOCK_TIME               SYSTEM_CLOCK_SOURCE_DPLL_LOCK_TIME_DEFAULT
#define CONF_CLOCK_DPLL_REFERENCE_CLOCK         SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_XOSC32K
#define CONF_CLOCK_DPLL_FILTER                  SYSTEM_CLOCK_SOURCE_DPLL_FILTER_DEFAULT
#define CONF_CLOCK_DPLL_PRESCALER               SYSTEM_CLOCK_SOURCE_DPLL_DIV_1


#define CONF_CLOCK_DPLL_REFERENCE_FREQUENCY     32767
#define CONF_CLOCK_DPLL_REFERENCE_DIVIDER       1
#define CONF_CLOCK_DPLL_OUTPUT_FREQUENCY        48000000

/* DPLL GCLK reference configuration */
#define CONF_CLOCK_DPLL_REFERENCE_GCLK_GENERATOR GCLK_GENERATOR_1
/* DPLL GCLK 32K reference configuration */
#define CONF_CLOCK_DPLL_REFERENCE_GCLK_32K_GENERATOR GCLK_GENERATOR_1

/* Set this to true to configure the GCLK when running clocks_init. If set to
 * false, none of the GCLK generators will be configured in clocks_init(). */
#define CONF_CLOCK_CONFIGURE_GCLK               true

/* Configure GCLK generator 0 (Main Clock) */
#define CONF_CLOCK_GCLK_0_ENABLE                true
#define CONF_CLOCK_GCLK_0_RUN_IN_STANDBY        true
#define CONF_CLOCK_GCLK_0_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_0_PRESCALER             1
#define CONF_CLOCK_GCLK_0_OUTPUT_ENABLE         false

/* Configure GCLK generator 1 */
#define CONF_CLOCK_GCLK_1_ENABLE                true
#define CONF_CLOCK_GCLK_1_RUN_IN_STANDBY        true
#define CONF_CLOCK_GCLK_1_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_XOSC32K
#define CONF_CLOCK_GCLK_1_PRESCALER             1
#define CONF_CLOCK_GCLK_1_OUTPUT_ENABLE         false

/* Configure GCLK generator 2  */
#define CONF_CLOCK_GCLK_2_ENABLE                false
#define CONF_CLOCK_GCLK_2_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_2_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_2_PRESCALER             1
#define CONF_CLOCK_GCLK_2_OUTPUT_ENABLE         false

/* Configure GCLK generator 3 */
#define CONF_CLOCK_GCLK_3_ENABLE                false
#define CONF_CLOCK_GCLK_3_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_3_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_3_PRESCALER             1
#define CONF_CLOCK_GCLK_3_OUTPUT_ENABLE         false

/* Configure GCLK generator 4 */
#define CONF_CLOCK_GCLK_4_ENABLE                false
#define CONF_CLOCK_GCLK_4_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_4_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_4_PRESCALER             1
#define CONF_CLOCK_GCLK_4_OUTPUT_ENABLE         false

/* Configure GCLK generator 5 */
#define CONF_CLOCK_GCLK_5_ENABLE                false
#define CONF_CLOCK_GCLK_5_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_5_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_5_PRESCALER             1
#define CONF_CLOCK_GCLK_5_OUTPUT_ENABLE         false

/* Configure GCLK generator 6 */
#define CONF_CLOCK_GCLK_6_ENABLE                false
#define CONF_CLOCK_GCLK_6_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_6_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_6_PRESCALER             1
#define CONF_CLOCK_GCLK_6_OUTPUT_ENABLE         false

/* Configure GCLK generator 7 */
#define CONF_CLOCK_GCLK_7_ENABLE                false
#define CONF_CLOCK_GCLK_7_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_7_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC48M
#define CONF_CLOCK_GCLK_7_PRESCALER             1
#define CONF_CLOCK_GCLK_7_OUTPUT_ENABLE         false

/* Configure GCLK generator 8 */
#define CONF_CLOCK_GCLK_8_ENABLE                true
#define CONF_CLOCK_GCLK_8_RUN_IN_STANDBY        false
#define CONF_CLOCK_GCLK_8_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_DPLL
#define CONF_CLOCK_GCLK_8_PRESCALER             1
#define CONF_CLOCK_GCLK_8_OUTPUT_ENABLE         false
#endif /* CONF_CLOCKS_H_INCLUDED */



main:
Code: [Select]

 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11228
  • Country: us
    • Personal site
Re: SAM C21: SPI problem receiving
« Reply #5 on: April 20, 2021, 06:59:43 pm »
To debug this, set a pin high before the call to dma_start_transfer_job() and set it low in transfer_rx_done(). Now capture SPI data and this pin at the same time using oscilloscope or a logic analyzer.

You may be missing some bytes because you don't start the DMA job in time or something like this.

I see nothing wrong with the code, and it should be able to receive bytes at 12 MHz SPI clock.
Alex
 

Offline YipeeTopic starter

  • Contributor
  • Posts: 10
  • Country: cz
Re: SAM C21: SPI problem receiving
« Reply #6 on: April 23, 2021, 12:25:40 pm »
Thank you Alex.

I don't know how it looks like on oscilloscope because colleague took probes with himself and he is few days away but it seems you found the problem.

When I called the dma_start_transfer_job() in transfer_rx_done() then it received all 3 bytes correctly. Also it seems stable but when it boots in the middle of transaction then it shifts the bytes but I hope I will be able to fix it by catching the first SPI_INTERRUPT_FLAG_TX_COMPLETE.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf