C++ 为什么在Raspberry Pi(主)和Nucleo l432kc板(从)之间的SPI通信中接收移位和重复数据
我在ST Q&A论坛和Raspberry Pi论坛上问过同样的问题,但我没有得到任何回答。我希望这里有人能帮助我 我想让两个核子l432kc(从机)通过SPI协议与树莓Pi(主机)通信。在Raspberry上,我使用的是Spidev API,而在Nucleo上,我使用的是带有DMA的HAL SPI接口 允许稳定传输的唯一配置是Spidev的SPI_模式_2,对应于HAL的SPI_极性_高和SPI_相位_2边缘配置 对于上述配置,我有两个问题:C++ 为什么在Raspberry Pi(主)和Nucleo l432kc板(从)之间的SPI通信中接收移位和重复数据,c++,raspberry-pi,stm32,spi,mbed,C++,Raspberry Pi,Stm32,Spi,Mbed,我在ST Q&A论坛和Raspberry Pi论坛上问过同样的问题,但我没有得到任何回答。我希望这里有人能帮助我 我想让两个核子l432kc(从机)通过SPI协议与树莓Pi(主机)通信。在Raspberry上,我使用的是Spidev API,而在Nucleo上,我使用的是带有DMA的HAL SPI接口 允许稳定传输的唯一配置是Spidev的SPI_模式_2,对应于HAL的SPI_极性_高和SPI_相位_2边缘配置 对于上述配置,我有两个问题: 主设备发送给从设备的消息始终正确到达,但从设备发送
0b00000011 0b00000001
我接收0b00000001 0b10000000
)。看起来MISO信号的采样延迟了,但我不知道为什么// SPI
__HAL_RCC_SPI1_CLK_ENABLE();
/* SPI1 parameter configuration*/
hspi.Instance = SPI1;
hspi.Init.Mode = SPI_MODE_SLAVE;
hspi.Init.Direction = SPI_DIRECTION_2LINES; // full duplex mode
hspi.Init.DataSize = SPI_DATASIZE_8BIT; // dimension of 1 byte
hspi.Init.CLKPolarity = SPI_POLARITY_HIGH; // start and idle clk value
hspi.Init.CLKPhase = SPI_PHASE_2EDGE; // edge of sampling of data both on miso and mosi
hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; // bit order
hspi.Init.TIMode = SPI_TIMODE_DISABLE; // disabling the TI mode
hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // disable crc calc
hspi.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; // disable NSS puls value
if (HAL_SPI_Init(&hspi) != HAL_OK)
return false;
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
// GPIO
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA1 ------> SPI1_SCK
PA11 ------> SPI1_MISO
PA12 ------> SPI1_MOSI
PB0 ------> SPI1_NSS
*/
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// DMA
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi_rx.Instance = DMA1_Channel2;
hdma_spi_rx.Init.Request = DMA_REQUEST_1;
hdma_spi_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi_rx.Init.Mode = DMA_NORMAL;
hdma_spi_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_spi_rx) != HAL_OK) return false;
__HAL_LINKDMA(&hspi,hdmarx,hdma_spi_rx);
/* DMA interrupt init */
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
/* SPI1 DMA Init */
/* SPI1_TX Init */
hdma_spi_tx.Instance = DMA1_Channel3;
hdma_spi_tx.Init.Request = DMA_REQUEST_1;
hdma_spi_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi_tx.Init.Mode = DMA_NORMAL;
hdma_spi_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_spi_tx) != HAL_OK) return false;
__HAL_LINKDMA(&hspi,hdmatx,hdma_spi_tx);
/* DMA interrupt init */
/* DMA1_Channel3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
这是在主机上运行的代码:
unsigned int bitsPerByte = 8u;
unsigned int delay = 0u;
unsigned int speed = 100; // hz
unsigned int cs_change = 0u; // false in C
// initialization
fd = ::open("/dev/spidev0.0", O_RDWR);
auto mode = SPI_MODE_2; // clock polarity low, clock phase second edge
if (fd == -1)
throw std::runtime_error("Can't open the spi device");
if (::ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1)
throw std::runtime_error("Can't set the spi mode");
/*
* bits per word
*/
auto bits = bitsPerByte;
auto ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
throw std::runtime_error("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
throw std::runtime_error("can't get bits per word");
/*
* max speed hz
*/
auto maxSpeed = speed;
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &maxSpeed);
if (ret == -1)
throw std::runtime_error("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &maxSpeed);
if (ret == -1)
throw std::runtime_error("can't get max speed hz");
// code used for sending messages
void transfer(uint8_t *tx, uint8_t *rx, size_t size) {
struct spi_ioc_transfer tr = {
(unsigned long)tx, // .tx_buf
(unsigned long)rx, // .rx_buf
size, // .len
speed, // .speed_hz
delay, // .delay_usecs
bitsPerByte, // .bits_per_word
cs_change, // .cs_change
0, // .tx_nbits
0, // .rx_nbits
0, // .pad
};
if (::ioctl(fd, SPI_IOC_MESSAGE(1), &tr) == -1)
throw std::runtime_error("Can't send data throught the spi");
}
在从机的主要功能中,我实际做的唯一一件事就是发回我收到的确切数据包
编辑
在覆盆子论坛上,有人告诉我使用piscope查看发送到PIN上的数字值。我已经这样做了,我看到CE0和CE1引脚总是很低。我不明白为什么味噌上没有碰撞
编辑2
我已经解决了第二个问题(重复数据),这是主配置上的一个错误,我使用的是cs_change=1
,但它必须等于0
随着我发现从机正确发送数据,是主机没有很好地读取数据 断开为1是典型的错误时钟边缘,或者SCK的空闲状态在激活和非激活SPI之间不同(如果激活SPI,将产生时钟滴答声。您需要为SCK使用上拉/下拉电阻器)。好的,我应该在从机或主机上使用下拉菜单?通常的方法是靠近主机,如果其他线路也有下拉菜单(这是个好主意),则将它们放在一起。但这并不重要。您只需要定义一个值。如果您愿意,您甚至可以将它们放在两端。我尝试过上拉和下拉(先上拉然后下拉),没有任何变化。关闭一个是错误时钟边缘的典型情况,或者SCK的空闲状态在活动和非活动SPI之间是不同的(如果你激活SPI,会产生时钟滴答声。你需要为SCK使用上拉/下拉电阻)。好的,我应该在从机或主机上使用下拉电阻?通常的方法是靠近主机,如果你对其他线路也有下拉电阻(这是个好主意)把它们放在一起。但这并不重要。你只想有一个定义的值。如果你愿意,你甚至可以把它们放在两端。我尝试过上拉和下拉(先上拉然后下拉),没有任何变化。