HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据

news2024/11/13 10:56:04

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据


  • 📚MT6816相关资料(来自商家的相关资料):
资料:https://pan.baidu.com/s/1CAbdLBRi2dmL4D7cFve1XA?pwd=8888 
提取码:8888
  • 📍驱动代码编写,可信源参考:
    https://github.com/simplefoc/Arduino-FOC-drivers/blob/master/src/encoders/mt6816/MT6816.cpp
    https://github.com/unlir/XDrive/blob/master/Firmware_APP/Base_Drivers/mt6816.c
📗MT6816所有型号和功能描述差异表

在这里插入图片描述

  • 从上图型号描述上看,差异在于ABZ模式下,径向磁铁旋转一圈,正交编码(A、B线)输出的脉冲数( ABZ输出分辨率)差异,对于Z线来讲,径向磁铁旋转一圈,都只产生一个脉冲信号,该脉冲信号的脉宽有所不同,读取角度的精度还是一样的。
  • 如果选择SPI作为通讯,选择基础款:MT6816CT-STD-R就可以了。
  • 如果ABZ模式下使用,推荐MT6816CT-ACD/ACE相比于AKD/AKH,一圈内,分辨率多了24个脉冲信号,正交编码信号相对变窄了一些。
  • 具体的其他型号的用途和场景使用,没有研究不做讨论。
    在这里插入图片描述

🛠输出模式配置说明

MT6816可以输出ABZ、UVW和PWM信号,另外还可以通过4线或3线SPI接口读取14位的绝对角度寄存器。其中ABZ、UVW和SPI接口是互相复用I/O引脚的。SPI接口和ABZ/UVW之间是通过HVPP引脚进行配置的,当HVPP接高电平VDD时,相关I/O管脚切换至SPI模式;当HVPP接地时,芯片相关I/O切换至ABZ或UVW模式。ABZ和UVW模式的切换,由芯片内部相关寄存器控制。4线SPI和3线SPI也是通过芯片内部寄存器进行切换控制的,MT6816出厂默认配置为4线SPI.
  • 👉ABZ、UVW和PWM信号读取数据和SPI方式读取数据区别在于芯片的第2引脚,如果接VCC就是SPI方式,如果接GND就是ABZ、UVW和PWM方式读取。
  • 🔧ABZ, UVW 和PWM模式参考电路:
    在这里插入图片描述

🍁MT6816 SPI参考电路

  • 🌿SPI方式参考电路:
    在这里插入图片描述

在这里插入图片描述

  • ⚡在手册中,没有提到mosi上拉,在实际测试过程中,如果外部没有配置上拉或者mcu驱动引脚(mosi)内部没有开启上拉,则读取不到数据,这一点需要注意,这个坑,排查了很久才发现。✨推荐在硬件电路设计时,在mosi引脚(芯片第5引脚)外部添加一个4.7K的上拉电阻。
    在这里插入图片描述
  • 👉对于较老的STM32F103系列,在配置软件上没有该配置选项,需要使用外部上拉电阻。

🧬MT6816 SPI时序

MT6816的SPI使用模式3(CPOL=1, CPHA=1)传输数据。如图-16所示,数据传输开始于CSN的下降沿,结束于CSN的上升沿,MT6816在时钟上升沿采样数据。
  • 时序图:
    在这里插入图片描述
📄4线SPI协议
  • 🌿4线SPI协议时序
    在这里插入图片描述
MT6816的CSN下降沿激活SPI通信,CSN的上升沿结束SPI通信。SCK时钟信号由上位机发送给MT6816,在非通信状态下,请保持SCK为高电平;MOSI (上位机输出、MT6816输入) 和MISO (上位机输入、MT6816输出) 是SPI接口的两路数据信号, 数据都是在时钟信号SCK的下降沿发生改变,所以推荐使用SCK时钟信号的上升沿对数据进行采样.
  • 比特 0: 读写标志位,。低电平为写操作,此时数据DI7~DI0写入芯片;高电平为读操作,此时从芯
    片读出数据DO7~DO0 。
  • 比特 1-7: 地址A6~A0。 寄存器操作地址。
  • 比特 8-15: 数据 DI7~DI0 (写模式)。会被写入芯片的数据 (MSB优先)。
  • 比特 8-15: 数据DO7~DO0 (读模式)。 从芯片读出的数据(MSB优先)。
📑3线SPI协议
  • 3线SPI时序
    在这里插入图片描述
CSN的下降沿激活SPI通信,CSN的上升沿结束SPI通信。SCK时钟信号由上位机发送给MT6816,在非通信状态下,请保持SCK为高电平。SDA是数据输入输出信号数据,是在时钟信号SCK的下降沿发生改变,所以推荐使用SCK时钟信号的上升沿对数据进行采样 。
  • Bit 0: 读写标志位,。低电平为写操作,此时数据DI7~DI0写入芯片;高电平为读操作,此时从芯
    片读出数据DO7~DO0 。
  • Bit 1-7: 地址A6~A0。 寄存器操作地址。
  • Bit 8-15: 数据 D7~D0 (写模式)。会被写入芯片的数据 (MSB优先)。
  • Bit 8-15: 数据D7~D0 (读模式)。 从芯片读出的数据(MSB优先)。
📗SPI读取角度数据(4线SPI)
  • 🌿4线SPI读取角度数据
    在这里插入图片描述
  • 📐角度数据寄存器
    在这里插入图片描述
    在这里插入图片描述
  • 🌿3线SPI读角度时序:
    在这里插入图片描述

📝SPI/ABZ/PWM驱动代码

  • ✨本驱动代码从https://github.com/unlir/XDrive项目中,移植有关MT6816内容,作为单独驱动读取MT6816磁编码器数据使用。
  • 🌿mt6816.c
/******
	************************************************************************
	******
	** @project : XDrive_Step
	** @brief   : Stepper motor with multi-function interface and closed loop function.
	** @brief   : 具有多功能接口和闭环功能的步进电机
	** @author  : unlir (知不知啊)
	** @contacts: QQ.1354077136
	******
	** @address : https://github.com/unlir/XDrive
	******
	************************************************************************
	******
	** {Stepper motor with multi-function interface and closed loop function.}
	** Copyright (c) {2020}  {unlir(知不知啊)}
	**
	** This program is free software: you can redistribute it and/or modify
	** it under the terms of the GNU General Public License as published by
	** the Free Software Foundation, either version 3 of the License, or
	** (at your option) any later version.
	**
	** This program is distributed in the hope that it will be useful,
	** but WITHOUT ANY WARRANTY; without even the implied warranty of
	** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	** GNU General Public License for more details.
	**
	** You should have received a copy of the GNU General Public License
	** along with this program.  If not, see <http://www.gnu.org/licenses/>.
	******
	************************************************************************
******/


