Старт ARM. SPI интерфейс, часть 1-ая. HAL

Доброе время суток продолжаем серию статей про микроконтроллеры stm32f4, сегодня будем осваивать SPI протокол, а поможет нам в этом хорошая девайсина – гироскоп L3GD20. Что такое гироскоп – это датчик измеряющий изменение угла ориентации тела в пространстве (p.s. кого интересую построение беспилотных летательных аппаратов рекомендую почитать замечательную книгу по данной тематики автора Распопов В.Я – микросистемная авионика). Будем юзать данную девайсину на борде stm32f401C-disco (stm32f401VC – LQFP100), данная микросхема также присутствует на отладочной плате stm32f4-discovery и stm32f429i-disco. Как обычно будем использовать HAL – Hardware Abstraction Layer, то есть слой аппаратной абстракции. Как работает SPI?
SPI (Serial Peripheral Interface, SPI bus — последовательный периферийный интерфейс, шина SPI) — последовательный синхронный стандарт передачи данных в режиме полного дуплекса, хотя режим передачи можно выбирать, так например полу дупликс, либо односторонняя передача в режимах мастер и слейв. SPI является синхронным интерфейсом, за счет линии синхронизации SCLK. Микроконтроллер с помощью сигнала Select chip – CS выбирает ведомое устройство и начинается с ним обмениваться информацией. Обмен информацией осуществляется по каналам MOSI(Master Out Slave In) и MISO(Master In Slave Out).

Передача осуществляется пакетами либо 8 бит либо 16 бит. В нашем случае пакет состоит из 16 бит. Причем первых 2 бита отвечаю за вид передачи данных (запись либо чтение).

Бит 0 – RW бит, если 0 –тогда пишем в устройство, 1 читаем из устройства.
Бит 1 – MS, 0 – адрес остается неизменным, 1 – автоматическое инкрементирование адреса
Биты 2 – 7 – адреса полей
Биты 8 – 15 – биты данных

Регистры девайсины
И конечно код нашей программы:
main.c

#include "stm32f4xx_hal.h"
SPI_HandleTypeDef hspi1;

float translate(uint16_t result){
		return result * 0.07;
}

float ms(float data){
	return 0.1F * data;
}

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);

int main(void)
{
	uint8_t address = 0;
	uint8_t data = 0;
	uint8_t data_return[2] = {0};

	uint16_t OUT_X_L = 0;	
	uint16_t OUT_Y_L = 0;
	uint16_t OUT_Z_L = 0;

	float fOUT_X_L;
	float fOUT_Y_L;
	float fOUT_Z_L;

	//..................................................
  HAL_Init();
  SystemClock_Config();
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  MX_GPIO_Init();
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
	MX_SPI1_Init();		
	//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	//WHO_AM_I
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
	address = 0x20;	//00100000 - CTRL_REG_1
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	address = 0x0F; //00001111
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
	
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
	address = 0x8F; //10001111 - WHO_AM_I - READ
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	address = 0x00; //00000000
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
	
	//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
	address = 0x23; //00100011 - CTRL_REG4 
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	address = 0x30;	//00110000
	HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
	//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	//OUT_X_L 0xE8 - 11101000 <- 0x28 0101000
	//OUT_X_H 0xE9 - 11101001 <- 0x29 0101001		
		
	//OUT_Y_L 0xEA - 11101010 <- 0x2A 0101010
	//OUT_Y_H 0xEB - 11101011 <- 0x2B 0101011
		
	//OUT_Z_L 0xEC - 11101100 <- 0x2C 0101100
	//OUT_Z_H 0xED - 11101101 <- 0x2D 0101101
	while (1)
  {
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
		address = 0xE8;		
		HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[0], sizeof(data_return[0]), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[1], sizeof(data_return[1]), 0x1000);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
		OUT_X_L = data_return[0] | (data_return[1] << 8);
		fOUT_X_L = translate(OUT_X_L);
		fOUT_X_L = ms(fOUT_X_L);		
		//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
		address = 0xEA;		
		HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[0], sizeof(data_return[0]), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[1], sizeof(data_return[1]), 0x1000);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
		OUT_Y_L = data_return[0] | (data_return[1] << 8);
		fOUT_Y_L = translate(OUT_Y_L);
		fOUT_Y_L = ms(fOUT_Y_L);
		//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
		address = 0xEC;		
		HAL_SPI_TransmitReceive(&hspi1, &address, &data, sizeof(data), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[0], sizeof(data_return[0]), 0x1000);
		address = 0x00;
		HAL_SPI_TransmitReceive(&hspi1, &address, &data_return[1], sizeof(data_return[1]), 0x1000);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
		OUT_Z_L = data_return[0] | (data_return[1] << 8);
		fOUT_Z_L = translate(OUT_Z_L);
		fOUT_Z_L = ms(fOUT_Z_L);
		//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  }
}

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

  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;

  __PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

}

