STM32 SPI 访问配置霍尔磁编码器KTH7823

STM32 SPI 访问配置霍尔磁编码器KTH7823

KTH7823是一款国产16 位高精度低延时霍尔磁编码器/角度传感器,电机运转时,可以通过编码器获取角度和位置以及计算出转速等信息。KTH7823芯片适应在轴和离轴监测方式。

如果采用多对级磁铁,则可以提高输出分辨率的稳定性。

这里介绍采用TM32CUBEIDE开发环境,及STM32F401RCT6访问配置KTH7823的过程。电路连接如图所示:

STM32硬件接口配置

首先进行STM32CUBEIDE的工程配置,建立新的工程并配置时钟和接口:

48MHz时钟是配置给USB VCOM所用,先要进行USB VCOM配置:


USB VCOM的配置使用也可以参考《STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)》

配置SPI2作为通讯端口:

保存并生成基本工程代码:

KTH7823 SPI访问寄存器

KTH7823 SPI访问协议

KTH7823 角度计算

STM32访问功能规划

本例程将实现如下功能:

  1. 通过USB VCOM可以发送十六进制单字节命令到STM32
  2. 发送0x01和1个字节地址,通过SPI读取KTH7823地址对应的寄存器数据
  3. 发送0x02,1个字节地址,1个字节数据,通过SPI配置KTH7823特定地址的特定数据
  4. 发送0x03,通过SPI读取KTH7823角度数据
  5. 发送0x04, 通过SPI读取KTH7823角度数据并转换为角度
  6. 发送0x05, 通过SPI发送寄存器锁定命令,在KTH7823关电重启前不能再写操作寄存器

STM32工程代码

首先设置USB VCOM接收串口指令:

c 复制代码
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
	extern uint8_t ucmd[8];


	memcpy(ucmd, Buf, *Len);


  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

在main.c文件里实现全部的控制逻辑:

c 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
//For i2c access
//Pin "mode" = high
//Pin "SSI CS = high
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float semiusDelayBase = 0;
void PY_semiusDelayTest(void)
{
__IO uint32_t firstms, secondms;
__IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  semiusDelayBase = ((float)counter)/2000;
}

void PY_Delay_semius_t(uint32_t Delay)
{
__IO  uint32_t delayReg;
__IO  uint32_t semiusNum = (uint32_t)(Delay*semiusDelayBase);

  delayReg = 0;
  while(delayReg!=semiusNum) delayReg++;
}