#include "mt6816.h"


//GPIO输出
#define MT6816_SPI_CS_H()		(MT6816_SPI_CS_GPIO_Port -> BSRR = MT6816_SPI_CS_Pin)
#define MT6816_SPI_CS_L()		(MT6816_SPI_CS_GPIO_Port -> BRR  = MT6816_SPI_CS_Pin)

#if (MT6816_Mode == MT6816_Mode_PWM)
/****************************** MT6816_PWM ******************************/
/****************************** MT6816_PWM ******************************/
/****************************** MT6816_PWM ******************************/
MT6816_PWM_Signal_Typedef mt6816_pwm;

/**
  * @brief  TIM_MT6816_PWM初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_TIM_MT6816_PWM_Init(void)
{
	/* GPIO初始化 */
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*GPIO Ports Clock Enable*/
	MT6816_PWM_GPIO_CLK_ENABLE();		//启用PWM端口时钟
	/*Configure GPIO pin*/
	GPIO_InitStruct.Pin = MT6816_PWM_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;	//输入模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;			//禁用上下拉
	HAL_GPIO_Init(MT6816_PWM_GPIO_Port, &GPIO_InitStruct);

	/* 定时器初始化 */
	TIM_SlaveConfigTypeDef sSlaveConfig = {0};
	TIM_MasterConfigTypeDef sMasterConfig = {0};
	TIM_IC_InitTypeDef sConfigIC = {0};
	MT6816_PWM_TIM_CLK_ENABLE();		//启用TIM时钟
	MT6816_PWM_Get_HTIM.Instance = MT6816_PWM_Get_TIM;
	MT6816_PWM_Get_HTIM.Init.Prescaler = (9 - 1);																	//预分频:9		采样频率 72M/9 = 8M   分辨精度125ns
	MT6816_PWM_Get_HTIM.Init.CounterMode = TIM_COUNTERMODE_UP;										//向上计数
	MT6816_PWM_Get_HTIM.Init.Period = (65536 - 1);																//采样宽度 65536 * 125ns = 8.192ms  频率122.07Hz (满足MT6816两种PWM频率输出)
	MT6816_PWM_Get_HTIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;							//不分频
	MT6816_PWM_Get_HTIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;	//禁用自动重新加载
	if (HAL_TIM_Base_Init(&MT6816_PWM_Get_HTIM) != HAL_OK) {
		Error_Handler();
	}
	if (HAL_TIM_IC_Init(&MT6816_PWM_Get_HTIM) != HAL_OK) {
		Error_Handler();
	}
	sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;											//从模式:复位模式
	sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;												//从模式触发信号:TI1FP1
	sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;		//从模式触发极性:上升沿触发
	sSlaveConfig.TriggerFilter = 0;																		//禁用滤波器
	if (HAL_TIM_SlaveConfigSynchro(&MT6816_PWM_Get_HTIM, &sSlaveConfig) != HAL_OK) {
		Error_Handler();
	}
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;						//主模式:复位
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;	//禁用主机模式
	if (HAL_TIMEx_MasterConfigSynchronization(&MT6816_PWM_Get_HTIM, &sMasterConfig) != HAL_OK) {
		Error_Handler();
	}
	sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;   //上升沿捕获
	sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;					//TI1FP1
	sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;										//不分频
	sConfigIC.ICFilter = 0;
	if (HAL_TIM_IC_ConfigChannel(&MT6816_PWM_Get_HTIM, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) {
		Error_Handler();
	}
	sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;	//下降沿捕获
	sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;				//TI1FP2
	if (HAL_TIM_IC_ConfigChannel(&MT6816_PWM_Get_HTIM, &sConfigIC, TIM_CHANNEL_2) != HAL_OK) {
		Error_Handler();
	}
	/*interrupt init*/
	HAL_NVIC_EnableIRQ(MT6816_PWM_Get_IRQn);		//使能定时中断
	/*begin work*/
	HAL_TIM_Base_Stop(&MT6816_PWM_Get_HTIM);									//停用TIM
	HAL_TIM_OC_Start_IT(&MT6816_PWM_Get_HTIM, TIM_CHANNEL_1);	//启动CH捕获比较中断
	HAL_TIM_OC_Start_IT(&MT6816_PWM_Get_HTIM, TIM_CHANNEL_2);	//启动CH捕获比较中断
	HAL_TIM_Base_Start_IT(&MT6816_PWM_Get_HTIM);							//启动TIM中断模式
}
/**
  * @brief  MT6816_PWM采集初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_PWM_Signal_Init(void)
{
	//采集数据
	mt6816_pwm.h_width = 0;
	mt6816_pwm.period = 0;
	mt6816_pwm.count_rising = 0;
	mt6816_pwm.count_falling = 0;
	mt6816_pwm.count_update = 0;
	mt6816_pwm.whole_h_flag = false;
	mt6816_pwm.whole_l_flag = false;
	mt6816_pwm.ready_once = false;
	mt6816_pwm.ready_again = false;
	//输出数据
	mt6816_pwm.valid_width = 0;

	//配置外设
	REIN_TIM_MT6816_PWM_Init();
}

/**
  * @brief  MT6816_PWM采集中断回调
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_PWM_TIM_Callback(void)
{
	//定时器捕获中断通道1 (上升沿中断) (必须在更新中断前执行)
	if (__HAL_TIM_GET_FLAG(&MT6816_PWM_Get_HTIM, TIM_FLAG_CC1) != RESET)
	{
		if (__HAL_TIM_GET_IT_SOURCE(&MT6816_PWM_Get_HTIM, TIM_IT_CC1) != RESET)
		{
			__HAL_TIM_CLEAR_IT(&MT6816_PWM_Get_HTIM, TIM_IT_CC1);
			MT6816_PWM_Get_HTIM.Channel = HAL_TIM_ACTIVE_CHANNEL_1;

			//采集上升沿数据
			mt6816_pwm.period = HAL_TIM_ReadCapturedValue(&MT6816_PWM_Get_HTIM, TIM_CHANNEL_1) + 1;		//获取PWM周期
			mt6816_pwm.count_rising = mt6816_pwm.count_update;																				//更新计数器镜像
		}
	}
	//定时器捕获中断通道2 (下降沿中断)
	if (__HAL_TIM_GET_FLAG(&MT6816_PWM_Get_HTIM, TIM_FLAG_CC2) != RESET)
	{
		if (__HAL_TIM_GET_IT_SOURCE(&MT6816_PWM_Get_HTIM, TIM_IT_CC2) != RESET)
		{
			__HAL_TIM_CLEAR_IT(&MT6816_PWM_Get_HTIM, TIM_IT_CC2);
			MT6816_PWM_Get_HTIM.Channel = HAL_TIM_ACTIVE_CHANNEL_2;

			//采集下降沿数据
			mt6816_pwm.h_width = HAL_TIM_ReadCapturedValue(&MT6816_PWM_Get_HTIM, TIM_CHANNEL_2) + 1;	//获取PWM高宽度
			mt6816_pwm.count_falling = mt6816_pwm.count_update;																				//更新计数器镜像
		}
	}
	//定时器更新中断 (更新事件中断) (由更新事件触发 | 溢出事件触发)
	if (__HAL_TIM_GET_FLAG(&MT6816_PWM_Get_HTIM, TIM_FLAG_UPDATE) != RESET)
	{
		if (__HAL_TIM_GET_IT_SOURCE(&MT6816_PWM_Get_HTIM, TIM_IT_UPDATE) != RESET)
		{
			__HAL_TIM_CLEAR_IT(&MT6816_PWM_Get_HTIM, TIM_IT_UPDATE);

			//单次PWM全高全低检测
			if (	(mt6816_pwm.count_update != mt6816_pwm.count_rising)	//(上升沿计数器镜像,更新计数器不相等)
			        && (mt6816_pwm.count_update != mt6816_pwm.count_falling)	//(下降沿计数器镜像,更新计数器不相等)
			   ) {
				//读取PWM电平用于判定全高或全低
				if (MT6816_PWM_GPIO_Port -> IDR & MT6816_PWM_Pin) {
					mt6816_pwm.whole_h_flag = true;		//置位100%标志
					mt6816_pwm.whole_l_flag = false;	//清位0%标志
				}
				else {
					mt6816_pwm.whole_h_flag = false;	//清位100%标志
					mt6816_pwm.whole_l_flag = true;		//置位0%标志
				}
			}
			else {
				mt6816_pwm.count_update ++;
				mt6816_pwm.whole_h_flag = false;		//清位100%标志
				mt6816_pwm.whole_l_flag = false;		//清位0%标志
			}

			//单次PWM有效性确认
			if (0) {}
			else if ((mt6816_pwm.whole_h_flag))	mt6816_pwm.ready_once = false;	//100%_PWM无效
			else if ((mt6816_pwm.whole_l_flag))	mt6816_pwm.ready_once = false;	//0%_PWM无效
			//脉冲超长检测(无需检测)
			//脉冲超短检测(无需检测)
			else																mt6816_pwm.ready_once = true;

			//可靠的PWM有效性确认
			if (mt6816_pwm.ready_once) {
				if (mt6816_pwm.ready_again) {
					mt6816_pwm.valid_width = mt6816_pwm.h_width;
				}
				else {
					mt6816_pwm.valid_width = 0;
					mt6816_pwm.ready_again = true;
				}
			}
			else {
				mt6816_pwm.valid_width = 0;
				mt6816_pwm.ready_again = false;
			}
		}
	}
}

/**
  * @brief  MT6816_PWM获取角度数据
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_PWM_Get_AngleData(void)
{
	//输出数据
	int32_t out = (mt6816_pwm.valid_width << 1) - 64;
	if (out < 0)
		mt6816_pwm.angle = 0;
	else if (out > 16383)
		mt6816_pwm.angle = 16383;
	else
		mt6816_pwm.angle = out;
}

#elif (MT6816_Mode == MT6816_Mode_ABZ)
/****************************** MT6816_ABZ ******************************/
/**
  * @brief  TIM_MT6816_ABZ初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_TIM_MT6816_ABZ_Init(void)
{
	/* GPIO初始化 */
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*GPIO Ports Clock Enable*/
	MT6816_ABZ_A_GPIO_CLK_ENABLE();		//启用ABZ_A端口时钟
	MT6816_ABZ_B_GPIO_CLK_ENABLE();		//启用ABZ_B端口时钟
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_ABZ_A_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;	//输入模式
	GPIO_InitStruct.Pull = GPIO_NOPULL;			//禁用上下拉
	HAL_GPIO_Init(MT6816_ABZ_A_GPIO_Port, &GPIO_InitStruct);
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_ABZ_B_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;	//输入模式
	GPIO_InitStruct.Pull = GPIO_NOPULL;			//禁用上下拉
	HAL_GPIO_Init(MT6816_ABZ_B_GPIO_Port, &GPIO_InitStruct);

	/* TIM初始化 */
	TIM_Encoder_InitTypeDef sConfig = {0};
	TIM_MasterConfigTypeDef sMasterConfig = {0};
	__HAL_RCC_TIM1_CLK_ENABLE();
	MT6816_ABZ_Get_HTIM.Instance = MT6816_ABZ_Get_TIM;
	MT6816_ABZ_Get_HTIM.Init.Prescaler = 0;																				//无预分频
	MT6816_ABZ_Get_HTIM.Init.CounterMode = TIM_COUNTERMODE_UP;										//向上计数
	MT6816_ABZ_Get_HTIM.Init.Period = (4096 - 1);																	//12位周期
	MT6816_ABZ_Get_HTIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;							//不分频
	MT6816_ABZ_Get_HTIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;	//禁用自动重新加载
	sConfig.EncoderMode = TIM_ENCODERMODE_TI12;				//双边沿四倍频采样
	sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;			//上升沿计数
	sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;	//TI1FP1
	sConfig.IC1Prescaler = TIM_ICPSC_DIV1;						//不分频
	sConfig.IC1Filter = 0;														//禁用滤波器
	sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;			//下降沿计数
	sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;	//TF2FP2
	sConfig.IC2Prescaler = TIM_ICPSC_DIV1;						//不分频
	sConfig.IC2Filter = 0;														//禁用滤波器
	if (HAL_TIM_Encoder_Init(&MT6816_ABZ_Get_HTIM, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;							//主机模式:复位
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;		//禁用主机模式
	if (HAL_TIMEx_MasterConfigSynchronization(&MT6816_ABZ_Get_HTIM, &sMasterConfig) != HAL_OK)
	{
		Error_Handler();
	}
	/*begin work*/
	HAL_TIM_Base_Stop(&MT6816_ABZ_Get_HTIM);
	HAL_TIM_Encoder_Start(&MT6816_ABZ_Get_HTIM, TIM_CHANNEL_ALL);
}
/****************************** MT6816_ABZ GPIO初始化******************************/
/**
  * @brief  GPIO初始化(MT6816_ABZ)
  * @param  NULL
  * @retval NULL
*/
void REIN_GPIO_MT6816_ABZ_Init(void)
{
#if (defined SENSOR_HVPP_CLK_ENABLE) || (defined MT6816_ABZ_Z_GPIO_CLK_ENABLE)
	GPIO_InitTypeDef GPIO_InitStruct = {0};
#endif

#ifdef SENSOR_HVPP_CLK_ENABLE
	/* GPIO Ports Clock Enable */
	SENSOR_HVPP_CLK_ENABLE();						//启用MT6816_HVPP端口时钟
	/*Configure GPIO pin Output Level*/
	SENSOR_HVPP_GPIO_Port -> BRR = SENSOR_HVPP_Pin;  //MT6816_HVPP引脚输出低电平,启动PWM+ABZ模式
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = SENSOR_HVPP_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;		//推挽输出
	GPIO_InitStruct.Pull = GPIO_NOPULL;						//禁用上下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;	//低速
	HAL_GPIO_Init(SENSOR_HVPP_GPIO_Port, &GPIO_InitStruct);
#endif

#ifdef MT6816_ABZ_Z_GPIO_CLK_ENABLE
	/* GPIO Ports Clock Enable */
	MT6816_ABZ_Z_GPIO_CLK_ENABLE();			//启用MT6816_ABZ_Z端口时钟
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_ABZ_Z_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;		//上升沿触发中断
	GPIO_InitStruct.Pull = GPIO_NOPULL;						//禁用上下拉
	HAL_GPIO_Init(MT6816_ABZ_Z_GPIO_Port, &GPIO_InitStruct);
	/* EXTI interrupt init*/
	HAL_NVIC_EnableIRQ(MT6816_ABZ_Z_EXTI_IRQn);		//启用MT6816_Z中断
#endif
}

/****************************** MT6816_ABZ ******************************/
MT6816_ABZ_Signal_Typedef	mt6816_abz;

/**
  * @brief  MT6816_ABZ采集初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_ABZ_Signal_Init(void)
{
	//采集数据
	mt6816_abz.sample_data = 0;
	//输出数据
	mt6816_abz.angle = 0;

	//配置外设
	REIN_TIM_MT6816_ABZ_Init();
	REIN_GPIO_MT6816_ABZ_Init();
}

/**
  * @brief  MT6816_ABZ采集Z脉冲中断回调
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_ABZ_ZPulse_Callback(void)
{
	//清除定时器计数值
	__HAL_TIM_SET_COUNTER(&MT6816_ABZ_Get_HTIM, 0);
}

/**
  * @brief  MT6816_ABZ获取角度数据
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_ABZ_Get_AngleData(void)
{
	//采集数据
	mt6816_abz.sample_data = __HAL_TIM_GET_COUNTER(&MT6816_ABZ_Get_HTIM) & 0x0FFF;	//读取定时器计数值
	//输出数据
	mt6816_abz.angle = ((mt6816_abz.sample_data + 1) << 2) - 1;		//获得数值0~16383
}

#elif (MT6816_Mode == MT6816_Mode_SPI)
/****************************** MT6816_SPI ******************************/
/****************************** MT6816_SPI GPIO初始化******************************/
/**
  * @brief  GPIO初始化(MT6816_SPI)
  * @param  NULL
  * @retval NULL
*/
void REIN_GPIO_MT6816_SPI_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

#ifdef SENSOR_HVPP_CLK_ENABLE
	/* GPIO Ports Clock Enable */
	SENSOR_HVPP_CLK_ENABLE();						//启用MT6816_HVPP端口时钟
	/*Configure GPIO pin Output Level*/
	SENSOR_HVPP_GPIO_Port -> BSRR = SENSOR_HVPP_Pin;			//MT6816_HVPP引脚输出高电平,启动PWM+SPI模式
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = SENSOR_HVPP_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;		//推挽输出
	GPIO_InitStruct.Pull = GPIO_NOPULL;						//禁用上下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;	//低速
	HAL_GPIO_Init(SENSOR_HVPP_GPIO_Port, &GPIO_InitStruct);
#endif

	/* GPIO Ports Clock Enable */
	MT6816_SPI_CS_GPIO_CLK_ENABLE();		//启用MT6816_SPI_CS端口时钟
	/*Configure GPIO pin Output Level*/
	MT6816_SPI_CS_GPIO_Port -> BSRR = MT6816_SPI_CS_Pin;	//CS引脚启动输出高电平
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_SPI_CS_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;		//推挽输出
	GPIO_InitStruct.Pull = GPIO_NOPULL;						//禁用上下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;	//低速
	HAL_GPIO_Init(MT6816_SPI_CS_GPIO_Port, &GPIO_InitStruct);
}

