ASR6601SE与ADXL345 SPI通信异常求助及代码修正需求
ASR6601SE与ADXL345 SPI通信故障排查及修正代码
核心问题分析
- 重复初始化SPI:原代码在while循环中反复执行SPI初始化与反初始化,导致硬件频繁重置,通信时序完全混乱。SPI初始化只需在系统启动时执行一次。
- SPI模式不匹配:ADXL345默认使用SPI模式0(CPOL=0,CPHA=0),但默认的SSP初始化参数不满足该要求,需手动配置时序。
- 寄存器地址位错误:
- 写操作时,ADXL345的地址最高位应为0(写标志),原代码未正确处理该位,导致寄存器寻址失败;
- 多字节位(0x40)的使用逻辑混乱,不符合ADXL345的通信协议。
- SPI收发逻辑错误:SPI是全双工通信,单独调用发送和接收函数无法触发从设备的数据输出,必须在同一传输周期内同步收发。
- NSS引脚时序混乱:main函数与读写函数中重复控制NSS引脚,导致片选信号不符合从设备要求。
修正后的完整代码
#include <stdio.h> #include "tremo_regs.h" #include "tremo_rcc.h" #include "tremo_spi.h" #include "tremo_gpio.h" #include <stdarg.h> #include "tremo_uart.h" #include "tremo_delay.h" #define SPI_X SSP1 #define NSS_PIN GPIO_PIN_9 #define NSS_PORT GPIOB uint8_t spi1_rx_data[6] = {0}; void uart_log_init(void); void spi_init(void); uint8_t spi_write_reg(uint8_t addr, uint8_t data); void spi_read_regs(uint8_t addr, uint8_t* buf, uint8_t size); int main(void) { // 初始化外设时钟 rcc_enable_peripheral_clk(RCC_PERIPHERAL_GPIOB, true); rcc_enable_peripheral_clk(RCC_PERIPHERAL_UART0, true); rcc_enable_peripheral_clk(RCC_PERIPHERAL_SSP1, true); // 初始化UART日志 uart_log_init(); // 初始化NSS引脚 gpio_init(NSS_PORT, NSS_PIN, GPIO_MODE_OUTPUT_PP_HIGH); gpio_write(NSS_PORT, NSS_PIN, GPIO_LEVEL_HIGH); // 配置SPI引脚复用 gpio_set_iomux(GPIOB, GPIO_PIN_8, 4); // CLK:GP24 gpio_set_iomux(GPIOB, GPIO_PIN_10, 4); // TX:GP26 gpio_set_iomux(GPIOB, GPIO_PIN_11, 4); // RX:GP27 // 初始化SPI(仅执行一次) spi_init(); // 初始化ADXL345 spi_write_reg(0x2D, 0x00); // 复位电源控制寄存器 delay_ms(10); spi_write_reg(0x2D, 0x08); // 设置为测量模式 delay_ms(10); spi_write_reg(0x31, 0x01); // 设置量程为±4g delay_ms(10); /* Infinite loop */ while (1) { // 读取6个数据寄存器(0x32到0x37) spi_read_regs(0x32, spi1_rx_data, 6); // 打印读取结果 for (int i = 0; i < 6; i++) { printf("ssp_Receive_data[%d], 0x%x\n", i, spi1_rx_data[i]); } delay_ms(500); } } void uart_log_init(void) { // 配置UART0引脚复用 gpio_set_iomux(GPIOB, GPIO_PIN_0, 1); // UART0 Tx gpio_set_iomux(GPIOB, GPIO_PIN_1, 1); // UART0 Rx uart_config_t uart_config; uart_config_init(&uart_config); uart_config.baudrate = UART_BAUDRATE_115200; uart_init(UART0, &uart_config); uart_cmd(UART0, ENABLE); } void spi_init(void) { ssp_init_t init_struct; ssp_init_struct(&init_struct); // 配置为SPI主模式,匹配ADXL345的模式0 init_struct.mode = SSP_MODE_MASTER; init_struct.clock_polarity = SSP_CLOCK_POLARITY_LOW; // CPOL=0 init_struct.clock_phase = SSP_CLOCK_PHASE_FIRST_EDGE; // CPHA=0 init_struct.frame_format = SSP_FRAME_FORMAT_SPI; init_struct.data_width = SSP_DATA_WIDTH_8BIT; init_struct.clock_rate = 1000000; // 1MHz时钟,ADXL345支持最高5MHz ssp_init(SPI_X, &init_struct); ssp_cmd(SPI_X, ENABLE); } uint8_t spi_write_reg(uint8_t addr, uint8_t data) { uint8_t tx_buf[2] = {0}; uint8_t rx_buf[2] = {0}; // ADXL345写操作:最高位为0,单字节写无需多字节位 tx_buf[0] = addr & 0x7F; // 清除最高位,标记为写操作 tx_buf[1] = data; gpio_write(NSS_PORT, NSS_PIN, GPIO_LEVEL_LOW); delay_us(1); // 确保片选稳定 // 同步收发,完成写操作 ssp_transfer_data(SPI_X, tx_buf, rx_buf, sizeof(tx_buf)); gpio_write(NSS_PORT, NSS_PIN, GPIO_LEVEL_HIGH); delay_us(1); return rx_buf[1]; // ADXL345写操作返回0xFF } void spi_read_regs(uint8_t addr, uint8_t* buf, uint8_t size) { uint8_t tx_buf[1] = {0}; // ADXL345读操作:最高位为1,置位多字节位 tx_buf[0] = (addr & 0x7F) | 0x80 | 0x40; gpio_write(NSS_PORT, NSS_PIN, GPIO_LEVEL_LOW); delay_us(1); // 先发送读地址 ssp_transfer_data(SPI_X, tx_buf, buf, 1); // 再读取指定长度的数据 ssp_transfer_data(SPI_X, NULL, buf, size); gpio_write(NSS_PORT, NSS_PIN, GPIO_LEVEL_HIGH); delay_us(1); } #ifdef USE_FULL_ASSERT void assert_failed(void* file, uint32_t line) { (void)file; (void)line; while (1) { } } #endif
关键修正点说明
- SPI初始化优化:将SPI初始化移到系统启动阶段,仅执行一次,避免硬件反复重置。
- SPI模式匹配:手动配置SSP为SPI模式0,完全匹配ADXL345的默认通信时序。
- 地址位修正:
- 写操作时清除地址最高位,明确标记为写操作;
- 读操作时同时设置读标志位(最高位)和多字节位,符合ADXL345协议要求。
- 全双工收发逻辑:使用
ssp_transfer_data同步收发,确保每次传输都有时钟触发,从设备能正确响应。 - NSS时序简化:统一在读写函数中控制NSS引脚,避免重复操作,保证片选信号稳定。
内容的提问来源于stack exchange,提问作者Annakkili Arumugam
相关产品推荐
相关产品推荐

