You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修正点说明

  1. SPI初始化优化:将SPI初始化移到系统启动阶段,仅执行一次,避免硬件反复重置。
  2. SPI模式匹配:手动配置SSP为SPI模式0,完全匹配ADXL345的默认通信时序。
  3. 地址位修正:
    • 写操作时清除地址最高位,明确标记为写操作;
    • 读操作时同时设置读标志位(最高位)和多字节位,符合ADXL345协议要求。
  4. 全双工收发逻辑:使用ssp_transfer_data同步收发,确保每次传输都有时钟触发,从设备能正确响应。
  5. NSS时序简化:统一在读写函数中控制NSS引脚,避免重复操作,保证片选信号稳定。

内容的提问来源于stack exchange,提问作者Annakkili Arumugam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:40:59