/****************************** MT6816_SPI 接口初始化******************************/
/**
	* @brief  SPI初始化(MT6816)
	* @param  NULL
	* @retval NULL
**/
void REIN_SPI_MT6816_SPI_Init(void)
{
	/* AFIO初始化 */
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*GPIO Ports Clock Enable*/
	MT6816_SPI_CLK_GPIO_CLK_ENABLE();		//启用CLK端口时钟
	MT6816_SPI_MOSI_GPIO_CLK_ENABLE();	//启用MOSI端口时钟
	MT6816_SPI_MISO_GPIO_CLK_ENABLE();	//启用MISO端口时钟
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_SPI_CLK_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;					//复用推挽
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;		//高速
	HAL_GPIO_Init(MT6816_SPI_CLK_GPIO_Port, &GPIO_InitStruct);
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_SPI_MOSI_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;					//复用推挽
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;		//高速
	HAL_GPIO_Init(MT6816_SPI_MOSI_GPIO_Port, &GPIO_InitStruct);
	/*Configure GPIO pins*/
	GPIO_InitStruct.Pin = MT6816_SPI_MISO_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;					//输入
	GPIO_InitStruct.Pull = GPIO_NOPULL;							//禁用上下拉
	HAL_GPIO_Init(MT6816_SPI_MISO_GPIO_Port, &GPIO_InitStruct);
	/*Configure AFIO*/
#ifdef MT6816_SPI_AFIO_REMAP
	MT6816_SPI_AFIO_REMAP				//启用备用的SPI_AFIO映射
#endif

	/* SPI初始化 */
	MT6816_SPI_SPI_CLK_ENABLE();          //启用SPI时钟
	MT6816_SPI_Get_HSPI.Instance = MT6816_SPI_Get_SPI;
	MT6816_SPI_Get_HSPI.Init.Mode = SPI_MODE_MASTER;												//主机模式
	MT6816_SPI_Get_HSPI.Init.Direction = SPI_DIRECTION_2LINES;							//双向传输
	MT6816_SPI_Get_HSPI.Init.DataSize = SPI_DATASIZE_16BIT;									//数据位宽:16
	MT6816_SPI_Get_HSPI.Init.CLKPolarity = SPI_POLARITY_HIGH;								//CLK空闲时高电平
	MT6816_SPI_Get_HSPI.Init.CLKPhase = SPI_PHASE_2EDGE;										//第二边沿采样
	MT6816_SPI_Get_HSPI.Init.NSS = SPI_NSS_SOFT;														//软件NSS
	MT6816_SPI_Get_HSPI.Init.BaudRatePrescaler = MT6816_SPI_Prescaler;			//输入预设分频
	MT6816_SPI_Get_HSPI.Init.FirstBit = SPI_FIRSTBIT_MSB;										//MSB传输
	MT6816_SPI_Get_HSPI.Init.TIMode = SPI_TIMODE_DISABLE;										//禁用中断模式
	MT6816_SPI_Get_HSPI.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;		//禁用CRC校验
	MT6816_SPI_Get_HSPI.Init.CRCPolynomial = 10;														//CRC校验多项式
	if (HAL_SPI_Init(&MT6816_SPI_Get_HSPI) != HAL_OK)
	{
		Error_Handler();
	}
}
MT6816_SPI_Signal_Typedef	mt6816_spi;

