Embedded 为什么从STM32F407G-Disc1 MCU上的LIS3DSH加速计读取数据时,我只得到0xFF?

Embedded 为什么从STM32F407G-Disc1 MCU上的LIS3DSH加速计读取数据时,我只得到0xFF?,embedded,stm32,Embedded,Stm32,所以我正在学习嵌入式开发,最近我学习了SPI的基础知识。作为一个项目,我希望仅使用CMSIS头与STM32F407G-DISC1板上的LIS3DSH加速计通信 我在下面粘贴了整个代码,但我将首先解释它,因为没有人想阅读所有这些代码 作为参考,以下是通过SPI进行通信所需的引脚(根据MCU的数据表): PA5-SPI1_SCK PA7-SPI1_MOSI PA6-SPI1_味噌 PE3-CS_I2C/SPI 以下是我在代码中采取的步骤: 使用AHB1ENR寄存器为GPIOA和GPIOE启用时钟

所以我正在学习嵌入式开发,最近我学习了SPI的基础知识。作为一个项目,我希望仅使用CMSIS头与STM32F407G-DISC1板上的LIS3DSH加速计通信

我在下面粘贴了整个代码,但我将首先解释它,因为没有人想阅读所有这些代码

作为参考,以下是通过SPI进行通信所需的引脚(根据MCU的数据表):

  • PA5-SPI1_SCK
  • PA7-SPI1_MOSI
  • PA6-SPI1_味噌
  • PE3-CS_I2C/SPI
