STM32通过SPI使能ADXL345

STM32 enable ADXL345 via SPI

我正在尝试连接到 ADXL345,它在另一块板上连接了 GPIO(PA5、PA6、PA7、PA12)。另外,我正在使用 [sigrok][1] 的 PulseViewer 软件显示传感器未启用,但功能似乎正确。有什么我想念的吗?

#include "stm32l0xx.h"
#include "pmi_stddefs.h"

int32_t spi_init_adxl345(void)
{

/* enable SPI1 clock */
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/* Enable clock for port A */
//RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
RCC->IOPENR |= RCC_IOPENR_IOPAEN;

/* CPHA at 1, CPOL at 1 */
SPI1->CR1 |= SPI_CR1_CPOL;
SPI1->CR1 |= SPI_CR1_CPHA;
/* enable master mode */
SPI1->CR1 |= SPI_CR1_MSTR;
/* baud rate Maximum (5MHZ ADXL = 011) */
// SPI1->CR1 &= ~SPI_CR1_BR_0;
// SPI1->CR1 |= SPI_CR1_BR_1;
// SPI1->CR1 |= SPI_CR1_BR_2;
SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1);

/* Internal Slave Select */    
/* soft. slave management */
SPI1->CR1 |= SPI_CR1_SSM;
/* select slave */
SPI1->CR1 |= SPI_CR1_SSI;

/* Frame Format MSB */
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
/* Receive only mode enable (0 = full-duplex (Transmit and receive)) */
SPI1->CR1 &= ~SPI_CR1_RXONLY;
/* Data frame format 8 */
SPI1->CR1 &= ~SPI_CR1_DFF;

//SPI1->CR2 = SPI_CR2_SSOE | SPI_CR2_RXNEIE;
SPI1->CR2 = 0;


/* Set AF,OUTPUT modes */
/* MISO AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE6_0;
GPIOA->MODER |= GPIO_MODER_MODE6_1;
/* MOSI AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE7_0;
GPIOA->MODER |= GPIO_MODER_MODE7_1;
/* SCLK AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE5_0;
GPIOA->MODER |= GPIO_MODER_MODE5_1;

// TODO: PUPDR for PA4
// /* CS OUTPUT*/
GPIOA->MODER |= GPIO_MODER_MODE12_0;
GPIOA->MODER &= ~GPIO_MODER_MODE12_1;

/* Set I/O output speed (11=very high speed) */
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED5;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED6;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED7;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED12;

/* Alternate Function Low Register 9.3.2*/
/* Pin 5,6,7 (in AFRL) */
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7;

return RC_SUCC;
}
int32_t spi_txrx (uint8_t *buf, uint32_t size)
{
GPIOA->BSRR |= GPIO_BSRR_BR_12;

/* TXE == 1 : the buffer is empty */
while (!(SPI1->SR & SPI_SR_TXE))
{
    /* wait untill TXE is empty */
}

for (uint8_t i = 0; i < size; i++)
{
    SPI1->DR = buf[i];
}

while (!(SPI1->SR & SPI_SR_TXE))
{
    /* mandatory wait until TXE is set */
}
/* detect the end of a transfer */
while ((SPI1->SR & SPI_SR_BSY) != 0)
{
    /* 1:busy, 0:not busy */
}

/* random noise */
uint8_t temp = SPI1->DR;
temp = SPI1->SR;


/* Disable sensor */
GPIOA->BSRR |= GPIO_BSRR_BS_12;
/* Diable the SPI <-- No need (Master in full-duplex or transmit 
only mode can finish any transaction when it stops providing data
for transmission. In this case, the clock stops after the last
data transaction.*/
return RC_SUCC;
}

我在这个问题上停留了相当长的时间,但我没有找到这个问题的答案。有任何想法吗?提前致谢! [1]: https://www.sigrok.org/wiki/PulseView

  1. SPI nCS 线通常为低电平有效,因此必须在板启动期间设置 PA12。在您的代码中始终选择 ADXL345。

GPIOA->BSRR |= GPIO_BSRR_BR_12; 已重置(BR = 位重置,BS = 位设置)。此外,无需使用 |= 运算符,因为 BSRR 是只写寄存器,旨在更改端口状态而不使用读取-修改-写入序列。

  1. SPI_CR1 寄存器中的 SPE 位未设置,SPI 被禁用且不传输任何内容。 spi_txrx 会卡在第二个 SPI_SR_TXE while 循环中。

  2. 与问题无关,但仍然-spi_txrx看起来是面向FIFO的,但是STM32L0系列SPI只有移位器和DR寄存器,交换必须在字节上实现-按字节为基础,或使用 DMA。也许这段代码适用于 1 或 2 字节长的传输,如果只有最后一个字节有价值,您甚至会得到正确的 RX 结果。但总的来说,这是一种代码味道。

在我看来,您为 PA5、PA6 和 PA7 选择了错误的备用功能。请注意,GPIO_AFRL_AFSEL5 定义为 (0xFUL << GPIO_AFRL_AFSEL5_Pos),因此当您将其写入 AFR[0] 寄存器时,您选择的是备用功能 15(不可用)。但是,您要为 SPI1 选择的是替代功能 1。

你可以试试

GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL5_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL6_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL7_Pos);