cembeddedspicortex-msamd21

SAMD21 SPI configuration without ASF


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 ?


Solution

  • 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
    }