STM32和ADXL345之间的SPI通信

Posted

技术标签:

【中文标题】STM32和ADXL345之间的SPI通信【英文标题】:SPI Communication between STM32 and ADXL345 【发布时间】:2021-12-11 06:12:05 【问题描述】:

我正在尝试使用 SPI 通信从 ADXL345 加速度计读取数据。我在主模式下配置了不同的引脚和 SPI,并尝试读取 x、y 和 z 轴加速度。

我的问题是 SPI 读数始终为 0。我尝试调试以找到问题,我意识到即使我正在传输数据并且我不明白为什么,RXNE 也从未设置。

我正在使用 STM32F103 开发板。

这是我的代码:

#include "Driver_GPIO.h"

#include "stm32f10x.h"

uint8_t RxData[6];
int x,y,z;
float   x_acc,y_acc,z_acc;

void GPIO_Config (void)


    
    MyGPIO_Struct_TypeDef NSS=GPIOA,4,Out_OD; // Output Open Drain
    MyGPIO_Struct_TypeDef SCK=GPIOA,5,AltOut_Ppull; // Alternate Output Push-Pull
    MyGPIO_Struct_TypeDef MISO=GPIOA,6,In_Floating; // Input Floating
    MyGPIO_Struct_TypeDef MOSI=GPIOA,7,AltOut_Ppull; // Alternate Output Push-Pull

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable GPIOA clk
    
    MyGPIO_Init(&NSS);
    MyGPIO_Init(&SCK);
    MyGPIO_Init(&MISO);
    MyGPIO_Init(&MOSI);

    

void SPI_Enable (void)

    SPI1->CR1 |= (SPI_CR1_SPE);   // SPE=1, Peripheral enabled


void SPI_Disable (void)

    SPI1->CR1 &= ~(SPI_CR1_SPE);   // SPE=0, Peripheral Disabled


void CS_Enable (void)

    GPIOA->BSRR |= GPIO_BSRR_BR9;


void CS_Disable (void)

    GPIOA->BSRR |= GPIO_BSRR_BS9;


void SPI_Config(void)
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;  // Enable SPI1 CLock
    
  SPI1->CR1 |= SPI_CR1_CPOL| SPI_CR1_CPHA;   // CPOL=1, CPHA=1
    
  SPI1->CR1 |= SPI_CR1_MSTR;  // Master Mode
    
  SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1);  // BR[2:0] = 400: fPCLK/16, PCLK2 = 72MHz, SPI clk = 3.375MHz
    
  SPI1->CR1 &= ~SPI_CR1_LSBFIRST;  // LSBFIRST = 0, MSB first
    
  SPI1->CR1 |= (SPI_CR1_SSM) | (SPI_CR1_SSI);  // SSM=1, SSI=1 -> Software Slave Management
    
  SPI1->CR1 &= ~SPI_CR1_RXONLY;  // RXONLY = 0, full-duplex
    
  SPI1->CR1 &= ~SPI_CR1_DFF;  // DFF=0, 8 bit data
    
  SPI1->CR2 = 0;



void SPI_Transmission(uint8_t *data, int size)
    uint8_t clear;
    //check flag TxE //
    int i=0;
    while (i<size)
    
        while (!((SPI1->SR)&(SPI_SR_TXE)));  // buffer is empty
        *(volatile uint8_t *)&SPI1->DR = data[i];
        i++;
    
    
    while (!((SPI1->SR)&(SPI_SR_TXE)));  // buffer is empty

    while (((SPI1->SR)&(SPI_SR_BSY))); // buffer not communicating

    
    clear= SPI1->DR; // empty Overrun flag
    clear= SPI1->SR;


void SPI_Receive (uint8_t *data,int size)

    while (size)
    
        while (((SPI1->SR)&(SPI_SR_BSY))) ;  // buffer not communicating
        *(volatile uint8_t *)&SPI1->DR = 0;  // dummy data
        while (!((SPI1->SR) &(SPI_SR_RXNE)));
        // buffer is not empty
        *data++= *(volatile uint8_t *)&SPI1->DR;
        size--;
    


void adxl345_write (uint8_t address, uint8_t value)

    uint8_t data[2];
    data[0] = address|0x40;  // multibyte write
    data[1] = value;
    CS_Enable ();  // pull the cs pin low
    SPI_Transmission (data,2);  // write data to register
    CS_Disable ();  // pull the cs pin high

    

void adxl345_read (uint8_t address, uint8_t *RxData)

    address |= 0x80;  // read operation
    address |= 0x40;  // multibyte read
    CS_Enable ();  // pull the pin low
    SPI_Transmission (&address,1);  // send address
    SPI_Receive (RxData,6);  // receive 6 bytes data
    CS_Disable ();;  // pull the pin high


