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)
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
According to AD5270 documentation, there are following commands:
So I decided to test the following:
- Send NOP (do nothing) command a couple of times (0x00 command)
- 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:
My couple concerns here:
- Why the Clock IDLE state is high? I have selected CPOL so I would assume it should be LOW
- Why the MISO is pulled down even though there is a pull up resistor
Anyways, lets move on, after I send first NOP command:
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:
Now even worse things happen when I try to read control register:
2nd time reading control register:
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):
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:
- Why initially (before sending any SPI commands), the CLOCK and MISO lines are in incorrect IDLE state?
- Why after sending first NOP command, the CLOCK and MISO lines are fixed?
- Why after reading control register once, the MISO line is pulled down?