STM32发现F3 SPI环回RXFIFO收不到数据
STM32 discovery F3 SPI loopback RXFIFO receives no data
我正在使用 STM32 F3 发现 套件并开始使用 SPI 外设。我从一个简单的环回系统开始:我检查 TXFIFOLVL 状态,如果它未满,我将我的数据发送到 DR 寄存器,这然后应该循环回到我的 RxBuffer(我从 DR 读取数据,而 RXFIFOLVL 不为空),但我遇到了一个问题——我的接收缓冲区没有得到任何回复,而且我似乎不明白为什么。我不使用 HAL 或 Standard Peripheral Library,所以我配置 SPI 并通过像这样的寄存器值:
SPI 代码的头文件:
#define GPIOA_ENABLE 0b1<<17 // Enable GPIO port A clock in AHBENR register
#define SPI1_CLOCK_ENABLE 0b1<<12 // Enable SPI1 clock in APB2ENR register
#define SPI1_PIN_ALT_FNC 0b1010<<4 // Sets PA5,PA6 & PA7 to Alternative function
#define SPI1_OUTPUT_TYPE ~(0b111<<5) // Sets PA5, PA6 & PA7 to push-pull
#define SPI1_PIN_SPEED 0b1111<<4 // Sets pins from 4 to 7 to work on 50 MHz output speed
#define SPI1_PIN_ALT_FNC_LOW 0b0101<<4 // Sets the Alternative function to AF5 in alternative function low register
#define SPI1_PIN_ALT_FNC_HIGH 0b0101<<4 // Sets the Alternative function to AF5 in alternative function high register
#define SPI1_BAUDRATE_PRESCALER_2 0b000<<3 // F_PCLK/2
#define SPI1_BAUDRATE_PRESCALER_128 0b110<<3 // F_PCLK/128
#define SPI1_MASTER_MODE 0b1<<2 // Sets the SPI1 to master mode
#define SPI1_PERI_ENABLE 0b1<<6 // Enable the SPI peripheral
#define SPI1_SSM_ENABLE 0b1<<9 // Enable SPI software slave management
#define SPI1_SSI_ENABLE 0b1<<8 // SPI1 internal slave select
#define SPI1_NSSP_ENABLE 0b1<<3 // Enable NSS pulse management
#define SPI1_FRXTH_8BIT 0b1<<12 //Set the FIFO reception threshold to 8 bits
#define SPI1_DATA_SIZE 0b0111<<8 // SPI1 DATA size
#define SPI1_TXFIFO_FULL_FLAG 0b11<<11 // SPI1 Tx FIFO transmission flag
#define SPI1_RXFIFO_EMPTY_FLAG 0b00<<9 // SPI1 Rx FIFO reception flag
#include "main.h"
#include "stm32f3xx_hal.h"
void spi_init();
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize);
SPI 代码的代码文件:
#include "SPI_toSD.h"
/* SPI1 configuration
* PA5 - SCK
* PA6 - MISO
* PA7 - MOSI
*/
void spi_init(){
// Start the GPIO and peripheral clocks in Reset and Clock Control register
RCC->AHBENR |= GPIOA_ENABLE;
RCC->APB2ENR |= SPI1_CLOCK_ENABLE;
// Configure the GPIOs for SPI communication
GPIOA->MODER |= SPI1_PIN_ALT_FNC;
GPIOA->OTYPER &= SPI1_OUTPUT_TYPE;
GPIOA->OSPEEDR |= SPI1_PIN_SPEED;
GPIOA->AFR[0] |= SPI1_PIN_ALT_FNC_LOW;
GPIOA->AFR[1] |= SPI1_PIN_ALT_FNC_HIGH;
// Configure the SPI peripheral
SPI1->CR1 |= SPI1_BAUDRATE_PRESCALER_2;
SPI1->CR1 |= SPI1_SSM_ENABLE;
SPI1->CR1 |= SPI1_MASTER_MODE;
SPI1->CR1 |= SPI1_SSI_ENABLE;
SPI1->CR2 |= SPI1_DATA_SIZE;
SPI1->CR2 |= SPI1_FRXTH_8BIT;
SPI1->CR2 |= SPI1_NSSP_ENABLE;
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
}
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize){
int i;
while((SPI1->SR & 0b11<<11)==SPI1_TXFIFO_FULL_FLAG);
for(i=0;i<bufferSize;i++){
SPI1->DR |= *txBuffer; // send *txBuffer++
txBuffer++;
while((SPI1->SR & 0b11<<9)!=SPI1_RXFIFO_EMPTY_FLAG){
*rxBuffer = SPI1->DR;
rxBuffer++;
}
}
}
在 main 中,我简单地定义了我的缓冲区并像这样初始化它们:
uint8_t rx_buff[SIZE] = {0,0,0,0,0,0,0,0,0,0};
uint8_t tx_buff[SIZE] = {1,2,3,4,5,6,7,8,9,10};
因此,在我的 spi_WriteRead() 函数被调用之后,我自然希望这些缓冲区具有相同的值。
我调用我的 spi_init() 函数,在我的 while 循环中我调用 spi_WriteRead() 函数:
spi_WriteRead(rx_buff,tx_buff,SIZE);
SIZE 在我的 main.c 中定义为:
#define SIZE 10
我使用 SW4STM32 环境进行编码和调试,因此在我的调试器中我可以看到所有寄存器值。我的 SPI 就像我定义的那样被初始化,我的数据被发送到 TXFIFO 缓冲区,但是 RXFIFO缓冲区。如果我检查 SPI SR 寄存器,我可以看到我的 TXFIFO 已满,但是 RXFIFO 标志表示它是空的。
有人知道我可能做错了什么吗?我是不是对 SPI 有一些简单的误解?感谢您的输入!
编辑:
这里好好看看:
#define SPI1_SSI_ENABLE 0b1<<8
...
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
现在您可能知道为什么 #define
宏通常被认为是一个坏主意。如果您使用 stm32f3xxx.h
header 中的 #define
值,您就不会遇到这个问题,因为所有带有操作的值都在那里有括号。你没有它们。这就是为什么您的代码对于编译器看起来像这样:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~0b1<<8;
相当于:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= (~0b1)<<8;
更进一步:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= 0xffffff00;
可能不是你想要的。
您还应该知道,如果您的设备是主设备,则应该同时设置 SSI 和 SSM 位。 https://whosebug.com/a/42169600/157344
原文:
请注意,在这些设备中,当您直接访问 SPI1->DR
时,您一次 send/receive 两个字节。那是因为这个寄存器定义为 uint16_t
并且 SPI 支持所谓的 "Data packing" (在参考手册中搜索它)。如果你想 send/receive 一次一个字节,那么你需要像这样转换寄存器进行写入和读取:
readByte = (volatile uint8_t*)SPI1->DR;
(volatile uint8_t*)SPI1->DR = writeByte;
顺便说一句 - 为什么不使用 CMSIS headers 提供的#defines?您不必定义 SPI1_MASTER_MODE
...
之类的内容
我正在使用 STM32 F3 发现 套件并开始使用 SPI 外设。我从一个简单的环回系统开始:我检查 TXFIFOLVL 状态,如果它未满,我将我的数据发送到 DR 寄存器,这然后应该循环回到我的 RxBuffer(我从 DR 读取数据,而 RXFIFOLVL 不为空),但我遇到了一个问题——我的接收缓冲区没有得到任何回复,而且我似乎不明白为什么。我不使用 HAL 或 Standard Peripheral Library,所以我配置 SPI 并通过像这样的寄存器值:
SPI 代码的头文件:
#define GPIOA_ENABLE 0b1<<17 // Enable GPIO port A clock in AHBENR register
#define SPI1_CLOCK_ENABLE 0b1<<12 // Enable SPI1 clock in APB2ENR register
#define SPI1_PIN_ALT_FNC 0b1010<<4 // Sets PA5,PA6 & PA7 to Alternative function
#define SPI1_OUTPUT_TYPE ~(0b111<<5) // Sets PA5, PA6 & PA7 to push-pull
#define SPI1_PIN_SPEED 0b1111<<4 // Sets pins from 4 to 7 to work on 50 MHz output speed
#define SPI1_PIN_ALT_FNC_LOW 0b0101<<4 // Sets the Alternative function to AF5 in alternative function low register
#define SPI1_PIN_ALT_FNC_HIGH 0b0101<<4 // Sets the Alternative function to AF5 in alternative function high register
#define SPI1_BAUDRATE_PRESCALER_2 0b000<<3 // F_PCLK/2
#define SPI1_BAUDRATE_PRESCALER_128 0b110<<3 // F_PCLK/128
#define SPI1_MASTER_MODE 0b1<<2 // Sets the SPI1 to master mode
#define SPI1_PERI_ENABLE 0b1<<6 // Enable the SPI peripheral
#define SPI1_SSM_ENABLE 0b1<<9 // Enable SPI software slave management
#define SPI1_SSI_ENABLE 0b1<<8 // SPI1 internal slave select
#define SPI1_NSSP_ENABLE 0b1<<3 // Enable NSS pulse management
#define SPI1_FRXTH_8BIT 0b1<<12 //Set the FIFO reception threshold to 8 bits
#define SPI1_DATA_SIZE 0b0111<<8 // SPI1 DATA size
#define SPI1_TXFIFO_FULL_FLAG 0b11<<11 // SPI1 Tx FIFO transmission flag
#define SPI1_RXFIFO_EMPTY_FLAG 0b00<<9 // SPI1 Rx FIFO reception flag
#include "main.h"
#include "stm32f3xx_hal.h"
void spi_init();
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize);
SPI 代码的代码文件:
#include "SPI_toSD.h"
/* SPI1 configuration
* PA5 - SCK
* PA6 - MISO
* PA7 - MOSI
*/
void spi_init(){
// Start the GPIO and peripheral clocks in Reset and Clock Control register
RCC->AHBENR |= GPIOA_ENABLE;
RCC->APB2ENR |= SPI1_CLOCK_ENABLE;
// Configure the GPIOs for SPI communication
GPIOA->MODER |= SPI1_PIN_ALT_FNC;
GPIOA->OTYPER &= SPI1_OUTPUT_TYPE;
GPIOA->OSPEEDR |= SPI1_PIN_SPEED;
GPIOA->AFR[0] |= SPI1_PIN_ALT_FNC_LOW;
GPIOA->AFR[1] |= SPI1_PIN_ALT_FNC_HIGH;
// Configure the SPI peripheral
SPI1->CR1 |= SPI1_BAUDRATE_PRESCALER_2;
SPI1->CR1 |= SPI1_SSM_ENABLE;
SPI1->CR1 |= SPI1_MASTER_MODE;
SPI1->CR1 |= SPI1_SSI_ENABLE;
SPI1->CR2 |= SPI1_DATA_SIZE;
SPI1->CR2 |= SPI1_FRXTH_8BIT;
SPI1->CR2 |= SPI1_NSSP_ENABLE;
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
}
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize){
int i;
while((SPI1->SR & 0b11<<11)==SPI1_TXFIFO_FULL_FLAG);
for(i=0;i<bufferSize;i++){
SPI1->DR |= *txBuffer; // send *txBuffer++
txBuffer++;
while((SPI1->SR & 0b11<<9)!=SPI1_RXFIFO_EMPTY_FLAG){
*rxBuffer = SPI1->DR;
rxBuffer++;
}
}
}
在 main 中,我简单地定义了我的缓冲区并像这样初始化它们:
uint8_t rx_buff[SIZE] = {0,0,0,0,0,0,0,0,0,0};
uint8_t tx_buff[SIZE] = {1,2,3,4,5,6,7,8,9,10};
因此,在我的 spi_WriteRead() 函数被调用之后,我自然希望这些缓冲区具有相同的值。
我调用我的 spi_init() 函数,在我的 while 循环中我调用 spi_WriteRead() 函数:
spi_WriteRead(rx_buff,tx_buff,SIZE);
SIZE 在我的 main.c 中定义为:
#define SIZE 10
我使用 SW4STM32 环境进行编码和调试,因此在我的调试器中我可以看到所有寄存器值。我的 SPI 就像我定义的那样被初始化,我的数据被发送到 TXFIFO 缓冲区,但是 RXFIFO缓冲区。如果我检查 SPI SR 寄存器,我可以看到我的 TXFIFO 已满,但是 RXFIFO 标志表示它是空的。
有人知道我可能做错了什么吗?我是不是对 SPI 有一些简单的误解?感谢您的输入!
编辑:
这里好好看看:
#define SPI1_SSI_ENABLE 0b1<<8
...
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
现在您可能知道为什么 #define
宏通常被认为是一个坏主意。如果您使用 stm32f3xxx.h
header 中的 #define
值,您就不会遇到这个问题,因为所有带有操作的值都在那里有括号。你没有它们。这就是为什么您的代码对于编译器看起来像这样:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~0b1<<8;
相当于:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= (~0b1)<<8;
更进一步:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= 0xffffff00;
可能不是你想要的。
您还应该知道,如果您的设备是主设备,则应该同时设置 SSI 和 SSM 位。 https://whosebug.com/a/42169600/157344
原文:
请注意,在这些设备中,当您直接访问 SPI1->DR
时,您一次 send/receive 两个字节。那是因为这个寄存器定义为 uint16_t
并且 SPI 支持所谓的 "Data packing" (在参考手册中搜索它)。如果你想 send/receive 一次一个字节,那么你需要像这样转换寄存器进行写入和读取:
readByte = (volatile uint8_t*)SPI1->DR;
(volatile uint8_t*)SPI1->DR = writeByte;
顺便说一句 - 为什么不使用 CMSIS headers 提供的#defines?您不必定义 SPI1_MASTER_MODE
...