/**
  * @brief  MT6816_SPI采集初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_SPI_Signal_Init(void)
{
	//采集数据
	mt6816_spi.sample_data = 0;
	//输出数据
	mt6816_spi.angle = 0;

	//配置外设
	REIN_GPIO_MT6816_SPI_Init();
	REIN_SPI_MT6816_SPI_Init();
}

/**
  * @brief  MT6816_SPI采集获取角度数据
  * @param  NULL
  * @retval NULL
**/
void RINE_MT6816_SPI_Get_AngleData(void)
{
	uint16_t data_t[2];
	uint16_t data_r[2];
	uint8_t h_count;

	data_t[0] = (0x80 | 0x03) << 8;
	data_t[1] = (0x80 | 0x04) << 8;

	for (uint8_t i = 0; i < 3; i++) {
		//读取SPI数据
		MT6816_SPI_CS_L();
		HAL_SPI_TransmitReceive(&MT6816_SPI_Get_HSPI, (uint8_t*)&data_t[0], (uint8_t*)&data_r[0], 1, HAL_MAX_DELAY);
		MT6816_SPI_CS_H();
		MT6816_SPI_CS_L();
		HAL_SPI_TransmitReceive(&MT6816_SPI_Get_HSPI, (uint8_t*)&data_t[1], (uint8_t*)&data_r[1], 1, HAL_MAX_DELAY);
		MT6816_SPI_CS_H();
		mt6816_spi.sample_data = ((data_r[0] & 0x00FF) << 8) | (data_r[1] & 0x00FF);

		//奇偶校验
		h_count = 0;
		for (uint8_t j = 0; j < 16; j++) {
			if (mt6816_spi.sample_data & (0x0001 << j))
				h_count++;
		}
		if (h_count & 0x01) {
			mt6816_spi.pc_flag = false;
		}
		else {
			mt6816_spi.pc_flag = true;
			break;
		}
	}

	if (mt6816_spi.pc_flag) {
		mt6816_spi.angle = mt6816_spi.sample_data >> 2;
		mt6816_spi.no_mag_flag = (bool)(mt6816_spi.sample_data & (0x0001 << 1));
	}
}

