STM32 SPI数据打包

Posted

技术标签:

【中文标题】STM32 SPI数据打包【英文标题】:STM32 SPI Data Packing 【发布时间】:2022-01-02 03:04:56 【问题描述】:

我无法在我的 STM32f3 发现板上获取 SPI(Datasheet) 与陀螺仪传感器一起使用(I3G4250D) 在寄存器级别。我知道我正在发送数据,因为我处于全双工状态并接收假人)来自传感器的字节使用 16 位数据打包,但是当我尝试使用 8 位访问 DR 寄存器接收时,我从传感器得到不一致的值,有时返回一个字节 0xff,有时返回 2 个字节 0xffff(至少我认为这是正在发生的事情)但是我想读取的传感器寄存器中没有实际值。我认为这与我芯片上的 STM32 SPI 自动打包有关,但我认为我正在通过使用 uint8_t* 访问 DR 寄存器来解决这个问题,但它似乎不起作用.我还想问,当我比较传感器上的SPI协议(数据表第24页)和STM32数据表(第729页)时,我推断STM32 SPI中的CPOL(时钟极性)和CPHA(时钟相位)位都应该设置,但是我似乎至少能够在设置或不设置这些位的情况下发送数据......

这是我的 SPI 初始化函数,其中包括尝试在其末尾读取字节并将字节写入传感器寄存器函数:

void SPI_Init() 

/* Peripheral Clock Enable */
RCC->AHBENR |= RCC_AHBENR_GPIOEEN|RCC_AHBENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

/* GPIO Configuration */
GPIOA->MODER |= GPIO_MODER_MODER5_1|GPIO_MODER_MODER6_1|GPIO_MODER_MODER7_1;                            //Alternate function
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5|GPIO_OSPEEDER_OSPEEDR6|GPIO_OSPEEDER_OSPEEDR7;     //High speed
GPIOA->AFR[0] |= 0x00500000|0x05000000|0x50000000;                                                                              //AF for SCK,MISO,MOSI

GPIOE->MODER |= GPIO_MODER_MODER3_0;                                                                                                            //Port E for NSS Pin
GPIOE->MODER |= GPIO_MODER_MODER3_0;

/* SPI Configuration */
SPI1->CR2 |= SPI_CR2_FRXTH|SPI_CR2_RXDMAEN;     //Enable DMA but DMA is not used
// not sure if I need this?|SPI_CR1_CPOL|SPI_CR1_CPHA; 
SPI1->CR1 |= SPI_CR1_BR_1|SPI_CR1_SSM|SPI_CR1_SSI|SPI_CR1_MSTR|SPI_CR1_SPE;   //big endian, SPI@6MH, since using software set SPI_CR1_SSI to high for master mode


/* Slave Device Initialization */
SPI_WriteByte(CTRL_REG1_G,0x9f);
SPI_WriteByte(CTRL_REG4_G,0x10);
SPI_WriteByte(CTRL_REG5_G,0x10);

//receive test
uint8_t test =0xff;
uint8_t* spiDrPtr = (__IO uint8_t*)&SPI1->DR;
*spiDrPtr = 0x80|CTRL_REG1_G;
while(!(SPI1->SR & SPI_SR_TXE))
//SPI1->CR2 &= ~(SPI_CR2_FRXTH); //this is done in HAL not sure why though
*spiDrPtr = test;                                                   //Send dummy
while(!(SPI1->SR & SPI_SR_RXNE))
test = *spiDrPtr;


static void SPI_WriteByte(uint8_t regAdd, uint8_t data) 
  uint8_t arr[2] = regAdd,data; //16 bit data packing
  SPI1->DR = *((uint16_t*)arr);

有什么建议吗?

【问题讨论】:

如果所有传输都是 16 位长,为什么还要使用 8 位打包而不只是使用完整的 16 位数据呢?设置FRXTH = 0,像SPI_WriteByte函数一样发送两个字节,然后从DR读取16位字并丢弃第一个字节。 好吧,原来我没有设置/重置片选线。我认为这不会有什么不同,因为我的 SPI1 总线上只有一个从机。数据表提到只有 1 个从设备时不需要管理 CS 线,但在同一段落中说无论如何都可能有用。我想它毕竟对我有用! 【参考方案1】:

尝试使用 FRXTH = 0,并以 16 位字执行所有 DR 读取和写入,然后丢弃第一个字节。

对于后面的问题,关于 CPOL/CPHA。这些位在位级别控制 SPI 传输格式。参考下图(来自wikipedia)。

CPOL = 0,CPHA = 0也称为“SPI模式0”,数据位在SCK上升沿采样

CPOL = 1,CPHA = 1也称为“SPI模式3”,数据位也在上升沿采样。

两种模式的区别在于传输之间的 SCK 电平,以及第一位之前的额外下降沿。

一些芯片明确声明支持模式 0 和模式 3。然而在 I3G4250D 数据表中,第 5.2 节:“SDI 和 SDO 是, 分别是串口数据的输入和输出。这些线在下降沿驱动 SPC 并且应该在 SPC 的上升沿被捕获。"

当数据以模式0从mcu发送到芯片时,mcu在第一个上升沿之前驱动MOSI线。因此,从芯片可以在模式 0 和模式 3 下接收有效数据。但是当数据从芯片传输到单片机时,芯片可能需要第一个 SCK 下降沿来移位/锁存第一个数据位到在 MISO 线和模式 0 下,您将收到移动一位的读数。

我强调了“可能”这个词,因为芯片仍然可以在两种模式下正确工作,在 nSS 下降沿锁存第一位,制造商只是没有进行测试,或者不保证它'将适用于其他版本和所有条件。

【讨论】:

感谢 yoCPOL 和 CPHA 现在有意义【参考方案2】:
    如果不使用 DMA,请不要启用它。 您需要强制 16 位访问(不是 32 位)
static void SPI_WriteByte(uint8_t regAdd, uint8_t data) 
  uint8_t arr[2] = regAdd,data; //16 bit data packing
  *(volatile uint16_t *)&SPI1->DR = *((volatile uint16_t*)arr);

【讨论】:

您确定右侧需要 volatile 吗? @TomV 它们被定义为volatile& 剥离 volatile 所以它被添加到演员表中 感谢您的回复,尽管这似乎对我也不起作用。如果我可以让轮询工作,我稍后需要使用 DMA(DMA 时钟、寄存器等当前未启用。我确实按照你的建议禁用了它)。我也一直在通过 HAL(这对我工作)同时执行此操作,并且 HAL 不会转换 DR 寄存器而不是传输它是:hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);跨度>

以上是关于STM32 SPI数据打包的主要内容,如果未能解决你的问题,请参考以下文章

STM32单片机SPI问题

stm32的spi读写只能在一个函数里完成吗?

STM32 HAL SPI 16 位发送

STM32F0 SPI 环回 - MISO 上没有数据

STM32学习笔记(15)——SPI协议

请教STM32的 SPI 同时中断收发问题