为什么在树莓派(主机)和核l432kc板(从机)之间的SPI通信中接收移位和重复的数据

时间:2019-06-03 12:47:57

标签: c++ raspberry-pi stm32 spi mbed

我在st问答论坛和raspberry pi论坛上都问了同样的问题,但没有得到任何答案。我希望这里有人可以帮助我。

我想通过SPI协议与树莓pi(主机)通信两个核l432kc(从机)。在树莓上,我正在使用spidev API,而在核子上,我正在使用具有DMA的HAL SPI接口。

唯一允许稳定传输的配置是spidev的SPI_MODE_2,它对应于HAL的SPI_POLARITY_HIGH和SPI_PHASE_2EDGE配置。

使用上述配置,我有两个问题:

  1. 主服务器发送给从服务器的消息始终正确到达,但从服务器发送给主服务器的所有消息始终向右移位1位(例如,如果我发送两个字节0b00000011 0b00000001我收到0b00000001 0b10000000) 。看来MISO信号的采样被延迟了,但我不知道为什么。
  2. 无论芯片选择是否设置,我都在两个从机上接收数据。在从站的响应中,我没有冲突,因此我认为,当NSS较高时,只有MISO总线在从站中有效地设置为高阻抗模式。

为了测试目的,我尝试使用mbed框架的SPISlave类, 该类不能使用DMA,所以我不能在实际程序中使用它。使用它,我可以解决第一个问题,但第二个问题仍然存在。

另一件事可能有用的是,使用SPISlave类,我可以使用SPI协议的所有4种模式,当然,从机和主机必须使用相同的模式,但不能导入哪个模式。 正如我之前在HAL接口上所说的,我只能使用SPI_MODE_2。

这是在两个从站上运行的配置代码:

// 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来查看通过销钉发送的数字值。我已经做到了,而且我已经看到CE0和CE1引脚始终为低电平。我不明白为什么我在MISO上没有碰撞。

编辑2

我已经解决了第二个问题(重复数据),这是主配置上的错误,我正在使用cs_change = 1,但是它必须等于0。

使用piscope,我发现从属设备正确地发送了数据,是不能很好地读取它们的主设备。

0 个答案:

没有答案