/* 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_256;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLED;
	hspi1.Init.CRCPolynomial = 7;
  HAL_SPI_Init(&hspi1);

}

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

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

  /*Configure GPIO pin : PE2 */
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

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

  /*Configure GPIO pins : PE4 PE5 PE1 */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

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

  /*Configure GPIO pin : PC3 */
  GPIO_InitStruct.Pin = GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PB2 */
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PB10 PB12 */
  GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PD12 PD13 PD14 PD15 
                           PD4 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15 
                          |GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /*Configure GPIO pins : PC7 PC10 PC12 */
  GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_10|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PA9 */
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PA10 PA11 PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PD5 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /*Configure GPIO pins : PB6 PB9 */
  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	
}

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t* file, uint32_t line){
}

#endif

stm32f4xx_hal_msp.c

#include "stm32f4xx_hal.h"
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hspi->Instance==SPI1)
  {
    /* Peripheral clock enable */
    __SPI1_CLK_ENABLE();
  
    /**SPI1 GPIO Configuration    
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  }

}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{

  if(hspi->Instance==SPI1)
  {
    __SPI1_CLK_DISABLE();
    /**SPI1 GPIO Configuration    
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI 
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);

  }

}


Проект целиком.
В следующей части про интерфейс SPI рассмотрим хардварные особенности его применения. На сегодня все, всем пока!), увидимся на просторах mcu.by.

15 thoughts on “Старт ARM. SPI интерфейс, часть 1-ая. HAL

  1. Я новичек микроконтроллерах, потому могу ошибаться но мне кажется в примере ошибка:
    Сначала вы пишите: “В нашем случае пакет состоит из 16 бит. ”
    А потом в коде где происходит инициализация SPI :
    “hspi1.Init.DataSize = SPI_DATASIZE_8BIT;”

    или все верно ?

  2. Все верно, просто пакет разбиваю на 2-е части. 1-часть пакета адрес, вторая часть пакета информация.

  3. Я Вас категорически приветствую! Я пошагово изучаю Ваш код, первое , что мне не понятно – 4-ый аргумент функции HAL_SPI_TransmitReceive, в Reference Manual, он описан как Size – amount of data to be sent, т.е. как я понял, количество данных для отправки, тогда почему вы передаете sizeof(data), резмер буфера для принимаемых данных?

  4. Все верно Вы понимаете, только у меня в коде data равен 1 байту, поэтому я смело использую sizeof(data), можно было просто написать 1, и результат был бы аналогичный

  5. Спасибо за приведённый пример работы с SPI на HAL Drivers. Как раз занялся изучением этой тематики и могу точно сказать, примеры кода найти сложно.
    Нашёл небольшую опечатку в тексте, перепутаны определения в скобках: … осуществляется по каналам MISO(Master Out Slave In) и MOSI(Master In Slave Out)…

  6. Хороший пример, спасибо. Я сейчас решаю похожую задачу для акселерометра LIS3DSH, который установлен на плате STM32F4. Поясните, будьте добры, зачем Вы используете функции translate, ms. Примерно понимаю зачем они, но чем это обусловлено не ясно.

    1. HAL_SPI_TransmitReceive – является блокирующей функцией, будем ждать промежуток времени указанный в скобках последним аргументом, если данных не поступило за данный промежуток времени, то идем выполнять программу дальше, если данные пришли, то все отлично, не ждем истечения заданного интервала времени, а выполним программу дальше.

  7. Здравствуйте. Прерывания здесь на используются?

    1. Здравствуйте, код был построен на блокирующий функциях SPI

  8. И вопрос. Большой код инициализации GPIO – это ещё для каких-то целей? Мне куб 10 строк выдал в этом месте.

  9. Добрый день,
    Столкнулся с непонятным явлением. Функция HAL_SPI_TransmitReceive() иногда возвращает HAL_TIMEOUT. Совершенно непонятно как это может быть, если мое устройство — Master, и оно само выдает клокирующие сигналы и само защелкивает в сдвиговом регистре какие-то принятые данные. Как при этих условиях я, Master, вдруг могу _не_получить_ байт и ждать его в течение таймаута? Возможно, это глюк HAL’а ?

Comments are closed.