前言:
MT6701磁编码器有多种读取方式,但是一般使用较多的时SSI和IIC两种读取方式,博主这里使用的是IIC驱动方式。软硬件的配置如下,附加源代码,希望能帮助更多的人。
本文推荐两篇博客:
STM32 HAL库 驱动 MT6701 磁编码器_mt6701 程序-CSDN博客
MT6701磁编码器使用指南,14Bit单圈绝对值,I2C stm32 HAL库读角度,兼容AS5600_Mark_md的博客-CSDN博客
第一篇提供了使用HAL库自带的IIC读取数据(博主没成功,可能和硬件设置有关,并且博主的项目其他IIC也是使用手写的IIC就没再研究)
第二篇详细的讲解了MT6701芯片,代码也很明确,(但是博主依旧没有跑起来......不知道为什么)
博主毕业从事嵌入式开发还未满一年也算萌新,代码或者博客有什么不足的欢迎大家指正,代码遇到问题可以给我留言,博主尽力帮助大家。祝各位CV愉快~~
一、硬件说明
博主在网上购买的成品芯片,详见下图
二、CV前须知
MT6701有两个可调地址,默认是0000110(0x06),可将地址改为1000110(0x46)代码中的.h文件已经将两个地址做了宏定义,但是并未撰写修改地址的代码,后期会更新帖子的。
三、CubeMX配置
设置时钟源
设置烧录器
设置时钟
设置USART
设置GPIO
四、附带代码请查阅
1、MT6701.h
#ifndef __MT6701_H
#define __MT6701_H
#include "main.h"
#include "Delay.h"
/*=========================地址信息=========================*/
#define Low_Address 0X0c //00001100默认低地址
#define High_Address 0X8c //10001100可调高地址
#define ReadAddress1 0X03 //数据高位寄存器地址
#define ReadAddress2 0X04 //数据低位寄存器地址
double MT6701_RecvData(GPIO_TypeDef *sda_GPIO, uint16_t sda_Pin, GPIO_TypeDef *scl_GPIO, uint16_t scl_Pin, uint8_t Number);
/*-----------------函数内部需要的支持函数------------------*/
void MT6701_Start(void); //开始信号
void MT6701_Stop(void); //结束信号
void GPIO_Write(void); //设置SDA为输出模式
void GPIO_Read(void); //设置SDA为输入模式
void MT6701_SendACK(int ack); //发送ACK信号
int MT6701_RecvACK(void); //读取ACK信号
uint8_t MT6701_RecvByte(void); //读取一个字节的数据
void MT6701_SendByte(uint8_t dat); //写入一个字节的数据
#endif
2、MT6701.C
#include "MT6701.h"
/*=====================全局变量部分======================*/
GPIO_InitTypeDef GPIO_InitStruct; //修改GPIO用的结构体
/*------------记录初始化函数传来的两线管脚信息----------*/
GPIO_TypeDef *SDA_GPIO = NULL; //sda总线数据
uint16_t SDA_Pin; //sda管脚数据
GPIO_TypeDef *SCL_GPIO = NULL; //scl总线数据
uint16_t SCL_Pin; //scl管脚数据
uint8_t Address = Low_Address; //BH1750地址数据,默认为低电平
/*======================读取MT6701编码器数据====================*/
/* 参数列表: */
/* GPIO_TypeDef *sda_GPIO 为SDA总线信息 */
/* uint16_t sda_Pin 为SDA管脚信息 */
/* GPIO_TypeDef *scl_GPIO 为SCL总线信息 */
/* uint16_t scl_Pin 为SCL管脚信息 */
/* uint8_t Number 为地址信息,0为低地址1为高地址*/
/*==============================================================*/
double MT6701_RecvData(GPIO_TypeDef *sda_GPIO, uint16_t sda_Pin, GPIO_TypeDef *scl_GPIO, uint16_t scl_Pin, uint8_t Number)
{
uint8_t RecvData = 0;
uint32_t MT6701_Data = 0;
/*---------------将数据存入到全局变量方便移植----------------*/
SDA_GPIO = sda_GPIO;
SDA_Pin = sda_Pin;
SCL_GPIO = scl_GPIO;
SCL_Pin = scl_Pin;
if(Number == 1)
Address = High_Address;
else if(Number == 0)
Address = Low_Address;
/*------------------------开始读取数据------------------------*/
/*-----------------------第一次读取数据-----------------------*/
MT6701_Start(); //启动信号
MT6701_SendByte(Address); //发送地址与写指令
MT6701_SendByte(ReadAddress1); //发送指令读取0x03寄存器
MT6701_Start(); //发送第二个启动信号开始读数
MT6701_SendByte(Address | 1); //发送地址与读命令
RecvData = MT6701_RecvByte(); //读取0x03寄存器的数据
MT6701_Stop(); //停止信号
MT6701_Data = RecvData; //将数据赋值
/*-----------------------第二次读取数据-----------------------*/
MT6701_Start(); //启动信号
MT6701_SendByte(Address); //发送地址与写指令
MT6701_SendByte(ReadAddress2); //发送指令读取0x04寄存器
MT6701_Start(); //发送第二个启动信号开始读数
MT6701_SendByte(Address | 1); //发送地址与读命令
RecvData = MT6701_RecvByte(); //读取0x04寄存器的数据
MT6701_Stop(); //停止信号
MT6701_Data = (MT6701_Data << 8) + RecvData;
MT6701_Data >>= 2; //取数据高14位
return (MT6701_Data*360.0)/16384.0;
}
/*========================功能函数:开始信号======================*/
/*开始信号为SCL高时SDA由高拉低至少250nm,SCL跟着拉低 */
/*===============================================================*/
void MT6701_Start()
{
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET); //拉高数据线
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
//在HAL库的初始阶段设置两个GPIO为输出模式高电平,这里为了保险再次设置;
My_Delay_us(10);
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin ,GPIO_PIN_RESET); //产生下降沿
My_Delay_us(10);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(10);
}
/*=======================功能函数:停止信号======================*/
/*停止信号为SCL高时SDA由低拉高至少250nm的动作 */
/*==============================================================*/
void MT6701_Stop()
{
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET); //拉低数据线
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(10);
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin ,GPIO_PIN_SET); //产生上升沿
My_Delay_us(10);
}
/*==========================发送应答信号=========================*/
/* 参数列表:ack (0:ACK 1:NAK) */
/*==============================================================*/
void MT6701_SendACK(int ack)
{
GPIO_Write();
if(ack == 1) //写应答信号
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET);
else if(ack == 0)
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET);
else
return;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET);
My_Delay_us(10);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET);
My_Delay_us(10);
}
/*=======================接收应答信号========================*/
/* 这里没有起到任何作用,没有ACK照样跑~~ */
/*==========================================================*/
int MT6701_RecvACK()
{
int ACK = 0;
GPIO_Read();
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(10);
if(HAL_GPIO_ReadPin(SDA_GPIO, SDA_Pin) == 1 ) //读应答信号
ACK = 1 ;
else
ACK = 0 ;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(10);
GPIO_Write();
return ACK;
}
/*=================向iic总线发送一个字节数据==================*/
/* 参数列表:dat——想要发送的数据 */
/*===========================================================*/
void MT6701_SendByte(uint8_t dat)
{
uint8_t i;
for (i=0; i<8; i++) //8位计数器
{
if( 0X80 & dat )
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET);
else
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET);
dat <<= 1;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(10);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(10);
}
MT6701_RecvACK();
}
/*===================从iic总线读取一个字节地址================*/
/* 返回值为读取到的一个字节数据 */
/*===========================================================*/
uint8_t MT6701_RecvByte()
{
uint8_t i;
uint8_t dat = 0;
uint8_t bit;
GPIO_Read();
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(10);
if( SET == HAL_GPIO_ReadPin(SDA_GPIO, SDA_Pin) )
bit = 0X01;
else
bit = 0x00;
dat |= bit; //读数据
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(10);
}
MT6701_SendACK(1); //函数内部有GPIO_Write();
return dat;
}
/*======================将SDA设置为输出模式====================*/
void GPIO_Write(void)
{
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SDA_GPIO, &GPIO_InitStruct);
}
/*======================将SDA设置为输入模式====================*/
void GPIO_Read(void)
{
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SDA_GPIO, &GPIO_InitStruct);
}
3、Delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "main.h"
void My_Delay_us_init(uint8_t SYSCLK);
void My_Delay_us(uint32_t nus);
void My_Delay_ms(uint32_t nms);
#endif
4、Delay.c
#include "Delay.h"
uint32_t fac_us;
/*==================延时标定函数====================*/
/* 参数uint8_t SYSCLK是系统时钟 */
/*==================================================*/
void My_Delay_us_init(uint8_t SYSCLK)
{
fac_us=SYSCLK;
}
/*==================纳秒级别的Delay==================*/
void My_Delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD;
ticks=nus*fac_us;
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
}
};
}
/*==================毫秒级别的Delay==================*/
void My_Delay_ms(uint32_t nms)
{
while(nms--)
{
My_Delay_us(1000);
}
}
5、Main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 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.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#include "MT6701.h"
#include "Delay.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/*==================定义串口收发函数数组大小================*/
#define USART1_Rx_Data_Number 1
#define USART1_Tx_Data_Number 1
uint8_t USART1_Tx_Data[USART1_Tx_Data_Number];
uint8_t USART1_Rx_Data [USART1_Rx_Data_Number];
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/*======================打印输出重定向======================*/
/*-----------------请配合#include"stdio.h"食用---------------*/
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch};
HAL_UART_Transmit(&huart1, temp, 1, 2);
return ch;
}
/*-----------------别忘了打开USE MicroLIB库-----------------*/
/*====================usart1接收中断函数==================*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, USART1_Rx_Data, USART1_Rx_Data_Number, 10);
}
HAL_UART_Receive_IT (&huart1, USART1_Rx_Data , USART1_Rx_Data_Number);//重启接收中断
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
double MT6701_Data = 0.0; //用于接收磁编码器
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
My_Delay_us_init(72); //Delay函数初始化
/* 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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
MT6701_Data = MT6701_RecvData(My_I2C_SDA_GPIO_Port, My_I2C_SDA_Pin, My_I2C_SCL_GPIO_Port, My_I2C_SCL_Pin, 0);
printf("%.2f\n", MT6701_Data);
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);
/* 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};
/** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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();
}
}
/* 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 */