STM32 SPI获取MT6816磁编码器绝对角度
一、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模式3SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC多项式,暂时用不到,给默认值7SPI_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);}}