void adxl345_init (void)

    adxl345_write (0x31, 0x01);  // data_format range= +- 4g
    adxl345_write (0x2d, 0x00);  // reset all bits
    adxl345_write (0x2d, 0x08);  // power_cntl measure and wake up 8hz

int main(void)
    
 
    GPIO_Config();
    SPI_Config();
    SPI_Enable();
    adxl345_init();

    do
        adxl345_read(0x32,RxData);
        x = ((RxData[1]<<8)|RxData[0]); // DATA X0, X1
        y = ((RxData[3]<<8)|RxData[2]); // DATA Y0, Y1
        z = ((RxData[5]<<8)|RxData[4]); // DATA Z0, Z1
        
        // Scale Factor for Xout, Yout and Zout is 7.8 mg/LSB for a +-4g, 10-bit resolution
        // ==> multiply by 0.0078 to get real acceleration values
        
        x_acc= x * 0.0078; 
        y_acc= y * 0.0078;
        z_acc= z * 0.0078;
        
    while(1);
    



【问题讨论】:

请对所有代码使用相同的格式规则,不要混合不同的编码风格。 Driver_GPIO.h 里面是什么?当您在传输数据时测量任何使用过的 SPI 线路时,​​您是否使用示波器看到了什么? Driver_GPIO.h 仅用于配置我的 4 个引脚(SCK、MOSI、MISO、NSS)。我也没有示波器所以没试过。 您不应该检查 TXE=1 标志是否已准备好传输,而不仅仅是 BUSY 标志吗?顺便说一句。您实际上可以先尝试读取 DEVID,以检查您的 SPI 通信吗?然后检查芯片的设置。 @kesselhaus 这很有道理,我会补充一点。同样通过 DEVID,您的意思是读取 0x00 地址吗? (我是一个初学者,所以我很抱歉)。如果是,我确实尝试过,但什么也读不出来(又是 0..)。 *(volatile uint8_t *)&amp;SPI1-&gt;DR 非常可疑。为什么选演员?隐藏错误不会让它们消失。 【参考方案1】:

如前所述,这里有很多问题

    为什么 NSS 引脚配置为漏极开路?通常 CS 线是推挽式的。我不知道原理图,但这是我第一次看到开漏 CS 在GPIO_Config NSS 中是针脚 4,但针脚 9 是从 CS_Enable 切换的 如果 F1 系列有单独的时钟位用于备用功能,则未启用

您说 RXNE 始终为零并且读数返回零也很奇怪。如果 RXNE 保持为零,您的代码应该停留在 while 循环中

【讨论】:

我完全同意,可能是第一项,因为我在 ADXL345 原理图上看到了上拉电阻。 @mryldz OD 带上拉电阻会导致缓慢的上升沿。如果交换很少见也没关系。但是,如果交换频繁且没有停顿 - CS 可能不会在传输之间上升到逻辑一级,这反过来可能会中断后续传输。你需要一台示波器才能确定 @Flexz 由于 NSS 是在软件模式下配置的,RM 说“未使用。可以用作 GPIO”,所以我真的不知道该怎么做。每当我写入数据时,我仍然需要将 cs 引脚拉低/拉高。我的代码也卡在第一个 RXNE while 循环中,因为它始终为 0。 @zalix,那么哪个引脚连接到 ADXL345 的 nCS 输入?如果是PA9,那么它在哪里配置?它必须是推挽式gpio输出。默认情况下,所有引脚都是输入。如果 PA4 不用于 SPI 通信 - 确认无关紧要【参考方案2】:

我不会分析幻数,因为我没有时间检查 RM 中的每个数字。但是你有很多明显的问题。

    启用外设时钟后需要延迟或回读。您立即设置了错误的寄存器。添加__DSB(); 或回读(例如(void)RCC-&gt;APB2ENR;)。所有外围设备都相同

【讨论】:

嗯 .. 在这样的评论之前最好阅读文档 .. The peripheral registers have to be accessed by half-words (16 bits) or words (32 bits). 所以,即使DR 寄存器也应该以 16 位而不是 8 位访问。 @kesselhaus 你说得对 - 我已经很多年没有使用 f1 了。较新的有先进先出,这种铸造是必要的

以上是关于STM32和ADXL345之间的SPI通信的主要内容,如果未能解决你的问题,请参考以下文章

使用ADXL345进行地震检测的加速度计

RP2040 和 adxl357 加速度计之间的 I2C 通信(C/C++ SDK)

STM32F103(二十六)SPI通信(+两块STM32之间的SPI通信)

STM32F103(二十六)SPI通信(+两块STM32之间的SPI通信)

STM32F103(二十六)SPI通信(+两块STM32之间的SPI通信)

STM32F103(二十六)SPI通信(+两块STM32之间的SPI通信)