void PY_semiusDelayOptimize(void)
{
__IO  uint32_t firstms, secondms;
__IO  float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_semius_t(2000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  semiusDelayBase = coe*semiusDelayBase;
}

void PY_Delay_semius(uint32_t Delay)
{
__IO  uint32_t delayReg;

  uint32_t msNum = Delay/2000;
  uint32_t semiusNum = (uint32_t)((Delay%2000)*semiusDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=semiusNum) delayReg++;
}


void PY_Delay_us_t(uint32_t Delay)
{
	PY_Delay_semius_t(Delay*2);
}

void PY_Delay_ms_t(uint32_t Delay)
{
	PY_Delay_us_t(Delay*1000);
}
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/*
*Convert float to string type
*Written by Pegasus Yu in 2022
*stra: string address as mychar from char mychar[];
*float: float input like 12.345
*flen: fraction length as 3 for 12.345
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
void py_f2s4printf(char * stra, float x, uint8_t flen)
{
	uint32_t base;
	int64_t dn;
	char mc[32];

	base = pow(10,flen);
	dn = x*base;
	sprintf(stra, "%d.", (int)(dn/base));
	dn = abs(dn);
	if(dn%base==0)
	{
		for(uint8_t j=1;j<=flen;j++)
		{
			stra = strcat(stra, "0");
		}
		return;
	}
	else
	{
		if(flen==1){
			sprintf(mc, "%d", (int)(dn%base));
			stra = strcat(stra, mc);
			return;
		}

		for(uint8_t j=1;j<flen;j++)
		{
			if((dn%base)<pow(10,j))
			{
				for(uint8_t k=1;k<=(flen-j);k++)
				{
					stra = strcat(stra, "0");
				}
				sprintf(mc, "%d", (int)(dn%base));
				stra = strcat(stra, mc);
				return;
			}
		}
		sprintf(mc, "%d", (int)(dn%base));
		stra = strcat(stra, mc);
		return;
	}
}

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);
/* USER CODE BEGIN PFP */
#define SPI2_CS_L   HAL_GPIO_WritePin(GPIOB, SPI2_CS_Pin, GPIO_PIN_RESET)
#define SPI2_CS_H   HAL_GPIO_WritePin(GPIOB, SPI2_CS_Pin, GPIO_PIN_SET)

uint8_t SPI2_DATA[2];

uint8_t KTH7823_READ_REG(uint8_t addr)
{
    uint16_t td = (0x01<<14) | ((addr&0x3F)<<8) | 0x0000;
    uint16_t rd;

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	PY_Delay_us_t(1);

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	return (uint8_t)(rd>>8);
}

uint8_t KTH7823_WRITE_REG(uint8_t addr, uint8_t data)
{
    uint16_t td = (0x02<<14) | ((addr&0x3F)<<8) | data;
    uint16_t rd;

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	PY_Delay_us_t(200000); //At least 20ms delay


	td =  0x0000;
	rd = 0;

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	return (uint8_t)(rd>>8);
}

uint16_t KTH7823_READ_ANGLE(void)
{
    uint16_t td = 0x0000;
    uint16_t rd;

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	PY_Delay_us_t(1);

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;

	return rd;
}

void KTH7823_MODE_LOCK(void)
{
    uint16_t td = 0xE802;
    uint16_t rd;

	SPI2_CS_L;
	HAL_SPI_TransmitReceive(&hspi2, &td, &rd, 1, 2700);
	SPI2_CS_H;
}

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t ucmd[8];

char mychar[50];
char str0[80];
char * str1;


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_USB_DEVICE_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
  PY_semiusDelayTest();
  PY_semiusDelayOptimize();


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(ucmd[0] == 0x01)
	  {
		  ucmd[0] = 0;

		  uint8_t rst = KTH7823_READ_REG(ucmd[1]);

		  CDC_Transmit_FS((uint8_t *)(&rst), 1);

	  }
	  else if(ucmd[0] == 0x02)
	  {
		  ucmd[0] = 0;

		  uint8_t rst = KTH7823_WRITE_REG(ucmd[1], ucmd[2]);

		  if(rst==ucmd[2]) CDC_Transmit_FS((uint8_t *)"\r\nOK\r\n", strlen("\r\nOK\r\n"));
		  else CDC_Transmit_FS((uint8_t *)"\r\nFailure\r\n", strlen("\r\nFailure\r\n"));

	  }
	  else if(ucmd[0] == 0x03)
	  {
		  ucmd[0] = 0;

		  KTH7823_READ_ANGLE();
		  PY_Delay_us_t(1);
		  uint16_t rst =  KTH7823_READ_ANGLE();

		  CDC_Transmit_FS((uint8_t *)(&rst), 2);

	  }
	  else if(ucmd[0] == 0x04)
	  {
		  ucmd[0] = 0;

		  KTH7823_READ_ANGLE();
		  PY_Delay_us_t(1);
		  uint16_t rst =  KTH7823_READ_ANGLE();


		  float spi_angle_f = ((float)rst/65536)*360.0;

          py_f2s4printf(mychar, spi_angle_f, 2);
          sprintf(str0, "\r\nCurrent Degree = %s °\r\n", mychar);
          CDC_Transmit_FS((uint8_t *)str0, strlen(str0));

	  }
	  else if(ucmd[0] == 0x05)
	  {
		  ucmd[0] = 0;

		  KTH7823_MODE_LOCK();

		  CDC_Transmit_FS((uint8_t *)"\r\nOK\r\n", strlen("\r\nOK\r\n"));

	  }
	  else PY_Delay_us_t(10);


    /* 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_SCALE2);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  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 = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  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_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != 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_16BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  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};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

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

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin : SPI2_CS_Pin */
  GPIO_InitStruct.Pin = SPI2_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(SPI2_CS_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

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

其中,USB VCOM打印输出的浮点转字符串函数参考:《STM32 UART串口printf函数应用及浮点打印代码空间节省 (HAL)》

STM32工程代码测试

通过串口工具连接并进行测试:


按照约90度转动电机轴

STM32工程代码下载

STM32F401 SPI访问磁编码器KTH7823例程(STM32CUBEIDE开发平台)

相关推荐
国科安芯11 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao13 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉16 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者17 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间18 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious18 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng1 天前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计1 天前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32