SAM D10 SPI too slow

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Afternoon )

 

Its my first time working with SAM D10 (and with any other SAM), and im trying to connect an 320x240x16bit LCD on SPI. 

It does work but takes about 10 secs to erase the screen (what would need about 153 kB to be transfered).

The thing is i connected an oscilloscope to the SCK pin, and i got that the clock runs actually at 2 Mhz as it should, but the space between bytes is too long.

The funny thing is that this same code on ATmega328 works perfect without any delay.

 

The code for transfer thru SPI will be as follows

uint8_t * lld_LCD_tranfer(uint8_t *data, uint8_t byte_count)
{
    spi_select_slave(&spi_master_instance, &LCD_slave, true);
    
    spi_transceive_buffer_job(&spi_master_instance, data, data, byte_count);
    
    while(!transrev_complete_spi_master) {}
    
    transrev_complete_spi_master = false;
    spi_select_slave(&spi_master_instance, &LCD_slave, false);
    
    return data;
}

static void lld_callback_spi_master(struct spi_module *const module)
{
    transrev_complete_spi_master = true;
}

* i tried also with spi_transceive_buffer_wait() but doesnt make any difference.

 

The code for eraseing the screen will be as follows

void lcd_clear(uint16_t color)
{
    SetWindow(0, SIZE_X, 0, SIZE_Y);
    SendColor565(color, CACHE_SIZE_MEM);
}

void SendColor565(uint16_t color, uint32_t count)
{
    CommandSend(RAMWR);
    while (count--) {
        Data16BitsSend(color);
    }
}

void Data16BitsSend(uint16_t data)
{
	uint8_t data2[2];
	data2[1] = data;// & 0xff;
	data2[0] = (data >> 8);// & 0xff;
	lld_LCD_tranfer(&data2, 2);
}

 

And the init for the SPI will be as follows

void lld_spi_init(void)
{
    struct spi_config config_spi_master;
    struct spi_slave_inst_config slave_dev_config;
    
    // SET LCD SLAVE
    spi_slave_inst_get_config_defaults(&slave_dev_config);
    slave_dev_config.ss_pin = LCD_PIN_CS;
    spi_attach_slave(&LCD_slave, &slave_dev_config);
    
    // SET TOUCH SLAVE
    spi_slave_inst_get_config_defaults(&slave_dev_config);
    slave_dev_config.ss_pin = TOUCH_PIN_CS;
    spi_attach_slave(&TOUCH_slave, &slave_dev_config);
    
    spi_get_config_defaults(&config_spi_master);
    config_spi_master.mux_setting = SPI_SIGNAL_MUX_SETTING_K; //(Di : 0x02 / Do : 0x02)
    config_spi_master.pinmux_pad0 = PINMUX_UNUSED;
    config_spi_master.pinmux_pad1 = SPI_BUS_SCK; // SCK  pin1
    config_spi_master.pinmux_pad2 = SPI_BUS_MISO; // MISO pin2
    config_spi_master.pinmux_pad3 = SPI_BUS_MOSI; // MOSI pin3
    config_spi_master.data_order = SPI_DATA_ORDER_MSB;
    config_spi_master.character_size = SPI_CHARACTER_SIZE_8BIT;
    config_spi_master.mode = SPI_MODE_MASTER;
    spi_init(&spi_master_instance, SERCOM0, &config_spi_master);
    
    spi_set_baudrate(&spi_master_instance, (uint32_t)SPI_CLK_FREQ); 
    
    spi_enable(&spi_master_instance);
    
    spi_register_callback(&spi_master_instance, lld_callback_spi_master, SPI_CALLBACK_BUFFER_TRANSCEIVED);
    spi_enable_callback(&spi_master_instance, SPI_CALLBACK_BUFFER_TRANSCEIVED);
}

 

Ill appreciate if someone has any idea of what im doing wrong.

And another thing, i checked the cpu freq with the system_cpu_clock_get_hz() function, and i got its running at 8 Mhz.

I looked for some examples on how to set the freq to 48 Mhz but the examples i got wount work. If someone has got an example on how to do it will be great.

 

Thank you )

 

 

Last Edited: Mon. Jun 29, 2020 - 04:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

brujo311 wrote:
the space between bytes is too long

Did you use ASF ?

 

I found that exact same problem when using ASF years ago.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeap, is done in ASF.

Any solution for that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You will have to re-write that part.

 

The trouble, IIRC, was that ASF searches through all possible SPIs in its handler - because it needs to be general.

 

You can be specific, and just hard-code the particular SPI you are using ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, thx for your reply.

 

I hard-coded as much as my understanding can... got it a little faster, but still is too slow.

This is the new write rutine, guess it has no time loseing code in it.

uint8_t lld_spi_write(struct spi_module *module, uint16_t tx_data)
{
	SercomSpi *const spi_module = &(module->hw->SPI);

	/* Write the character to the DATA register */
	spi_module->DATA.reg = tx_data & SERCOM_SPI_DATA_MASK;
	
	while (!spi_is_ready_to_write(module));
	
	return spi_module->DATA.reg & SERCOM_SPI_DATA_MASK;
}