#endif

/****************************** MT6816 ******************************/
/****************************** MT6816 ******************************/
/****************************** MT6816 ******************************/
MT6816_Typedef	mt6816;

/**
  * @brief  MT6816初始化
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_Init(void)
{
#ifndef MT6816_Mode
#error "NO define MT6816_Mode"
#endif
#if   (MT6816_Mode == MT6816_Mode_PWM)
	REIN_MT6816_PWM_Signal_Init();		//初始化PWM接口
#elif (MT6816_Mode == MT6816_Mode_SPI)
	REIN_MT6816_SPI_Signal_Init();		//初始化SPI接口
#elif (MT6816_Mode == MT6816_Mode_ABZ)
	REIN_MT6816_ABZ_Signal_Init();		//初始化ABZ接口
#else
#error "MT6816_Mode Invalid !!!"
#endif

	//初始化阶段获取一次角度数据(过滤错误数据)(暂未查明复位后第一次读取数据丢失的原因)
	REIN_MT6816_Get_AngleData();

}

/**
  * @brief  MT6816获取角度数据
  * @param  NULL
  * @retval NULL
**/
void REIN_MT6816_Get_AngleData(void)
{
#ifndef MT6816_Mode
#error "NO define MT6816_Mode"
#endif
#if   (MT6816_Mode == MT6816_Mode_PWM)
	REIN_MT6816_PWM_Get_AngleData();	//MT6816_PWM获取角度数据
	mt6816.angle_data = mt6816_pwm.angle;
	mt6816.angle = 360*mt6816.angle_data/16384.00f; //转换后的角度0 - 360
#elif (MT6816_Mode == MT6816_Mode_SPI)
	RINE_MT6816_SPI_Get_AngleData();
	mt6816.angle_data = mt6816_spi.angle;
	mt6816.angle = 360*mt6816.angle_data/16384.00f; //转换后的角度0 - 360
#elif (MT6816_Mode == MT6816_Mode_ABZ)
	REIN_MT6816_ABZ_Get_AngleData();	//MT6816_ABZ获取角度数据

#else
#error "MT6816_Mode Invalid !!!"
#endif
}

#if (MT6816_Mode == MT6816_Mode_PWM)
/**
  * @brief TIM3_IRQHandler
**/
void TIM3_IRQHandler(void)
{
	//TIM3的中断标志清除由PWM处理函数识别并清除
	REIN_MT6816_PWM_TIM_Callback();	//MT6816_PWM采集中断回调
}
#endif

