Страница 1 из 1

Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 10:54:46
foxis
Здравствуйте,
в процессе обучения программированию SPI на плате Nucleo-L053R8
возникли проблемы с передачей байта.

В данной статье описывается
один из простейших способов передачи, когда в один и тот же модуль
spi сначала передается байт данных, а потом принимается данный байт.

Для этого нужно кинуть перемычку между MISO и MOSI в SPI1 (в моей плате соединяются PA6, PA7).

Перемычку кинул, инициализацию провел в Cube.
Передаю и принимаю байт с помощью:

Код: Выделить всё

  HAL_SPI_Transmit(&hspi1, transmitBuffer, 1, 20);
  HAL_SPI_Receive_IT(&hspi1, receiveBuffer, 1);
соответственно.

Однако когда программа доходит до while(1)
в переменной receiveBuffer - пусто. А должен быть символ "v".

В чем может быть проблема ?
Спойлер

Код: Выделить всё


  */
/* Includes ------------------------------------------------------------------*/
#include "stm32l0xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t transmitBuffer[1] = "v"; 
uint8_t receiveBuffer[1];
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();

  /* USER CODE BEGIN 2 */

  HAL_SPI_Transmit(&hspi1, transmitBuffer, 1, 20);
  HAL_SPI_Receive_IT(&hspi1, receiveBuffer, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  __PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = 0;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* SPI1 init function */
void MX_SPI1_Init(void)
{

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
  hspi1.Init.CRCPolynomial = 7;
  HAL_SPI_Init(&hspi1);

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
     PA2   ------> USART2_TX
     PA3   ------> USART2_RX
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __GPIOC_CLK_ENABLE();
  __GPIOH_CLK_ENABLE();
  __GPIOA_CLK_ENABLE();
  __GPIOB_CLK_ENABLE();

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : USART_TX_Pin USART_RX_Pin */
  GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF4_USART2;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

#ifdef USE_FULL_ASSERT

/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

}

#endif




Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 12:45:08
Аlex
foxis писал(а):В чем может быть проблема ?
Да в чём угодно. Начиная от неправильно сконфигурированного модуля и его использования, заканчивая "железными" проблемами. У Вас железо в руках, пройдите отладкой.
Ну а вообще, для справки, SPI передаёт и принимает данные одновременно. Т.е. приём битов на MOSI идёт одновременно с передачей битов по MISO.

Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 13:11:28
foxis
Аlex писал(а):
foxis писал(а):В чем может быть проблема ?
Да в чём угодно. Начиная от неправильно сконфигурированного модуля и его использования, заканчивая "железными" проблемами. У Вас железо в руках, пройдите отладкой.
Ну а вообще, для справки, SPI передаёт и принимает данные одновременно. Т.е. приём битов на MOSI идёт одновременно с передачей битов по MISO.
Импульсы определенные появляются при прохождении строки (в режиме отладки):

Код: Выделить всё

HAL_SPI_Transmit(&hspi1, transmitBuffer, 1, 20);


Также после этой же строки значение из transmitBuffer переносится в регистр DR.

После строки

Код: Выделить всё

HAL_SPI_Receive_IT(&hspi1, receiveBuffer, 1);
Регистр DR очищается, но receiveBuffer остается пустым.



Насчет синхронного приема и передачи я в курсе, но как это может помочь найти ошибку ?

Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 13:14:34
Аlex
foxis писал(а): После строки

Код: Выделить всё

HAL_SPI_Receive_IT(&hspi1, receiveBuffer, 1);
Регистр DR очищается, но receiveBuffer остается пустым.
А зайти в эту строку функцию и пошагать там ?

Попробуйте разорвать связь. Будет ли содержаться значение в регистре после передачи ? Для начала хоть узнаете, реально ли данные передаются, или Вы просто видите записанное туда значение.

Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 13:48:03
foxis
Аlex писал(а):
foxis писал(а): После строки

Код: Выделить всё

HAL_SPI_Receive_IT(&hspi1, receiveBuffer, 1);
Регистр DR очищается, но receiveBuffer остается пустым.
А зайти в эту строку функцию и пошагать там ?

Попробуйте разорвать связь. Будет ли содержаться значение в регистре после передачи ? Для начала хоть узнаете, реально ли данные передаются, или Вы просто видите записанное туда значение.
Аlex,

1) Код функции HAL_SPI_Receive_IT
Спойлер

Код: Выделить всё

