cancel
Showing results for 
Search instead for 
Did you mean: 

STEVAL-FCU001V1 and LSM6DSL SPI communication error.

Manuel-Baez
Associate II

Hello, dear community!

Currently, I am working on a project with the STEVAL-FCU001V1 board that has an STM32F401CC microcontroller and an LSM6DSL 3D accelerometer and gyroscope sensors integrated chip, the board datasheet says that it is accessible through the SPI2 module in half duplex mode, using the PA8 as slave select, PB13 as a clock signal and the PB15 as data bidirectional line.

So, in the beginning, I tried to configure the module using CMSIS macros, as shown below.

/* Global variables -----------------------------------------------------------------------------------------------------------*/
char Addr[] = {Write_Frame(LSM6DSL_CTRL1_XL), Write_Frame(LSM6DSL_CTRL3_C), Write_Frame(LSM6DSL_CTRL4_C), Write_Frame(LSM6DSL_CTRL6_C), Write_Frame(LSM6DSL_CTRL9_XL)};
char Dato [] = {0x40, 0x0C, 0x04, 0x10, 0x00};

/* @Brief:  Configuración del los perifericos mediante los modulos GPIOA->GPIOH.
 * @PAram:  None
 * @Return: None
 */
void init_GPIO(void){
	GPIOA->MODER &= 0xFFC0FFFF;	// Habilitación de los pines PA9 y PA10 para funciones adicionales y PA8 como salida.
	GPIOA->MODER |= 0x002904A3;
	GPIOA->OSPEEDR |= 0x003C0000;	// Asignación de la maxima velocidad de conmutación a los pines PA9 Y PA10.
	GPIOA->AFR[1] &= 0xFFFFF00F;	// Configuración de PA9 Y PA10 como RX y TX para el USART1.
	GPIOA->AFR[1] |= 0x00000770;
	GPIOA->ODR |= GPIO_ODR_OD8;			// PA8 en estado alto.

	GPIOB->MODER &= 0x33FFFFFF;	// Habilitación de los pines PB15 y PB13 para funciones adicionales.
	GPIOB->MODER |= 0x88000000;
	GPIOB->OSPEEDR |= 0xCC000000;	// Asignación de la maxima velocidad de conmutación a los pines PB13 Y PB15.
	GPIOB->AFR[1] &= 0x00FFFFFF;	// Configuración de los pines PB13 y PB15 como SDA y CLK para el SPI2.
	GPIOB->AFR[1] |= 0x55000000;
	return;
}

/* @Brief:  Configuración del modulo USART2 para una comunicación asincrona a 9600 baudios.
 * @PAram:  None
 * @Return: None
 */
void init_SPI2(void){
	SPI2->CR1 &= ~SPI_CR1_SPE;
	SPI2->CR1 &= 0xC387;	// Deshabilitación del CRC(CRCEN=0), longitud de mensage de 8bits(DFF=0).
	SPI2->CR1 |= 0x8397;	/* Half Duplex(BIDIMODE=1), selección de esclavo por software(SSM=1), LSB primero(LSBFIRST=1), frecuencia de SYSCLK/8(BR[2:0]=0b010),
	--------------------	   configuración como maestro(MSTR=1), estado normal del reloj en alto(CPOL=1), lectura de dato en flanco descendente(HPHA=1).*/
	SPI2->CR2 &= 0xFF08;	// Deshabilitación de interrupciones, formato Motorola de trama(FRF=0), deshabilitación de los DMA.
	SPI2->CR1 |= SPI_CR1_SPE;
	return;
}

/* @Brief:  Configuración del 3D giroscopio y acelerometro LSM6DSL externo por medio del protocolo SPI.
 * @PAram:  char* addr: Un puntero a un vector de tipo char en el que se almacenan las direcciones de memoria a configurar.
 * @PAram:  char* dato: Un puntero a un vector de tipo char en el que se almacenan las distintas configuraciones para el integrado.
 * @Return: None
 */
void init_LSM6DSL(char* addr, char* dato){
	for (int i=0; (*(addr+i)!='\0'); i++){
		write_SPI2(*(addr+i), *(dato+i));
	}
}

/* @Brief:  Función de lectura de los valores recividos mediante el SPI2.
 * @PAram:  None
 * @Return: (char) SPI2->RDR: El caracter o valor recivido, un dato de 8 a 16 bits guartado en el registro RDR.
 */
char read_SPI2(char addr){
	if(!(SPI2->CR1 & SPI_CR1_BIDIOE)){
		SPI2->CR1 &= ~SPI_CR1_SPE;	// Deshabilitación del modulo SPI2 para modificar su configuración.
		SPI2->CR1 |= 0x4000;	// Estado de transmisión(BIDIOE=1).
		SPI2->CR1 |= SPI_CR1_SPE;	// Habilitación del modulo SPI2.
	}
	GPIOA->ODR &= ~GPIO_ODR_OD8;	// Puesta el bajo de PA8 para seleccionar el dispocitivo esclavo.
	SPI2->DR = addr;	// Envío de la dirección de lectura.
	while(!(SPI2->SR & SPI_SR_TXE));	// Espera a la finalización de la transmisión.
	SPI2->CR1 &= ~SPI_CR1_SPE;	// Deshabilitación del modulo SPI2 para pasar del estado de transmisión al estado de recepción.
	SPI2->CR1 &= ~SPI_CR1_BIDIOE;	// Estado de recepción(BIDIOE=1).
	SPI2->CR1 |= SPI_CR1_SPE;	// Habilitación del modulo SPI2.
	while(!(SPI2->SR & SPI_SR_RXNE));	// Espera a la finalización de la recepción del dato.
	GPIOA->ODR |= GPIO_ODR_OD8;	// Puesta en alto de PA8 para finalizar la comunicación con el dispocitivo esclavo.
	return (char)(SPI2->DR);	// Retorno del dato recivido.
}