#if (MT6816_Mode == MT6816_Mode_ABZ)
/**
  * @brief EXTI15_10_IRQHandler
**/
void EXTI15_10_IRQHandler(void)
{
  if (__HAL_GPIO_EXTI_GET_IT(MT6816_ABZ_Z_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(MT6816_ABZ_Z_Pin);
		REIN_MT6816_ABZ_ZPulse_Callback();	//编码器器Z脉冲中断
  }
}
#endif


  • 🌿mt6816.h
/******
	************************************************************************
	******
	** @project : XDrive_Step
	** @brief   : Stepper motor with multi-function interface and closed loop function.
	** @brief   : 具有多功能接口和闭环功能的步进电机
	** @author  : unlir (知不知啊)
	** @contacts: QQ.1354077136
	******
	** @address : https://github.com/unlir/XDrive
	******
	************************************************************************
	******
	** {Stepper motor with multi-function interface and closed loop function.}
	** Copyright (c) {2020}  {unlir(知不知啊)}
	**
	** This program is free software: you can redistribute it and/or modify
	** it under the terms of the GNU General Public License as published by
	** the Free Software Foundation, either version 3 of the License, or
	** (at your option) any later version.
	**
	** This program is distributed in the hope that it will be useful,
	** but WITHOUT ANY WARRANTY; without even the implied warranty of
	** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	** GNU General Public License for more details.
	**
	** You should have received a copy of the GNU General Public License
	** along with this program.  If not, see <http://www.gnu.org/licenses/>.
	******
	************************************************************************
******/


#ifndef MT6816_H
#define MT6816_H

#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>	
//引用端口定义

/********** MT6816 **********/
//MT6816模式控制(GPIO)
#define SENSOR_HVPP_CLK_ENABLE()				__HAL_RCC_GPIOA_CLK_ENABLE()	//PA7
#define SENSOR_HVPP_GPIO_Port						(GPIOA)
#define SENSOR_HVPP_Pin									(GPIO_PIN_7)
//MT6816_PWM采集(AFIO & TIM)
#define MT6816_PWM_GPIO_CLK_ENABLE()		__HAL_RCC_GPIOA_CLK_ENABLE()	//PA6
#define MT6816_PWM_GPIO_Port						(GPIOA)
#define MT6816_PWM_Pin 									(GPIO_PIN_6)
#define MT6816_PWM_TIM_CLK_ENABLE()			__HAL_RCC_TIM3_CLK_ENABLE()		//TIM3
#define	MT6816_PWM_Get_TIM							(TIM3)
#define	MT6816_PWM_Get_HTIM							(htim3)
#define MT6816_PWM_Get_IRQn							(TIM3_IRQn)		//TIM3中断
//MT6816_ABZ采集(GPIO)
#define MT6816_ABZ_Z_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOA_CLK_ENABLE()	//PA10
#define MT6816_ABZ_Z_GPIO_Port					(GPIOA)
#define MT6816_ABZ_Z_Pin 								(GPIO_PIN_10)
#define MT6816_ABZ_Z_EXTI_IRQn 					(EXTI15_10_IRQn)	//EXTI15_10中断
//MT6816_ABZ采集(AFIO & TIM)
#define MT6816_ABZ_A_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOA_CLK_ENABLE()	//PA8
#define MT6816_ABZ_A_GPIO_Port					(GPIOA)
#define MT6816_ABZ_A_Pin 								(GPIO_PIN_8)
#define MT6816_ABZ_B_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOA_CLK_ENABLE()	//PA9
#define MT6816_ABZ_B_GPIO_Port					(GPIOA)
#define MT6816_ABZ_B_Pin 								(GPIO_PIN_9)
#define MT6816_ABZ_TIM_CLK_ENABLE()			__HAL_RCC_TIM1_CLK_ENABLE()		//TIM1
#define	MT6816_ABZ_Get_TIM							(TIM1)
#define	MT6816_ABZ_Get_HTIM							(htim1)
//MT6816_SPI采集(GPIO)
//#define MT6816_SPI_CS_GPIO_CLK_ENABLE()		__HAL_RCC_GPIOB_CLK_ENABLE()	//PB12
//#define MT6816_SPI_CS_GPIO_Port						(GPIOB)
//#define MT6816_SPI_CS_Pin 								(GPIO_PIN_12)
//MT6816_SPI采集(AFIO & SPI)
//#define MT6816_SPI_CLK_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB13
//#define MT6816_SPI_CLK_GPIO_Port					(GPIOB)
//#define MT6816_SPI_CLK_Pin 								(GPIO_PIN_13)
//#define MT6816_SPI_MISO_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB14
//#define MT6816_SPI_MISO_GPIO_Port					(GPIOB)
//#define MT6816_SPI_MISO_Pin 							(GPIO_PIN_14)
//#define MT6816_SPI_MOSI_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB15
//#define MT6816_SPI_MOSI_GPIO_Port					(GPIOB)
//#define MT6816_SPI_MOSI_Pin 							(GPIO_PIN_15)
//#define MT6816_SPI_SPI_CLK_ENABLE()				__HAL_RCC_SPI2_CLK_ENABLE();	//SPI2
//#define MT6816_SPI_Get_SPI								(SPI2)
//#define MT6816_SPI_Get_HSPI								(hspi2)
//#define	MT6816_SPI_AFIO_REMAP							__HAL_AFIO_REMAP_SPI2_ENABLE();	//SPI2_AFIO端口重新映射

/********** MT6816 **********/
//MT6816_SPI采集(GPIO)
#define MT6816_SPI_CS_GPIO_CLK_ENABLE()		__HAL_RCC_GPIOA_CLK_ENABLE()	//PA15
#define MT6816_SPI_CS_GPIO_Port						(GPIOA)
#define MT6816_SPI_CS_Pin 								(GPIO_PIN_15)
//MT6816_SPI采集(AFIO & SPI)
#define MT6816_SPI_CLK_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB3
#define MT6816_SPI_CLK_GPIO_Port					(GPIOB)
#define MT6816_SPI_CLK_Pin 								(GPIO_PIN_3)
#define MT6816_SPI_MISO_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB4
#define MT6816_SPI_MISO_GPIO_Port					(GPIOB)
#define MT6816_SPI_MISO_Pin 							(GPIO_PIN_4)
#define MT6816_SPI_MOSI_GPIO_CLK_ENABLE()	__HAL_RCC_GPIOB_CLK_ENABLE()	//PB5
#define MT6816_SPI_MOSI_GPIO_Port					(GPIOB)
#define MT6816_SPI_MOSI_Pin 							(GPIO_PIN_5)
#define MT6816_SPI_SPI_CLK_ENABLE()				__HAL_RCC_SPI1_CLK_ENABLE();	//SPI1
#define MT6816_SPI_Get_SPI								(SPI1)
#define MT6816_SPI_Get_HSPI								(hspi1)
#define	MT6816_SPI_AFIO_REMAP							__HAL_AFIO_REMAP_SPI1_ENABLE();	//SPI1_AFIO端口重新映射
#define	MT6816_SPI_Prescaler							(SPI_BAUDRATEPRESCALER_8)
//MT6816工作模式定义
#define MT6816_Mode_PWM		(0x01)	//MT6816工作在PWM模式		(支持该模式的硬件版本:XDrive_REIN_Basic_H1_0)
#define MT6816_Mode_ABZ		(0x02)	//MT6816工作在ABZ模式		(支持该模式的硬件版本:XDrive_REIN_Basic_H1_0)
#define MT6816_Mode_SPI		(0x03)	//MT6816工作在SPI模式		(支持该模式的硬件版本:XDrive_REIN_Basic_H1_0 / XDrive_REIN_Basic_H1_1)

//MT6816工作模式配置
#define MT6816_Mode		MT6816_Mode_PWM

#if (MT6816_Mode == MT6816_Mode_PWM)
#include "tim.h"
/********** MT6816_PWM **********/
/********** MT6816_PWM **********/
/********** MT6816_PWM **********/
typedef struct {
	//采集数据(PWM)
	uint16_t	h_width;				//PWM高宽度
	uint16_t	period;					//PWM周期
	uint8_t		count_rising;		//PWM上升沿计数器镜像
	uint8_t		count_falling;	//PWM下降沿计数器镜像
	uint8_t		count_update;		//PWM更新计数器
	bool		whole_h_flag;			//PWM全高标志
	bool		whole_l_flag;			//PWM全低标志
	bool		ready_once;				//PWM就绪标志(完成一次PWM采集后置位)
	bool		ready_again;			//PWM就绪标志(完成二次PWM采集后置位)
	uint16_t	valid_width;		//PWM有效宽度
	//输出数据(PWM)
	uint16_t	angle;
} MT6816_PWM_Signal_Typedef;

//MT6816_PWM
void REIN_MT6816_PWM_Signal_Init(void);		//MT6816_PWM采集初始化
void REIN_MT6816_PWM_TIM_Callback(void);	//MT6816_PWM采集中断回调
void REIN_MT6816_PWM_Get_AngleData(void);	//MT6816_PWM获取角度数据

#elif (MT6816_Mode == MT6816_Mode_ABZ)
/********** MT6816_ABZ **********/
/********** MT6816_ABZ **********/
/********** MT6816_ABZ **********/
typedef struct {
	//采集数据(ABZ)
	uint16_t	sample_data;	//ABZ读取到的数据
	//输出数据(ABZ)
	uint16_t	angle;				//ABZ输出的角度
} MT6816_ABZ_Signal_Typedef;

//MT6816_ABZ
void REIN_MT6816_ABZ_Signal_Init(void);			//MT6816_ABZ采集初始化
void REIN_MT6816_ABZ_ZPulse_Callback(void);	//MT6816_ABZ采集Z脉冲中断回调
void REIN_MT6816_ABZ_Get_AngleData(void);		//MT6816_ABZ获取角度数据

#elif (MT6816_Mode == MT6816_Mode_SPI)
#include "spi.h"
/********** MT6816_SPI **********/
/********** MT6816_SPI **********/
/********** MT6816_SPI **********/
typedef struct {
	//采集数据
	uint16_t	sample_data;	//SPI读取到的数据
	//输出数据
	uint16_t	angle;				//SPI输出的角度
	bool			no_mag_flag;	//磁铁数据有效标志
	bool			pc_flag;			//奇偶校验位
} MT6816_SPI_Signal_Typedef;

//MT6816_SPI
void REIN_MT6816_SPI_Signal_Init(void);		//MT6816_SPI采集初始化
void RINE_MT6816_SPI_Get_AngleData(void);	//MT6816_SPI采集获取角度数据

#endif

/********** MT6816 **********/
/********** MT6816 **********/
/********** MT6816 **********/
typedef struct {
	uint16_t	angle_data;				//角度数据
	float	angle;		//换算陈0 - 360 角度数据
//	bool			rectify_valid;		//校准数据有效标志
} MT6816_Typedef;
extern MT6816_Typedef	mt6816;

//MT6816
void REIN_MT6816_Init(void);							//MT6816初始化
void REIN_MT6816_Get_AngleData(void);			//MT6816获取角度数据

#ifdef __cplusplus
}
#endif

