почему получение сдвинутых и дублированных данных в SPI-связи между платами Raspberry Pi (master) и nucleo l432kc (slave) - PullRequest
0 голосов
/ 03 июня 2019

Я задавал один и тот же вопрос на форуме st Q & A и на форуме Raspberry Pi, но ответа не получил.Я надеюсь, что здесь есть кто-то, кто может мне помочь.

Я хочу установить связь двух ядер l432kc (рабов) с малиновым пи (ведущим) через протокол SPI.На малине я использую API spidev, а на ядре я использую интерфейс HAL SPI с DMA.

Единственной конфигурацией, которая разрешает стабильную передачу, является SPI_MODE_2 для spidev, который соответствует конфигурации SPI_POLARITY_HIGH и SPI_PHASE_2EDGE HAL.

С вышеуказанной конфигурацией у меня есть две проблемы:

  1. Сообщения, отправляемые ведущим на ведомые устройства, всегда поступают правильно, но все сообщения, отправляемые ведомыми на ведущее устройство, всегда приходятсдвинут на 1 бит справа (например, если я отправляю два байта 0b00000011 0b00000001, я получаю 0b00000001 0b10000000).Кажется, что выборка сигнала MISO задерживается, но я не могу понять, почему.
  2. Я получаю данные на обоих ведомых, независимо от того, установлен ли выбор микросхемы или нет.В ответе ведомых я не имею коллизий, поэтому я думаю, что только шина MISO эффективно установлена ​​в режиме высокого импеданса в ведомом устройстве, когда NSS высокий.

Для тестирования я пытался использовать класс SPISlave платформы mbed, этот класс не может использовать DMA, поэтому я не могу использовать его в реальной программе.Используя его, я решаю первую проблему, но вторая остается.

Еще одна вещь, которая может быть полезна, - это то, что с классом SPISlave я могу использовать все 4 режима протокола SPI, конечно, ведомые и ведущие должныиспользовать тот же режим, но не импортировать какой.Как я уже говорил ранее с интерфейсом 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");
}

В основных функциях рабов единственное, что я на самом деле делаю, - это отправляю обратно именно тот пакет, который я получаю.

EDIT

На форуме малины кто-то сказал мне использовать piscope, чтобы увидеть цифровое значение, которое отправляется на контакты.Я сделал это, и я видел, что контакты CE0 и CE1 всегда низки.Я не могу понять, почему у меня нет коллизий в MISO.

РЕДАКТИРОВАТЬ 2

Я решил вторую проблему (дублированные данные), это была ошибка в главной конфигурации,Я использовал cs_change = 1, но он должен быть равен 0.

С piscope Я выяснил, что ведомые устройства отправляют данные правильно, мастер, который их не читаетЧто ж.

...