没有 ASF 的 SAMD21 SPI 配置
SAMD21 SPI configuration without ASF
我目前正在进行基于 SAMD21J18A µC 的项目优化。
我的代码可以正常工作,但它基于 ASF API,我正在尝试逐渐摆脱它。
我的大部分代码都做到了,但是当我想在没有 ASF 的情况下配置 SPI 总线时遇到困难。
我的目标是通过 SPI 与移位寄存器通信以控制 LED。
我的 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);
}
我的代码没有 ASF (编辑) :
#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
}
我得到的结果是随机点亮 LED。我真的不能更精确地了解我得到的东西。
我想我遗漏了一些关于从属配置的东西,但我没找到如何去做。
有什么想法吗?
好吧...我偶然发现了一个可行的解决方案...如果您知道原因,请随时告诉我。
通过删除 PMUX 寄存器配置中的 Slave Select 引脚配置,一切正常(不知道为什么...)。
我的SPI总线配置函数现在是:
#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
}
很高兴你现在可以使用它了:)至于为什么...我现在对原始代码为什么不起作用有了一个合格的猜测。
我注意到有一个硅片 errata(我的浏览器说这个 link 由于缺少 https 不安全)可能会导致虚假 /SS 中断。
1.15.3 SPI with Slave Select Low Detection
If the SERCOM is enabled in SPI mode with SSL detection enabled (CTRLB.SSDE) and CTRLB.RXEN = 1, an erroneous slave select low interrupt (INTFLAG.SSL) can be generated.
Workaround
Enable the SERCOM first with CTRLB.RXEN = 0. In a subsequent write, set CTRLB.RXEN = 1.
受影响的芯片修订:A、B、C、D、E。
你的初始代码使用 RXEN = 1
直到我在评论中问为什么,所以在你删除那部分之前上面的勘误表可能适用。
我不确定这是否是您遇到问题的原因,但请记住这一点。检查您的芯片版本 - 在购买评估板时,通常会得到旧芯片,因为此类板很早就发布了。
我目前正在进行基于 SAMD21J18A µC 的项目优化。 我的代码可以正常工作,但它基于 ASF API,我正在尝试逐渐摆脱它。
我的大部分代码都做到了,但是当我想在没有 ASF 的情况下配置 SPI 总线时遇到困难。
我的目标是通过 SPI 与移位寄存器通信以控制 LED。
我的 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);
}
我的代码没有 ASF (编辑) :
#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
}
我得到的结果是随机点亮 LED。我真的不能更精确地了解我得到的东西。
我想我遗漏了一些关于从属配置的东西,但我没找到如何去做。
有什么想法吗?
好吧...我偶然发现了一个可行的解决方案...如果您知道原因,请随时告诉我。
通过删除 PMUX 寄存器配置中的 Slave Select 引脚配置,一切正常(不知道为什么...)。
我的SPI总线配置函数现在是:
#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
}
很高兴你现在可以使用它了:)至于为什么...我现在对原始代码为什么不起作用有了一个合格的猜测。
我注意到有一个硅片 errata(我的浏览器说这个 link 由于缺少 https 不安全)可能会导致虚假 /SS 中断。
1.15.3 SPI with Slave Select Low Detection
If the SERCOM is enabled in SPI mode with SSL detection enabled (CTRLB.SSDE) and CTRLB.RXEN = 1, an erroneous slave select low interrupt (INTFLAG.SSL) can be generated.
Workaround
Enable the SERCOM first with CTRLB.RXEN = 0. In a subsequent write, set CTRLB.RXEN = 1.
受影响的芯片修订:A、B、C、D、E。
你的初始代码使用 RXEN = 1
直到我在评论中问为什么,所以在你删除那部分之前上面的勘误表可能适用。
我不确定这是否是您遇到问题的原因,但请记住这一点。检查您的芯片版本 - 在购买评估板时,通常会得到旧芯片,因为此类板很早就发布了。