#endif

⛳驱动代码使用说明

  • 🔖驱动代码基于STM32F103,SPI/ABZ/PWM 3方式读取MT6816磁编码器数据做过实测验证。
  • 🌿SPI方式默认采用的是SPI1。
  • 🌿ABZ方式需要用到TIM1定时器。
  • 🌿PWM方式需要利用到TIM3定时器。
🔧STM32CubeMX工程配置说明:
  • 针对使用不同通讯方式:
  • SPI方式:使能spi1接口即可。(驱动代码中已经包含了对SPI1的初始化代码,其他方式相同,主要是为例生成驱动文件,用来包含spi.htim.h头文件)
    在这里插入图片描述

  • ABZ模式:使能定时器TIM1既可。
    在这里插入图片描述

  • PWM方式:使能定时器TIM3即可。
    在这里插入图片描述

  • 🌟生成的驱动代码,默认初始化的函数用不到,全部注释掉:(驱动代码中已经包含了相关初始化代码)
    在这里插入图片描述

  • 📑调用方式:

  REIN_MT6816_Init();//初始化MT6816
#if (MT6816_Mode == MT6816_Mode_PWM)
    HAL_NVIC_SetPriority(MT6816_PWM_Get_IRQn,	1,	0);	//定时器捕获(MT6816_PWM_PWM)获取MT6816_PWM两个边沿计数值
#endif
#if (MT6816_Mode == MT6816_Mode_ABZ)
    HAL_NVIC_SetPriority(MT6816_ABZ_Z_EXTI_IRQn,	1,	0);	//外部中断(MT6816_ABZ_Z脉冲)		矫正MT6816_ABZ信号
#endif
while(1) {
        REIN_MT6816_Get_AngleData();	//MT6816获取角度数据
        printf("angle_data:%d,angle:%.2f\r\n", mt6816.angle_data, mt6816.angle);
        HAL_Delay(1000);
    }

在这里插入图片描述

  • 🔖以下资源性链接不公开,为避免文章和资源被爬取和盗用,不再对外公布相关资源链接,如果按照上面提供的驱动代码无法独立驱动起来,而你是真正需要这份源码作为参考,用于个人学习研究,可以私信作者,其具体文章和相关资源。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1951482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++的auto关键字

一、auto关键字介绍 因为C是静态类型语言&#xff0c;即会在编译阶段进行类型检查。我们知道对象的类型决定一个对象所能参与的操作&#xff0c;因此如果类型检查过程中发现试图执行对象类型不支持的操作&#xff0c;编译器将会报错。 这要求我们在声明变量时必须指出变量的类…

php、mantis、showDoc部署

