0

I am working with AD5270 IC (10 bit digital potentiometer): https://www.analog.com/media/en/technical-documentation/data-sheets/ad5270_5271.pdf

I am writing SPI drivers for it on STM32F407.

Little bit about hardware:

On the same SPI bus, I have initially connected 2 (AD5270 IC's, but I have desoldered IC6 from the board just to make debugging easier (to ensure it does not have any affect on the IC that I am testing (IC2)

AD5270#1

AD5270#

STM32CubeMX configuration:

As you can see from the STM32CubeMX configuration below, I have selected SPI2 peripheral (CPOL = 0 and CPHA = 1) as requested in the AD5270 datasheet enter image description here

According to AD5270 documentation, there are following commands:

enter image description here

So I decided to test the following:

  1. Send NOP (do nothing) command a couple of times (0x00 command)
  2. Read contents of control register a couple of times (0x20 command)

The source code:


#include "main.h"


static void AD5270_CS_Enable();
static void AD5270_CS_Disable();
static void AD5270_Send_NOP_command();
static uint16_t AD5270_Read_control_reg();



SPI_HandleTypeDef hspi2;

static void AD5270_CS_Enable(){
    HAL_GPIO_WritePin(CS_CTRL_IO_VCC2_GPIO_Port, CS_CTRL_IO_VCC2_Pin, 0); // enable the AD5270

}

static void AD5270_CS_Disable(){
    HAL_GPIO_WritePin(CS_CTRL_IO_VCC2_GPIO_Port, CS_CTRL_IO_VCC2_Pin, 1); // enable the AD5270
}




static uint16_t AD5270_Read_control_reg()
{
    uint16_t result = 0;
    uint8_t data[2] = {0x20,0xFF};

    AD5270_CS_Enable();
    HAL_SPI_Transmit(&hspi2, (uint8_t *)&data, 2, 0xff);
    AD5270_CS_Disable();

    AD5270_CS_Enable();
    HAL_SPI_Receive(&hspi2, (uint8_t *)&result, 2, 0xff);
    AD5270_CS_Disable();
    printf("AD5270 READ REG VALUE = %.2x \n",result);
    return result;


}



static void AD5270_Send_NOP_command()
{

    AD5270_CS_Enable();
    uint8_t data[2] = {0x00,0xFF};
    HAL_SPI_Transmit(&hspi2, (uint8_t *)&data, 2, 0xff);
    AD5270_CS_Disable();

}


void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);

int main(void)
{
 
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_SPI2_Init();
  //ensure both chipselects are HIGH (default off)
  HAL_GPIO_WritePin(CS_CTRL_IO_VCC1_GPIO_Port, CS_CTRL_IO_VCC1_Pin, 1);
  HAL_GPIO_WritePin(CS_CTRL_IO_VCC2_GPIO_Port, CS_CTRL_IO_VCC2_Pin, 1);


  AD5270_Send_NOP_command();
  HAL_Delay(1000);
  AD5270_Send_NOP_command();
  HAL_Delay(1000);
  AD5270_Send_NOP_command();
  HAL_Delay(1000);

  AD5270_Read_control_reg();
  HAL_Delay(1000);

  AD5270_Read_control_reg();
  HAL_Delay(1000);

  AD5270_Read_control_reg();
  HAL_Delay(1000);


  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_0, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7, GPIO_PIN_RESET);

  /*Configure GPIO pin : PG0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /*Configure GPIO pin : PE7 */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#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 /* USE_FULL_ASSERT */

I am monitoring the SPI bus via the logic analyzer and I am very confused about the results, I put a break point on the first Send NOP command function:

enter image description here

enter image description here

My couple concerns here:

  1. Why the Clock IDLE state is high? I have selected CPOL so I would assume it should be LOW
  2. Why the MISO is pulled down even though there is a pull up resistor

Anyways, lets move on, after I send first NOP command: enter image description here

As you can see from above, it seems to be sending correct command (0x00,0xFF). After the command is send, the clock goes LOW and IDLE as it should be. Also, the MISO line is also pulled up as it should be. Why is that happening? Why do I need to send a dummy command to ensure the SPI IDLE states are as expected?

The 2nd and 3rd NOP commands look as following: enter image description here

Now even worse things happen when I try to read control register: enter image description here

2nd time reading control register: enter image description here

As you can see from 2 images above, after the first control register read, the MISO line was pulled down and never recovered.

The full sequence (3 times NOP command and 3 times reading control register): enter image description here

I am hoping to get some clarification regards to debugging SPI issues that I am having. I have been trying out different things over and over again without any luck. Thanks in advance..

To summarise, my questions:

  1. Why initially (before sending any SPI commands), the CLOCK and MISO lines are in incorrect IDLE state?
  2. Why after sending first NOP command, the CLOCK and MISO lines are fixed?
  3. Why after reading control register once, the MISO line is pulled down?
TheBestPlayer
  • 324
  • 2
  • 13
  • For your first two questions: The STM HAL drivers are kinda bad. They try to do everything behind your back to make it "simpler", but then you don't understand what they are doing (assuming they do it right, which they don't). In this case they disable the SPI peripheral at init, and only enable it at first use. You can either work around this by using __HAL_SPI_ENABLE yourself and hope they is no more problems with the HAL, or take the time to make a driver yourself. – ElderBug Nov 25 '22 at 14:04
  • You are reading the control register, which defaults to zero. What do you expect to see on the MISO line? – Flexz Nov 25 '22 at 17:36
  • I got it to work. I was just very confused by that initial CLOCK and MISO state before any SPI command is sent.. – TheBestPlayer Nov 30 '22 at 12:23

0 Answers0