cancel
Showing results for 
Search instead for 
Did you mean: 

SPI with STM32f429

Matt G
Associate II

Hi,

I am using an STM32f429 Discovery board with CubeMX and HAL, and have some problems regarding SPI. I am using an DAC which takes data in 24 bit format, 8 control bits and 16 data. 

  1. I can only select between 8 or 16 bit data, not both. If I try to send 8 bit control data in 16 bit mode I get extra clocks either end depending if LSB or MSB is sent first. I have tried changing between modes via the hspi struct but it won't change after first configuration. 

  1. I am using 3 element 8 bit 2D arrays for to store initial control data & output state data but the order of SPI data transmitted via HAL_SPI_Transmit_DMA(&hspi1,Var,3)

when declared as Var[]={1,2,3} is actually {1 3 2} where 3 is transmitted as 85. 

  1. The last element of the array (except the 2nd since order is messed up) is always transmitted as 85 (01010101) regardless of what I set it to. 

Please can someone advise?

Thanks 

11 REPLIES 11
T J
Lead

maybe you have an array overrun.

we are here to help with code, but there is no code in your question..

Matt G
Associate II

Hi,

Here is my code, it just a basic test program.

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
 
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_tx;
 
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_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();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
 
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
		uint8_t bias_position_D[] = {255, 255, 255};
		HAL_SPI_Transmit_DMA(&hspi1,bias_position_D,3);
  /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
 
}
 
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 360;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Activate the Over-Drive mode 
    */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
 
/* SPI1 init function */
static 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_HARD_OUTPUT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
 
}
 
/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
     PG5   ------> FMC_A15_BA1
*/
static void MX_GPIO_Init(void)
{
 
  GPIO_InitTypeDef GPIO_InitStruct;
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
 
  /*Configure GPIO pin : PG5 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
 
}
 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  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
 
/**
  * @}
  */ 
 
/**
  * @}
*/ 
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

T J
Lead

well it is very poor programming technique that is your problem.

you send one frame and as it's finished, sent, or not, you are sending another frame...

I would suggest that you forget about using the SPI DMA approach,

much easier to control at the register level.

when you mentioned 8 bit and 16bit in the same frame that implies that you really need to control the nSS pin manually, hence the register level approach.

which DAC chip is it.. ?

Matt G
Associate II

Hi,

Sorry I realised I submitted an earlier version, my current main is as follows (everything else is the same). I think the IF statement takes care of what you referred to.

uint8_t bias_position_D[] = {255, 255, 255}         
 for (;;)
  {
          if (hspi1.State != HAL_SPI_STATE_BUSY_TX)
		HAL_SPI_Transmit_DMA(&hspi1,bias_position_D,3);
  }

I think the IF statement takes care of what you referred to.

I made the array declaration a global variable but this time position 3 is transmitted as 0 every time rather than 85. Seems like there is a bug with the HAL?

I am pretty new to STM programming so my code is based my reading so far. I am using DMA simply because it may be more efficient later on, but I initially used HAL_SPI_Transmit.

The DAC is an AD5664, see below.

http://www.analog.com/media/en/technical-documentation/data-sheets/AD5624_5664.pdf

My intention was to change from 8 bit to 16 bit, briefly pausing the clock then resuming it and sending the remaining 16 clocks. I'm not sure what you meant about manual control of the NSS pin as I only have one slave. At the moment I am just looking at data on an oscilloscope but I will probably control the NSS pin in software at the start and end of all data once I can see valid 24 bit data.

I don't Cube nor CubeMX, but how do you associate the DMA structure to the SPI structure? In the examples I see

 /* Associate the initialized DMA handle to the the SPI handle */

   __HAL_LINKDMA(hspi, hdmatx, hdma_tx);

JW

T J
Lead

I never used the SPI DMA function because the NSS pin is not handled properly..

maybe DMA as a slave is ok.

T J
Lead

I use this to initialise the SPI interface:

void initSPI(void)  {
    int8_t TxSPIBuffer[1];
    int8_t RxSPIBuffer[1];
    TxSPIBuffer[0] = 0xFF;
    //InitSpi port for LCD
    HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)TxSPIBuffer, (uint8_t *)RxSPIBuffer, 1, 10);
}

then I use this to talk to the target chip, controlling the nSS pin as needed by the target device da

void transfer(unsigned short data) {   // send only
    char RxSPI;
   
    while (!(hspi1.Instance->SR  & SPI_FLAG_TXE)) ;
    *((__IO uint8_t *)&hspi1.Instance->DR) =  data;
            RxSPI = hspi1.Instance->DR;// + readSPI_SR;    // read Rx byte and discard, only clearing the buffer
}
char transfer_receive(unsigned short data) {   // send and receive byte
 
    char RxSPI;                                                     
    while(!(hspi1.Instance->SR  & SPI_FLAG_TXE))    // make sure the last byte is gone.
         ;
    while ((hspi1.Instance->SR  & SPI_FLAG_RXNE))  
     RxSPI = hspi1.Instance->DR;                    //empty and dump all fifo bytes
 
    *((__IO uint8_t *)&hspi1.Instance->DR) = data;  // force the SPI to transceive 8 bit
    while(!(hspi1.Instance->SR  & SPI_FLAG_TXE))    // wait to transmitter double buffer to shift into transmitter
         ;                   
    while ((hspi1.Instance->SR  & SPI_FLAG_BSY)) ;  // wait for data to shift out of the processor pins
    while((hspi1.Instance->SR  & SPI_FLAG_RXNE))   // load all the bytes received, only keep the last one
        RxSPI = hspi1.Instance->DR;               // we only want the last byte
 
    return RxSPI;
}

tasheet.

int8_t readLis3DEByte(char address) {
    address &= 0x3F;     // 6bit address
    address += 0x80;     // Read
    address += 0x40;     // Auto Increment
    HAL_GPIO_WritePin(LIS3DE_nSS_GPIO_Port, LIS3DE_nSS_Pin, GPIO_PIN_RESET);
    
    transfer(address);
    char dummyByte = 0;
    char RxSPI = transfer_receive(dummyByte);
    
    //putc1(RxSPI);
    HAL_GPIO_WritePin(LIS3DE_nSS_GPIO_Port, LIS3DE_nSS_Pin, GPIO_PIN_SET);
    return RxSPI;
   
}

using these routines:

Matt G
Associate II

Hi,

Thanks for the code. I appreciate you sharing it with me but all of my code for other on chip peripherals I've written is using HAL so I would prefer to stick with it for continuity, and resolve the issues. It also makes it easier for others I'm working with to follow my code.

I think what I'm trying to achieve could be done quite simply with HAL, if it worked as it should.

These problems exist using DMA and Polling modes.

Please could an ST employee or anyone familiar with the HAL advise on the position of what appears to be bugs and how to solve?

Thanks

Matt G
Associate II

Someone?