/**
  * @brief  Receive an amount of data in no-blocking mode with Interrupt
  * @param  hspi: pointer to a SPI_HandleTypeDef structure that contains
  *                the configuration information for SPI module.
  * @param  pData: pointer to data buffer
  * @param  Size: amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  if(hspi->State == HAL_SPI_STATE_READY)
  {
    if((pData == NULL) || (Size == 0)) 
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(hspi);

    /* Configure communication */
    hspi->State        = HAL_SPI_STATE_BUSY_RX;
    hspi->ErrorCode    = HAL_SPI_ERROR_NONE;

    hspi->RxISR        = &SPI_RxISR;
    hspi->pRxBuffPtr   = pData;
    hspi->RxXferSize   = Size;
    hspi->RxXferCount  = Size ; 

   /*Init field not used in handle to zero */
    hspi->TxISR        = 0;
    hspi->pTxBuffPtr   = NULL;
    hspi->TxXferSize   = 0;
    hspi->TxXferCount  = 0;

    /* Configure communication direction : 1Line */
    if(hspi->Init.Direction == SPI_DIRECTION_1LINE)
    {
      SPI_1LINE_RX(hspi);
    }
    else if((hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->Init.Mode == SPI_MODE_MASTER))
    {
      /* Process Unlocked */
      __HAL_UNLOCK(hspi);

      /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
      return HAL_SPI_TransmitReceive_IT(hspi, pData, pData, Size);
    }

    /* Reset CRC Calculation */
    if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
    {
      SPI_RESET_CRC(hspi);
    }

    /* Enable TXE and ERR interrupt */
    __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_RXNE | SPI_IT_ERR));

    /* Process Unlocked */
    __HAL_UNLOCK(hspi);

    /* Note : The SPI must be enabled after unlocking current process 
              to avoid the risk of SPI interrupt handle execution before current
              process unlock */

    /* Check if the SPI is already enabled */ 
    if((hspi->Instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE)
    {
      /* Enable SPI peripheral */
      __HAL_SPI_ENABLE(hspi);
    }

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY; 
  }
}
В ней компилятор заходит в

Код: Выделить всё

 else if((hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->Init.Mode == SPI_MODE_MASTER))
    {
      /* Process Unlocked */
      __HAL_UNLOCK(hspi);

      /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
      return HAL_SPI_TransmitReceive_IT(hspi, pData, pData, Size);
    }
и после строки return HAL_SPI_TransmitReceive_IT(hspi, pData, pData, Size);
обнуляет регистр DR.

2) Если перемычку между РА6 и РА7 убрать, то в регистр DR вообще ничего не заносится
ни во время приема, ни во время передачи (он всегда равен нулю).

Видимо DR все таки заполняется когда идет прием данных.

Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Пн ноя 30, 2015 23:30:55
Аlex
foxis писал(а): и после строки return HAL_SPI_TransmitReceive_IT(hspi, pData, pData, Size);
обнуляет регистр DR.
А Вас не смутило название этой строки функции ? :) Тем более, ещё и её аргументы обо всём говорят.
Зайдите в неё отладкой и увидите, что она передаёт данные из одного буфера и принимает в другой. А не просто читает регистр DR.
Вообще, учитывая, что
foxis писал(а):Насчет синхронного приема и передачи я в курсе
, странно, что Вы до сих пор не поняли, почему у Вас ничего не принимается.

Re: Простая передача по SPI на плате Nucleo-L053R8 (STM32)

Добавлено: Вт дек 01, 2015 11:03:38
foxis
Аlex писал(а):
А Вас не смутило название этой строки функции ? :) Тем более, ещё и её аргументы обо всём говорят.
Зайдите в неё отладкой и увидите, что она передаёт данные из одного буфера и принимает в другой. А не просто читает регистр DR.
Аlex, да, спасибо, теперь я понимаю, что там имеется сдвиговый регистр, а также TX, RX буферы.
Данные сначала поступают на TX буфер, потом уходят в сдвиговый регистр. Я так понял,
что DR это и есть тот самый сдвиговый регистр.
Аlex писал(а): странно, что Вы до сих пор не поняли, почему у Вас ничего не принимается.
1) Согласен, прием не работает. По крайней мере если с соединенной перемычкой между
РА6, РА7 регистр DR ведется себя по разному, то хотя бы в электрической связи можно не сомневаться,
дело скорее всего в программной реализации.

2) При отладке заметил, что флаг TXE устанавливается, а вот флаг RXNE не хочет устанавливаться.
Ни совсем понятно, что мешает данному флагу установиться ?
Вроде есть перемычка, регистр DR заполнен, что еще не хватает ?