以下是我在代码中采取的步骤:

  • 使用AHB1ENR寄存器为GPIOA和GPIOE启用时钟
  • 对于GPIOA,我将三个引脚设置为备用功能,输出为推拉,速度低,无上拉/下拉,并将备用功能配置为SPI
  • 对于GPIOE,将其设置为GPIO模式、推拉、低速、上拉,然后将其设置为高(如写入BSSR寄存器)
  • 使用APB2ENR寄存器为SPI启用时钟
  • 配置SPI1:首先禁用它,启用2线单向模式,将波特率设置为fPCL/16,因为APB2外围时钟为84MHz,加速计的最大时钟为10MHz。然后将时钟相位和极性设置为1。8位数据帧,MSB优先,启用了软件从机管理,还启用了主配置。最后,启用了SPI1
  • 在所有这些之后,我将0x63传输到加速度计的0x20寄存器。这将输出速率设置为100Hz,并启用x轴和y轴。我不知道这是否真的有效。我假设这是因为我检查SPI状态寄存器时TX缓冲区是空的
  • 然后,为了测试我是否可以接收,我尝试从加速计的WHO_AM_I寄存器获取数据。但我只在调试垃圾数据时看到垃圾数据(0xFF) 我在谷歌上搜索了一下原因,很多人认为时钟的极性和相位可能不正确。但是,我已经检查了多次,并且我相当确定我正确地配置了它

    我试过设置中断。在中断期间,即使RXNE(RX缓冲区不为空)为真,它仍然只读取0xFF。我不明白为什么会发生这种事

    代码如下。起点是加速计_init()。从WHO\u AM\u I寄存器读取的数据位于
    打开加速计()

    #包括
    #包括
    #包括“stm32f4xx.h”
    #包括“加速度计.h”
    静态无效gpio_时钟_启用(无效);
    静态无效gpio_a_init(无效);
    静态void gpio_e_init(void);
    静态无效加速计\时钟\启用(无效);
    静态空隙配置加速计(空隙);
    静态空隙拉力高(空隙);
    静态空隙拉力低(空隙);
    静态无效打开加速计(无效);
    静态无效等待直到传输完成(无效);
    仅静态无效传输(uint8地址、uint8数据);
    静态无效接收虚拟数据(无效);
    无效加速计_init(无效){
    gpio_时钟_启用();
    gpio_a_init();
    gpio_e_init();
    加速计时钟启用();
    配置_加速计();
    打开加速计();
    }
    无效gpio_时钟_启用(无效){
    RCC_TypeDef*RCC=RCC;
    rcc->AHB1ENR |=(1 SR>>7)和1;
    易失性bool为传输缓冲区空=(spi\u 1->SR>>1)&1;
    如果(!正在忙&正在传输缓冲区为空){
    打破
    }
    }
    }
    无效接收\u伪\u数据(无效){
    SPI_类型定义*SPI_1=SPI1;
    spi_1->DR;
    spi_1->SR;
    }
    
    您使用SPI的方式不正确

    这辆公共汽车是这样工作的:

    • 主控单元(MCU)在MOSI中发送字节
    • 行在同一时间从(LIS)发送MISO行中的字节。在这一刻,奴隶不知道,到底是什么字节主传输给它
    要传输一个字节,您应该:

    • 在数据寄存器中写入字节
    • 等待转移完成
    • 读取数据寄存器
    因此,要读取WHO_AM_I register,我们获得下一个序列:

    • 初始化SPI
    • 刷新数据寄存器(仅读取SPI->DR)
    • 发送命令
    • 等等
    • 读取虚拟数据(您的0xFF)
    • 写入第二个字节(0x00或0xFF,这无关紧要)
    • 等等
    • 阅读LIS的正确答案

    使用逻辑分析仪或示波器验证信号是否按预期工作。CS是否正在走低?SCK正在切换吗?MOSI/MISO的数据是否符合您的预期?尝试时钟极性和相位的其他三种组合不会有什么坏处。我同意@kkrambo。最好是用示波器探测导线。如果您只接收<代码> 0xFF,那么在我看来,加速度计没有响应,因为这可能只是线路的默认状态(高数据空闲或拖动)。实际上,如果没有范围,甚至无法开始考虑这样的应用程序。它是所有嵌入式软件开发的必备工具。任何特定“速度”设置下任何GPIO的最大频率取决于您的电源电压和负载电容。“低速”设置在您正在计时的频率(5.25MHz?)下充其量是边际的。您应该至少使用“中等”速度模式。这是检验信号完整性和定时所必需的范围。如果线路太“慢”,则时钟信号可能因转换率过低而无效。。。。或者进一步划分PCLK-您不需要非常高的速率来及时获取加速度计数据。对于100sps的两个16位寄存器,10KHz的速度已经足够快了。
    #include <stdint.h>
    #include <stdbool.h>
    #include "stm32f4xx.h"
    #include "accelerometer.h"
    
    static void gpio_clock_enable(void);
    static void gpio_a_init(void);
    static void gpio_e_init(void);
    static void accelerometer_clock_enable(void);
    static void configure_accelerometer(void);
    static void pull_slave_high(void);
    static void pull_slave_low(void);
    static void turn_on_accelerometer(void);
    static void wait_till_transmit_complete(void);
    static void transmit_only(uint8_t address, uint8_t data);
    static void receive_dummy_data(void);
    
    void accelerometer_init(void) {
        gpio_clock_enable();
        gpio_a_init();
        gpio_e_init();
    
        accelerometer_clock_enable();
        configure_accelerometer();
        turn_on_accelerometer();
    }
    
    void gpio_clock_enable(void) {
        RCC_TypeDef *rcc = RCC;
        rcc->AHB1ENR |= (1 << 0) | (1 << 4);
    }
    
    void gpio_a_init(void) {
        GPIO_TypeDef *gpio_a = GPIOA;
    
        // Reset mode and set as alternate function
        gpio_a->MODER &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
        gpio_a->MODER |= (0x2 << 10) | (0x2 << 12) | (0x2 << 14);
    
        // Set output to PP
        gpio_a->OTYPER &= ~(1 << 5) & ~(1 << 6) & ~(1 << 7);
    
        // Set speed to low
        gpio_a->OSPEEDR &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
    
        // Set to no pull-up / pull-down
        gpio_a->PUPDR &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
    
        // Reset alternate function and set to SPI
        gpio_a->AFR[0] &= ~(0xF << 20) & ~(0xF << 24) & ~(0xF << 28);
        gpio_a->AFR[0] |= (0x5 << 20) | (0x5 << 24) | (0x5 << 28);
    }
    
    void gpio_e_init(void) {
        GPIO_TypeDef *gpio_e = GPIOE;
    
        // Set as general purpose output mode
        gpio_e->MODER &= ~(0x3 << 6);
        gpio_e->MODER |= (1 << 6);
    
        // Set as push pull
        gpio_e->OTYPER &= ~(1 << 3);
    
        // Set as low speed
        gpio_e->OSPEEDR &= ~(0x3 << 6);
    
        // Set to pull up
        gpio_e->PUPDR &= ~(0x3 << 6);
        gpio_e->PUPDR |= (1 << 6);
    
        // Set it high
        pull_slave_high();
    }
    
    void accelerometer_clock_enable(void) {
        RCC_TypeDef *rcc = RCC;
        rcc->APB2ENR |= (1 << 12);
    }
    
    void configure_accelerometer(void) {
        SPI_TypeDef *spi_1 = SPI1;
    
        // First disable it while we configure SPI
        spi_1->CR1 &= ~(1 << 6);
    
        // 2-line unidirectional data mode enabled
        spi_1->CR1 &= ~(1 << 15);
    
        // Reset baud rate and set to fPCLK/16
        // because APB2 peripheral clock currently is 84 MHz
        // and the max clock of the accelerometer is 10 MHz.
        spi_1->CR1 &= ~(0x7 << 3);
        spi_1->CR1 |= (0x3 << 3);
    
        // Set clock phase to 1
        spi_1->CR1 |= (1 << 0);
    
        // Set clock polarity to 1
        spi_1->CR1 |= (1 << 1);
    
        // 8 bit data frame format
        spi_1->CR1 &= ~(1 << 11);
    
        // MSB first
        spi_1->CR1 &= ~(1 << 7);
    
        // Software slave management enabled
        spi_1->CR1 |= (1 << 9);
        spi_1->CR1 |= (1 << 8);
    
        // Master configuration enabled
        spi_1->CR1 |= (1 << 2);
    
        // SS output enabled
    //    spi_1->CR2 |= (1 << 2);
    
        // Enable SPI
        spi_1->CR1 |= (1 << 6);
    
        // Wait a little bit for accelerometer to turn on
        for (int i=0; i<1000000; i++);
    }
    
    void pull_slave_high(void) {
        // Wait until SPI is no longer busy
        SPI_TypeDef *spi_1 = SPI1;
        while ((spi_1->SR >> 7) & 1);
    
        GPIO_TypeDef *gpio_e = GPIOE;
        gpio_e->BSRR |= (1 << 19);
    }
    
    void pull_slave_low(void) {
        // Wait until SPI is no longer busy
        SPI_TypeDef *spi_1 = SPI1;
        while ((spi_1->SR >> 7) & 1);
    
        GPIO_TypeDef *gpio_e = GPIOE;
        gpio_e->BSRR |= (1 << 3);
    }
    
    void turn_on_accelerometer(void) {
        // Set output data rate to 100Hz
        // and enable X-axis, Y-axis.
        transmit_only(0x20, 0x63);
        receive_dummy_data();
    
        // Temp test checking the WHO_AM_I register on the accelerometer.
        SPI_TypeDef *spi_1 = SPI1;
        pull_slave_low();
        wait_till_transmit_complete();
        uint8_t address = 0x0F | 0x80;
        spi_1->DR = address;
        wait_till_transmit_complete();
    
        while (true) {
            volatile bool is_busy = (spi_1->SR >> 7) & 1;
            volatile bool is_rx_buffer_not_empty = (spi_1->SR >> 0) & 1;
    
            if (!is_busy && is_rx_buffer_not_empty) {
                break;
            }
        }
        volatile uint32_t data = spi_1->DR;
        pull_slave_high();
    }
    
    /*
     * Transmit is synchronous.
     */
    void transmit_only(uint8_t address, uint8_t data) {
        SPI_TypeDef *spi_1 = SPI1;
    
        // Select the accelerometer as the slave
        pull_slave_low();
    
        // Wait till transmit buffer is ready
        wait_till_transmit_complete();
    
        spi_1->DR = address;
    
        // Wait till transmit buffer is ready
        wait_till_transmit_complete();
    
        spi_1->DR = data;
    
        // Wait till transmit buffer has been read
        wait_till_transmit_complete();
    
        // Deselect the slave
        pull_slave_high();
    }
    
    void wait_till_transmit_complete(void) {
        SPI_TypeDef *spi_1 = SPI1;
    
        while (true) {
            volatile bool is_busy = (spi_1->SR >> 7) & 1;
            volatile bool is_transmit_buffer_empty = (spi_1->SR >> 1) & 1;
    
            if (!is_busy && is_transmit_buffer_empty) {
                break;
            }
        }
    }
    
    void receive_dummy_data(void) {
        SPI_TypeDef *spi_1 = SPI1;
        spi_1->DR;
        spi_1->SR;
    }