一、MT6816简介
MT6816是一款由MagnTek推出的基于AMR原理的磁编码器,其支持ABZ&UVW&PWM&SPI四种模式。可以输出分辨率为14位的绝对角度数据;支持最高转速25000RPM;工作温度范围为-40~125℃。常应用于直流无刷电机控制、伺服电机控制,闭环步进电机控制。
其引脚定义如下:
二、MT6816外围电路
1、MT6816模块原理图
2.模式选择
SPI(3线、4线)模式:
R2用0R电阻短路,或者HVPP引脚接到3.3V。
ABZ、UVW、和PWM模式:
R3用0R电阻短路,或者HVPP接地。
3.PCB布局即注意事项
注:MT6816芯片要位于步进电机的几何中心且芯片底部不能布线。
4、磁铁的选择及安装注意事项:
使用镜像磁铁,用胶水粘在步进电机尾部的转动轴上,芯片距离磁铁建议1-3mm。
5、与STM32连接
MT6816CS接PA4; SCK接PA5; MISO接PA6; MOSI接PA7。
三、MT6816 4线SPI介绍
1、MT6816SPI时序
2.4线SPI协议
3、4线SPI读取角度:
这里我们只读取角度数据,不使用弱磁报警和奇偶校验位、超速报警。
四、代码编写
1、基本实现逻辑
基本逻辑是拉低片选,先输入0x83,写入03寄存器的地址和指令,读取03寄存器的数据,拉高片选,得到第一个数据,再次拉低片选,写入0x04寄存器的地址和指令,读取04寄存器的数据,拉高片选,重复得到05的,然后按照表格上的顺序进行运算,将03数据左移8位,加上04的数据,得到的新数据左移2位得到角度数值,除以16384,乘以角度360°得到角度值。
2、SPI底层代码编写
spi.c文件:
#include "stm32f10x.h" // Device header
/**
* 函 数:SPI写SS引脚电平,SS仍由软件模拟
* 参 数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1
* 返 回 值:无
* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平
*/
void MySPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //根据BitValue,设置SS引脚的电平
}
/**
* 函 数:SPI初始化 CS PA4; SCK PA5; MISO PA6; MOSI PA7
* 参 数:无
* 返 回 值:无
*/
void MySPI_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SPI1的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4引脚初始化为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA5和PA7引脚初始化为复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
/*SPI初始化*/
SPI_InitTypeDef SPI_InitStructure; //定义结构体变量
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,选择为SPI主模式
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据宽度,选择为8位
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,选择高位先行
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //SPI极性,选择高极性
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //SPI相位,选择第二个时钟边沿采样,极性和相位决定选择SPI模式3
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7
SPI_Init(SPI1, &SPI_InitStructure); //将结构体变量交给SPI_Init,配置SPI1
/*SPI使能*/
SPI_Cmd(SPI1, ENABLE); //使能SPI1,开始运行
/*设置默认电平*/
MySPI_W_SS(1); //SS默认高电平
}
/**
* 函 数:SPI起始
* 参 数:无
* 返 回 值:无
*/
void MySPI_Start(void)
{
MySPI_W_SS(0); //拉低SS,开始时序
}
/**
* 函 数:SPI终止
* 参 数:无
* 返 回 值:无
*/
void MySPI_Stop(void)
{
MySPI_W_SS(1); //拉高SS,终止时序
}
/**
* 函 数:SPI交换传输一个字节,使用SPI模式3
* 参 数:ByteSend 要发送的一个字节
* 返 回 值:接收的一个字节
*/
uint8_t MySPI_SwapByte(uint16_t ByteSend)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空
SPI_I2S_SendData(SPI1, ByteSend); //写入数据到发送数据寄存器,开始产生时序
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET); //等待接收数据寄存器非空
return SPI_I2S_ReceiveData(SPI1); //读取接收到的数据并返回
}
3、MT6816读取角度代码:
#include "stm32f10x.h" // Device header
#include "MySPI.h"
void MT6816_Init(void)
{
MySPI_Init();
}
float MT6816_Get_AngleData(void)
{
uint8_t Data1 = 0x00;
uint8_t Data2 = 0x00;
MySPI_Start();
MySPI_SwapByte(0x83); //向MT6816发送读取角度寄存器指令
Data1 =MySPI_SwapByte(0xFF); //将角度数据读回来
MySPI_Stop();
MySPI_Start();
MySPI_SwapByte(0x04);
Data2 =MySPI_SwapByte(0xFF);
MySPI_Stop();
float MyAngle = (((float)((Data1*256+Data2)/4)/16384)*360); //将0X03和0X04数据进行拼接并将其右移两位,保留14位角度数据,根据公式将读取到的数据转换为角度
return MyAngle;
}
4、将角度值显示到OLED屏幕
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MT6816.h"
float Angle;
int main(void)
{
OLED_Init();
MT6816_Init();
OLED_ShowString(1,1,"Angle:");
OLED_ShowChar(1,12,'.');
while (1)
{
float Angle = MT6816_Get_AngleData();
int FractionalPart = (int)((Angle - (int)Angle)* 1000);//角度小数部分计算,并将其化为整数
//float LinearDisplacement = Angle / 360 * 4;
OLED_ShowNum(1, 7, Angle, 5);
OLED_ShowNum(1, 13, FractionalPart, 3);
//OLED_ShowNum(2, 1, LinearDisplacement, 3);
}
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_64386340/article/details/139183344
|