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
- SPI nCS 线通常为低电平有效,因此必须在板启动期间设置 PA12。在您的代码中始终选择 ADXL345。
此 GPIOA->BSRR |= GPIO_BSRR_BR_12;
已重置(BR
= 位重置,BS
= 位设置)。此外,无需使用 |=
运算符,因为 BSRR 是只写寄存器,旨在更改端口状态而不使用读取-修改-写入序列。
SPI_CR1
寄存器中的 SPE
位未设置,SPI 被禁用且不传输任何内容。 spi_txrx
会卡在第二个 SPI_SR_TXE
while 循环中。
与问题无关,但仍然-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);
我正在尝试连接到 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
- SPI nCS 线通常为低电平有效,因此必须在板启动期间设置 PA12。在您的代码中始终选择 ADXL345。
此 GPIOA->BSRR |= GPIO_BSRR_BR_12;
已重置(BR
= 位重置,BS
= 位设置)。此外,无需使用 |=
运算符,因为 BSRR 是只写寄存器,旨在更改端口状态而不使用读取-修改-写入序列。
SPI_CR1
寄存器中的SPE
位未设置,SPI 被禁用且不传输任何内容。spi_txrx
会卡在第二个SPI_SR_TXE
while 循环中。与问题无关,但仍然-
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);