下载软件安装包:Mantis Bug Tracker 下载vs包&#xff1a;https://www.microsoft.com/en-us/download/details.aspx?id48145,解决“因为计算机中丢失msvcr110.dll”下载apache服务器:Apache VS17 binaries and modules download下载php:PHP For Windows: Binaries and source…

Kylin 入门教程

Apache Kylin 是一个开源的分布式数据仓库和 OLAP(在线分析处理)引擎,旨在提供亚秒级查询响应时间,即使在处理超大规模数据集时也是如此。Kylin 可以有效地将原始数据预计算为多维数据立方体(Cube),并利用这些预计算结果来提供快速查询。本文将带你从基础知识到操作实践…

【React】详解“最新”和“最热”切换与排序

文章目录 一、基本概念和初始化二、切换与排序功能的实现1. 函数定义和参数2. 设置活动 Tab3. 定义新列表变量4. 根据排序类型处理列表4.1 按时间降序排序4.2 按点赞数降序排序 5. 更新评论列表 三、渲染导航 Tab 和评论列表1. map 方法2. key 属性3. className 动态赋值4. onC…

五大设备制造商的 200 多种机型的安全启动功能完全失效

2012 年&#xff0c;一个由硬件和软件制造商组成的行业联盟采用了安全启动技术&#xff0c;以防范长期存在的安全威胁。这种威胁是恶意软件的幽灵&#xff0c;它可以感染 BIOS&#xff0c;即每次计算机启动时加载操作系统的固件。从那里&#xff0c;它可以保持不受检测和删除&a…

jenkins参数化构建在UI中定义脚本中使用

先看配置&#xff1a; 流水线脚本&#xff1a; pipeline {agent {//label "${server}"label "${28}"}stages {stage(Hello) {steps {echo "--------------------------"// 只有这个可以输出变量echo "${character_argument}"echo &q…

Pytorch使用教学5-视图view与reshape的区别

有同学后台留言问为什么view有时可对张量进行形变操作&#xff0c;有时就会报错&#xff1f;另外它和reshape功能好像一致&#xff0c;有什么区别呢&#xff1f;本文就带你了解PyTorch中视图的概念。 在PyTorch中对张量进行形变操作时&#xff0c;很多同学也会使用view方法&am…

kettle从入门到精通 第八十课 ETL之kettle kettle中的json对象字段写入postgresql中的json字段

场景&#xff1a;源数据库表为mysql的其中有json字段&#xff0c;通过kettle 查询出来 插入到目标数据库 postgresql中&#xff0c;对应的表中也有json字段。。但是报错&#xff0c;提示kettle查询出来是varchar的的字段&#xff0c;无法插入到目标数据库中。 1、创建测试表。 …

【VSCode实战】Golang无法跳转问题竟是如此简单

上一讲【VSCode实战】Go插件依赖无法安装 – 经云的清净小站 (skycreator.top)&#xff0c;开头说到了在VSCode中Golang无法跳转的问题&#xff0c;但文章的最后也没给出解决方案&#xff0c;只解决了安装Go插件的依赖问题。 解决了插件依赖问题&#xff0c;无法跳转的问题也离…

echo,tail ,飘号和重定向符

1. 输出指定内容 echo 语法; echo 输出的内容 较多内容使用 “ 双引号 ”&#xff0c; 相当于 printf &#xff1b; 2. 飘号 飘号&#xff0c;也就是我们通常所说的反引号&#xff0c;被飘号包括的内容会当作命令执行&#xff0c;常配合 echo 使用&#xff0c;输出结果为…

【python】python图书管理系统_普通用户+管理员菜单(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Docker安装 OpenResty详细教程

OpenResty 是一个基于 Nginx 的高性能 Web 平台&#xff0c;它集成了 Lua 脚本语言&#xff0c;使得开发者可以在 Nginx 服务器上轻松地进行动态 Web 应用开发。OpenResty 的核心目标是通过将 Nginx 的高性能与 Lua 的灵活性结合起来&#xff0c;提供一个强大且高效的 Web 开发…

IO多路复用——select

仅一个线程、进程处理并发 IO多路转接&#xff08;复用&#xff09;之select 跨平台适用linux&#xff0c;windows 底层&#xff1a;线性表 IO多路转接&#xff08;复用&#xff09;之poll 适用linux 底层&#xff1a;线性表 IO多路转接&#xff08;复用&#xff09;之epo…

微信小程序支付流程

前端需要做的事情&#xff1a; 生成平台订单&#xff1a;前端调用接口&#xff0c;向后端传递购买的商品信息、收货人信息&#xff0c;&#xff08;后端生成平台订单&#xff0c;返回订单编号&#xff09;获取预付单信息&#xff1a;将订单编号发送给后端后&#xff0c;&#x…

2024最新Selenium面试题(附带答案),建议收藏备用

一.你在TestNG中使用了哪些注解&#xff1f; TestBeforeSuiteAfterSuiteBeforeTestAfterTestBeforeClassAfterClassBeforeMethodAfterMethod 二.如何从Excel中读取数据&#xff1f; FileInputStream fs new FileInputStream(“excel文件路径”); Workbook wb WorkbookFact…

Web前端知识视频教程分享(五) Bootstrap

资料下载地址&#xff1a; https://545c.com/f/45573183-1336822373-45bb4f?p7526 (访问密码: 7526)

WordPress原创插件:自定义文章标题颜色

插件设置截图 文章编辑时&#xff0c;右边会出现一个标题颜色设置&#xff0c;可以设置为任何颜色 更新记录&#xff1a;从输入颜色css代码&#xff0c;改为颜色选择器&#xff0c;更方便&#xff01; 插件免费下载 https://download.csdn.net/download/huayula/89585192…

Xinstall揭秘:一键拉起服务如何助力App提升用户体验和下载转化率

在移动互联网时代&#xff0c;App的运营和推广显得尤为重要。而在这个过程中&#xff0c;如何提升用户体验和下载转化率成为了每个App运营者关注的焦点。今天&#xff0c;我们就来揭秘一下Xinstall的一键拉起服务&#xff0c;看看它是如何助力App提升用户体验和下载转化率的。 …

示例:WPF中如何处理TabControl页面绑定ItemsSource切换TabItem时UI数据没有持久保存的问题

一、目的&#xff1a;在WPF开发过程中&#xff0c;经常用到TabControl&#xff0c;也会遇到类似问题&#xff0c;用TabControl绑定数据源ItemsSource时&#xff0c;切换TabItem时&#xff0c;UI上的数据没有持久保存&#xff0c;本文介绍一种处理方式&#xff0c;可以做到缓存页…