基于nRF52840-DK通过SPIM读取Arducam测试寄存器值异常排查
我采用裸机编程方式,将Arducam Mini 2MP Plus通过SPIM接口连接至nRF52840-DK开发板。根据Arducam datasheet,其SPI从机接口固定为SPI模式0(POL=0、PHA=1),SCLK最高速率8MHz,读写周期内需保持CSn有效,命令字节最高位为读写位(0读1写),低7位为寄存器地址。写入时为命令+数据字节,读取时为命令+哑字节,从MISO获取寄存器值。测试寄存器地址为0x00,我编写代码向其写入0x55后尝试读回,但得到的是0x52、0x1c等错误值而非0x55。已用逻辑分析仪验证nRF52840-DK的SPIM接口功能正常,请问我遗漏了什么或操作有误?
#include<stdint.h> #include "nrf52840.h" #include "nrf52840_bitfields.h" #include "nrf_delay.h" #include "nrf_gpio.h" int spi_init(unsigned int pin_cs, unsigned int pin_mosi, unsigned int pin_miso,unsigned int pin_clk) { NRF_SPIM3->PSEL.CSN = pin_cs; NRF_SPIM3->PSEL.SCK = pin_clk; NRF_SPIM3->PSEL.MOSI = pin_mosi; NRF_SPIM3->PSEL.MISO = pin_miso; /* Change the SPI mode here */ NRF_SPIM3->CONFIG = (SPIM_CONFIG_CPHA_Leading << SPIM_CONFIG_CPHA_Pos) | (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) | (SPIM_CONFIG_ORDER_MsbFirst << SPIM_CONFIG_ORDER_Pos); /* Change the frequency here */ NRF_SPIM3->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_M4; NRF_SPIM3->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos); return 0; } int spi_read(uint8_t address) { uint8_t tx_buf[2]; tx_buf[0] = address; tx_buf[1] = 0; // dummy byte uint8_t rx_buf[2] ; rx_buf[0] = 0; rx_buf[1] = 0; // dummy byte /* The SPI transaction will last for max(n_tx, n_rx) bytes */ NRF_SPIM3->TXD.MAXCNT = 2; NRF_SPIM3->RXD.MAXCNT = 2; NRF_SPIM3->TXD.PTR = (uint32_t)tx_buf; NRF_SPIM3->RXD.PTR = (uint32_t)rx_buf; NRF_SPIM3->EVENTS_END = 0; /* Start the transaction */ NRF_SPIM3->TASKS_START = 1; /* Busy wait until it has finished */ while (NRF_SPIM3->EVENTS_END == 0) { }; NRF_SPIM3->EVENTS_END = 0; return rx_buf[1]; } int spi_write(uint8_t address, uint8_t value) { static uint8_t tx_buf[2]; tx_buf[0] = address; tx_buf[1] = value; /* The SPI transaction will last for max(n_tx, n_rx) bytes */ NRF_SPIM3->TXD.MAXCNT = 2; NRF_SPIM3->RXD.MAXCNT = 0; NRF_SPIM3->TXD.PTR = (uint32_t)tx_buf; NRF_SPIM3->RXD.PTR = (uint32_t)0; NRF_SPIM3->EVENTS_END = 0; /* Start the transaction */ NRF_SPIM3->TASKS_START = 1; /* Busy wait until it has finished */ while (NRF_SPIM3->EVENTS_END == 0) { }; NRF_SPIM3->EVENTS_END = 0; return 0; } int main(void) { spi_init(31, 30, 29, 28); int data = 0xff; while(1){ // spi_testing spi_write(0x00 | 0x80,0x55); data = spi_read(0x00 & 0x7F); if(data == 0x55){ // SPIM is working } nrf_delay_ms(1000); } }
SPI模式配置错误:Arducam要求的是SPI模式0(POL=0、PHA=1),对应nRF52840 SPIM的配置应该是
SPIM_CONFIG_CPHA_Trailing(PHA=1,时钟后沿采样),但当前代码中用的是SPIM_CONFIG_CPHA_Leading(PHA=0,前沿采样),这会导致数据采样时序不匹配,是核心问题。修改CONFIG行:NRF_SPIM3->CONFIG = (SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos) | (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) | (SPIM_CONFIG_ORDER_MsbFirst << SPIM_CONFIG_ORDER_Pos);CSn时序问题:当前使用SPIM自动控制CSn,部分设备对CSn的拉低/拉高时序有严格要求。可以尝试改为手动控制CSn:在spi_init中配置CSn为输出引脚并初始拉高,在spi_write和spi_read函数开头拉低CSn,事务结束后拉高,确保读写操作间的CSn状态符合设备要求。
设备初始化延迟:Arducam上电后需要一定时间稳定,在spi_init后添加
nrf_delay_ms(100);等延迟,再执行读写操作。SPI速率确认:当前配置的是4MHz(
SPIM_FREQUENCY_FREQUENCY_M4),虽然低于设备最高8MHz,但可以尝试降低速率(比如1MHz)测试是否是速率兼容性问题。RX配置优化:在spi_write函数中,将
RXD.MAXCNT设为2并读取返回值,部分设备会在写入时返回状态字节,可用于排查写入是否成功。
内容的提问来源于stack exchange,提问作者shakingwindow

