I'm currently working on a project optimization based on SAMD21J18A µC. My code is working but it's based on ASF API an I'm trying to gradually get rid of it.
I managed to do it for most of my code but I'm experiencing difficulties when I want to configure the SPI bus without ASF.
My goal is to communicate through SPI with a shift register to control LEDs.
My code with ASF :
void configure_sercom0_spi(struct spi_module *const spi_master_instance, struct spi_slave_inst *const slave){
struct spi_config master_config;
struct spi_slave_inst_config slave_config;
spi_get_config_defaults(&master_config);
master_config.mux_setting = SPI_SIGNAL_MUX_SETTING_E; // DOPO: 0x1, DIPO: 0x0
master_config.pinmux_pad0 = PINMUX_PA04D_SERCOM0_PAD0; // MISO PA04
master_config.pinmux_pad1 = PINMUX_PA05D_SERCOM0_PAD1; // Slave Selection PA05
master_config.pinmux_pad2 = PINMUX_PA06D_SERCOM0_PAD2; // MOSI PA06
master_config.pinmux_pad3 = PINMUX_PA07D_SERCOM0_PAD3; // SCK PA07
master_config.mode_specific.master.baudrate = 0xF4240; // 1000000
spi_slave_inst_get_config_defaults(&slave_config);
slave_config.ss_pin = PIN_PA05;
spi_init(spi_master_instance, SERCOM0, &master_config);
spi_attach_slave(slave, &slave_config);
spi_enable(spi_master_instance);
}
My code without ASF (EDIT) :
#define SPI_LIGHTING_MAIN_CLK_FREQ 0x7A1200 // 8Mhz
#define SPI_LIGHTING_BAUDRATE 0xF4240 // 1000000
// Peripheral function D selected
#define SPI_LIGHTING_PERIPHERAL_MUX_EVEN 0x3
#define SPI_LIGHTING_PERIPHERAL_MUX_ODD 0x3
void gclk_spi_config(void){
// GCLK generator 0 ; No division
GCLK->GENDIV.reg |= GCLK_GENDIV_ID(0)
| GCLK_GENDIV_DIV(1);
// Generic generator 0 ; OSC8M oscillator
GCLK->GENCTRL.reg |= GCLK_GENCTRL_ID(0)
| GCLK_GENCTRL_GENEN
| GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSC8M_Val)
| GCLK_GENCTRL_OE;
// SERCOM 0 peripheral ; clock generator 0
GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_ID(GCLK_CLKCTRL_ID_SERCOM0_CORE_Val)
| GCLK_CLKCTRL_GEN(GCLK_CLKCTRL_GEN_GCLK0_Val)
| GCLK_CLKCTRL_CLKEN;
// Synchronous bus clock without prescaler
PM->APBCSEL.reg |= PM_APBCSEL_APBCDIV(PM_APBCSEL_APBCDIV_DIV1_Val);
// Enable SERCOM 0
PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0;
}
void configure_spi_master(void){
// Software reset
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST;
while(SERCOM0->SPI.CTRLA.bit.SWRST){}; // Wait until reset
// SPI master ; SPI frame format ; DIPO 0x0; DOPO 0x1
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val)
| SERCOM_SPI_CTRLA_FORM(0)
| SERCOM_SPI_CTRLA_DIPO(0)
| SERCOM_SPI_CTRLA_DOPO(1);
// Slave select low detect enable ; Master slave select enable
SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_SSDE
| SERCOM_SPI_CTRLB_MSSEN;
/*
/ Fix the baud rate at 1000000
/ SystemCoreClock / (2 * baudrate) - 1
/ SystemCoreClock = 8000000
/ baudrate = 1000000
*/
SERCOM0->SPI.BAUD.bit.BAUD = (SPI_LIGHTING_MAIN_CLK_FREQ) / (2 * (SPI_LIGHTING_BAUDRATE)) - 1;
// Configure PIN DIPO
PORT->Group[0].PINCFG[4].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[2].bit.PMUXE = SPI_LIGHTING_PERIPHERAL_MUX_EVEN;
PORT->Group[0].PINCFG[4].bit.INEN = 0x1; // Enable input
// Configure PIN SS
PORT->Group[0].PINCFG[5].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[2].bit.PMUXO = SPI_LIGHTING_PERIPHERAL_MUX_ODD;
// Configure PIN DOPO
PORT->Group[0].PINCFG[6].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[3].bit.PMUXE = SPI_LIGHTING_PERIPHERAL_MUX_EVEN;
// Configure PIN SCK
PORT->Group[0].PINCFG[7].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[3].bit.PMUXO = SPI_LIGHTING_PERIPHERAL_MUX_ODD;
// Enable SPI
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
while(!(SERCOM0->SPI.CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE)){}; // Wait until SPI is enabled
NVIC_SetPriority(SERCOM0_IRQn, 3); // Set the interrupt priority to 3 (lowest value)
NVIC_EnableIRQ(SERCOM0_IRQn); // Enable the interrupt
}
The result I have is a random lighting of the LEDs. I cant be really more precise about what I obtain.
I think I'm missing something about slave configuration but I don't manage to find how to do it.
any idea ?
Well... I found a solution that works a bit by chance... If you know the reason why do not hesitate to let me know.
By deleting the Slave Select pin configuration in the PMUX register's configuration, everything works perfectly (no idea why...).
My SPI bus configuration function is now :
#define SPI_LIGHTING_MAIN_CLK_FREQ 0x7A1200 // 8Mhz
#define SPI_LIGHTING_BAUDRATE 0xF4240 // 1000000
// Peripheral function D selected
#define USART_MIDI_PERIPHERAL_MUX_ODD 0x3
#define USART_MIDI_PERIPHERAL_MUX_EVEN 0x3
void configure_spi(void){
// Software reset
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST;
while(SERCOM0->SPI.CTRLA.bit.SWRST){}; // Wait until reset
// SPI master ; SPI frame format ; DIPO 0x0; DOPO 0x1
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val)
| SERCOM_SPI_CTRLA_FORM(0)
| SERCOM_SPI_CTRLA_DIPO(0)
| SERCOM_SPI_CTRLA_DOPO(1);
// Slave select low detect enable ; Master slave selection enable
SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_MSSEN
| SERCOM_SPI_CTRLB_SSDE;
/*
/ Fix the baud rate at 1000000
/ SystemCoreClock / (2 * baudrate) - 1
/ SystemCoreClock = 8000000
/ baudrate = 1000000
*/
SERCOM0->SPI.BAUD.bit.BAUD = (float)(SPI_LIGHTING_MAIN_CLK_FREQ ) / (2 * (float)(SPI_LIGHTING_BAUDRATE )) - 1;
// Configure PIN DIPO
PORT->Group[0].PINCFG[4].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[2].bit.PMUXE = SPI_LIGHTING_PERIPHERAL_MUX_EVEN;
PORT->Group[0].PINCFG[4].bit.INEN = 0x1; // Enable input
// Configure PIN DOPO
PORT->Group[0].PINCFG[6].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[3].bit.PMUXE = SPI_LIGHTING_PERIPHERAL_MUX_EVEN;
// Configure PIN SCK
PORT->Group[0].PINCFG[7].bit.PMUXEN = 0x1; // Enable peripheral multiplexing
PORT->Group[0].PMUX[3].bit.PMUXO = SPI_LIGHTING_PERIPHERAL_MUX_ODD;
// Enable SPI
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
while(!(SERCOM0->SPI.CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE)){}; // Wait until SPI is enabled
NVIC_SetPriority(SERCOM0_IRQn, 3); // Set the interrupt priority to 3 (lowest value)
NVIC_EnableIRQ(SERCOM0_IRQn); // Enable the interrupt
}