uint8_t * lld_LCD_tranfer(uint8_t *data, uint8_t byte_count)
{
	uint8_t a = 0;
	
	spi_select_slave(&spi_master_instance, &LCD_slave, true);
	
	while(byte_count) {
		data[a] = lld_spi_write(&spi_master_instance, data[a++]);
		byte_count--;
	}
		
	spi_select_slave(&spi_master_instance, &LCD_slave, false);
	
	return data;
}

If there is a way to address the hardware without ASF would be nice if you have any example.

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have some inline functions here that were a speed improvement particularity for the case where there is no need to read from the slave:

https://community.atmel.com/forum/samd21-spi-dma

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

brujo311 wrote:
If there is a way to address the hardware without ASF

Of course there is: your code can read from & write to any register - that's all that ASF is doing!

 

 

 

would be nice if you have any example.

Take ASF as your example - it's all source code.

 

I find the easiest way to follow it is by stepping in the debugger - it can be hard to follow just by reading the source code, as there are so many options.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void Data16BitsSend(uint16_t data)

I would expect that spi_transceive_buffer_job() has significant overhead, and your code would go MUCH faster if you set things up so that it was called with buffers that were much larger than 2 bytes.

Of course, this demonstrates a weakness of the ASF libraries (and vendor libraries in general), where they end up not being optimized in a way that fits the user task :-(

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, will try that one and see if i get any faster.

And i did try with long buffers (128 bytes, even 640 bytes) but still the spacing between bytes seems to be the same.

 

Last Edited: Fri. Jul 3, 2020 - 01:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perhaps you're seeing gaps between octets because you are not accounting for double buffering in the Sercom (e.g. Tx holding and shift registers). The following snippet of code will transmit back-to-back octets by keeping the Tx holding register full.

void
sam_spi_readwrite_pio(SercomSpi *r, uint8_t *rd, uint16_t rdlen)
{
        const uint8_t *wr;
        uint16_t wrlen;

        wr = rd;
        wrlen = rdlen;

        /* Loop until all writes and reads are complete. */
        while (wrlen || rdlen) {
                if (wrlen && (r->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE) != 0) {
                        /* We can write another octet. */
                        r->DATA.reg = (uint32_t)*wr++;
                        wrlen--;
                }

                if (rdlen && (r->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC) != 0) {
                        /* We can read another octet. */
                        *rd++ = (uint8_t)r->DATA.reg;
                        rdlen--;
                }
        }
}

No ASF required.

 

Steve

Maverick Embedded Technologies Ltd. Home of wAVR and Maven.

wAVR: WiFi AVR ISP/PDI/uPDI Programmer.

Maven: WiFi ARM Cortex-M Debugger/Programmer

https://www.maverick-embedded.co...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello again...

Thanks for all the replies but still im not getting it to work. Im about to shift back to an ATmega328 that gets the job done cuz is the 3rd day im stuck with all the serial interfaces of this SAM thing...

Not even the USART is working ( for another purpose ), and already got the memory 95% coded up.

Now im trying to code without ASF and this is what i get:

Would like to understand why this

PM->EXTCTRL.bit.SETDIS = 1;

gets a 1 written to SETDIS bit, and this

PORT->Group[0].OUTCLR.reg |= (1 << LCD_PIN_CS);

is clearing a bit in the port... but this

SERCOM0->SPI.CTRLA.bit.MODE = 0x03; // (3 bits)
SERCOM0->SPI.CTRLA.bit.CPOL = 0; // (0 -> SCK low)
SERCOM0->SPI.CTRLA.bit.CPHA = 0; // (0 -> leading edge sample)
SERCOM0->SPI.CTRLA.bit.FORM = 0; // (4 bits)
SERCOM0->SPI.CTRLA.bit.DIPO = 0x02; // MISO (2 bits) Sercom pin 2
SERCOM0->SPI.CTRLA.bit.DOPO = 0x02; // MOSI (2 bits) Sercom pin 3 / SCK Sercom pin 1
SERCOM0->SPI.CTRLA.bit.RUNSTDBY = 1; // Run in standby
SERCOM0->SPI.CTRLA.bit.DORD = 0; // (0 -> MSB first)

or even this

SERCOM0->SPI.CTRLA.reg = 0x0022008C;

is not getting the CTRLA of the SERCOM0 written. (checked with the debugger and pops 0 in the watch, same with the BAUD register that i also wrote the same way).

 

The write protection in PAC2 seems to be all cleared, and even if i wirte a one to SERCOM0 in PAC2->WPCLR.reg will get me to a "dummy interrupt" loop which i guess is an error exception or something of the sort.

 

I dont understand this device any more...

 

Someone save me... please...

Last Edited: Sun. Jul 5, 2020 - 02:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you make sure the sercom was disabled before trying to configure SPI?

 

 

The following registers are enable-protected, meaning that they can only be written when the SPI is disabled (CTRL.ENABLE is zero):

  •  Control A register (CTRLA), except Enable (CTRLA.ENABLE) and Software Reset (CTRLA.SWRST)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The CTRLA register reads 0... always... so guess that includes ENABLED = 0 too

Whatever i do i always get CTRLA, CTRLB, ...... all SPI registers in 0.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe you don't have the PM and GCLK set (would help if you posted the code). Anyway examples here:

https://community.atmel.com/forum/sercom-usart-samd09

/Lars