/* @Brief:  Función de escritura mediante el SPI2.
 * @PAram:  char dato: El valor de entre 8 y 16 bits a transmitir mediante escritura en el registro TDR.
 * @Return: None
 */
void write_SPI2(char addr, char dato){
	if(!(SPI2->CR1 & SPI_CR1_BIDIOE)){
		SPI2->CR1 &= ~SPI_CR1_SPE;	// Deshabilitación del modulo SPI2 para modificar su configuración.
		SPI2->CR1 |= SPI_CR1_BIDIOE;	// Estado de transmisión(BIDIOE=1).
		SPI2->CR1 |= SPI_CR1_SPE;	// Habilitación del modulo SPI2.
	}
	GPIOA->ODR &= ~GPIO_ODR_OD8;	// Puesta el bajo del PA8 para seleccionar el dispocitivo esclavo.
	SPI2->DR = dato;	// Envío de la configuracion de acceso a memoria.
	while(!(SPI2->SR & SPI_SR_TXE));	// Espera a la finalización del transmisión.
	SPI2->DR = addr;	// Envío del dato a escribir.
	while(!(SPI2->SR & SPI_SR_TXE));	// Espera a la finalización del transmisión.
	GPIOA->ODR |= GPIO_ODR_OD8;	// Puesta en alto de PA8 para finalizar la comunicación con el dispocitivo esclavo.
	return;
}

 In these lines of code, I work with a sysclk = 40MHz and I have written a header file "lsm6dsl.h" with the macros for the address of the lsm6dsl sensor, and a #define Write_Frame(addr) (0x80 | addr), also I wrote an init configuration function, a write function and a read function for the SPI half duplex protocol and an init configuration sensor function for transfer the start configuration registers to the lsm6dsl integrated, but when I send the read command for the WHO_AM_I register, the expected response is 0xA8 but I receive 0x00.

So I started a new MX project thinking maybe I didn't configure the SPI2 module correctly, The code for this project is below.

char frame[] = {
				0x40,
				Write_Frame(LSM6DSL_CTRL1_XL),
				0x0C,
				Write_Frame(LSM6DSL_CTRL3_C),
				0x04,
				Write_Frame(LSM6DSL_CTRL4_C),
				0x10,
				Write_Frame(LSM6DSL_CTRL6_C),
				0x00,
				Write_Frame(LSM6DSL_CTRL9_XL)
};

/**
  * @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_1LINE;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_LSB;
  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 */
  SPI_1LINE_TX(&hspi2);
  HAL_Delay(5);
  __HAL_SPI_ENABLE(&hspi2);
  /* USER CODE END SPI2_Init 2 */
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PB12 */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
  GPIOC->ODR |= GPIO_ODR_OD13;
  GPIOB->ODR |= GPIO_ODR_OD12;
  GPIOA->ODR |= GPIO_ODR_OD8;
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
void write_LSM6DSL(char data, char addr){
	GPIOA->ODR &= ~GPIO_ODR_OD8;
	HAL_SPI_Transmit(&hspi2, (uint8_t*) &data, 1, 15);
	HAL_SPI_Transmit(&hspi2, (uint8_t*) &addr, 1, 15);
	GPIOA->ODR |= GPIO_ODR_OD8;
}

char read_LSM6DSL(char addr){
	char data = 0;
	GPIOA->ODR &= ~GPIO_ODR_OD8;
	HAL_SPI_Transmit(&hspi2, (uint8_t*) &addr, 1, 15);
	__HAL_SPI_DISABLE(&hspi2);
	SPI_1LINE_RX(&hspi2);
	__HAL_SPI_ENABLE(&hspi2);
	HAL_SPI_Receive(&hspi2, (uint8_t*) &data, 1, 15);
	__DSB();
	__DSB();
	__HAL_SPI_DISABLE(&hspi2);
	GPIOA->ODR |= GPIO_ODR_OD8;
	SPI_1LINE_TX(&hspi2);
	__HAL_SPI_ENABLE(&hspi2);
	return data;
}

void init_LSM6DSL(char* frame, char zise){
	for (int i=0; i<(zise-1); i+=2){
		write_LSM6DSL(*(frame+i), *(frame+i+1));
	}
}

Again, when I send the read command for the WHO_AM_I record, the response is 0x00. so I still don't know what the error is or where I can start looking for it, so any help you can provide me will be very appreciated, I have uploaded the most important files of both codes in the post.

 

Manue Baez
1 REPLY 1
Manuel-Baez
Associate II

I would like to indicate the last actuality of the project state, I started the debugging of the project and I think that I have found a bug in the HAL_SPI_RECEIVE() function of HA_STM32F4 Library because when I execute the function Step by Step, the SFRs section shows that you have received the expected value but this value hasn't saved in the RX data buffer.

Manue Baez