MA732GQ-Z
带ABZ增量式和PWM输出的14位数字式非接触式角度传感器
MA732GQ-Z中文资料_最新报价_数据手册下载_MPS(芯源)-位置传感器-立创商城
图1:磁编码器原理接线图
- 磁铁贴紧芯片 → reg27=0x80(仅 Bit7=1,红灯亮)
- 磁铁拿很远 → reg27=0x40(仅 Bit6=1,绿灯亮)
- 找到黄金距离 → reg27=0x00(两灯全灭,读取正常)
1.SPI和SSI两种方式
MA732不需要软件复位
上电自动从 NVM 加载配置,立即可用
根据规格书,MA732 的 SPI 通信确实分为三种操作模式(SSI 是一种仅用于数据读取的两线同步串行接口。仅支持角度读取。无法使用 SSI 读取或写入寄存器。)
| 模式 | 说明 |
|---|---|
| 读取角度 (Read Angle) | 主机拉低 CS 即可,无需发送命令,MISO 直接输出角度数据 |
| 读取寄存器 (Read Register) | 主机需发送 16 位帧:命令010+ 5 位寄存器地址 + 8 位零 |
| 写入寄存器 (Write Register) | 主机需发送 16 位帧:命令100+ 5 位寄存器地址 + 8 位数据值 |
图2:传感器规格书SPI说明
2. 1 SPI读取角度
图3:读取角度,MOSI发全0
ma732_angle:角度值MA732 输出 14bit 角度,范围0 ~ 16383(0x3FFF)
2.2 SPI读取寄存器
需要两个连续的16-bit SPI帧(共32个时钟周期)。第一帧取回角度,第二帧取回寄存器值。
图4:读取角度需要命令和读取的寄存器地址
图5:规格书对应的寄存器对应的地址
2.3 SPI 时钟模式(2种):
Mode 0 和 Mode 3
| SPI 模式 | CPOL | CPHA | SCLK 空闲状态 | 数据捕获 | 数据发送 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平 | SCLK 上升沿 | SCLK 下降沿 |
| Mode 3 | 1 | 1 | 高电平 | SCLK 上升沿 | SCLK 下降沿 |
图6:传感器规格书SPI时钟模式
选用标准通信为SPI Mode0
- SCLK 空闲状态:低电平
- 数据在 SCLK 上升沿锁存、下降沿移位
图7:时钟和CS波形
图8:时钟和mosi波形
2.4遇见问题:
如图可见时钟电压不对
解决:通过IO模拟,增加上拉电阻等,仍没有解决,但在配置读取寄存器MOSI(需要发送命令+寄存器地址),在示波器可以看到MOSI在时钟下是有正确数据的。
读取到010 11011 (010为命令,11011是1B为寄存器27的值)。可见示波器显示的时钟线为2V,也可以正常识别到的。
图9:证明有驱动3.3.V的能力
正常 SPI 模式需要上电瞬间直接跳到接近 3.3V 高电平。
和DSP28335 连接,初始化SPIA(文章后有完整代码)
MOSI (SPISIMOA), MISO (SPISOMIA), SCLK (SPICLKA), CS(SPISTEA)
硬件连接,代码都相对简单,但在测试的时候MA732 SPI只读出0 / 65535
检查接线没有问题:
| 信号 | F28335 引脚 |
|---|---|
| MOSI (SPISIMOA) | GPIO16 |
| MISO (SPISOMIA) | GPIO17 |
| SCLK (SPICLKA) | GPIO18 |
| CS | GPIO19 |
图9:编码器和F28335接线对应图
先用SPI回环测试
图10:根据TI的例程,Expressions发送和接收数据没有问题
没放磁铁时,HL1(绿灯)亮,代表磁场弱(MGL=1),此时 A、B 输出的是非常干净、规整的 3.3V 方波。在磁场有效(两个灯都灭)时,A、B 会变成稳定的高/低电平。
2.5 SPI代码(未成功)
不知道是链路有问题,还是配置问题,角度和寄存器的值只有最大和最小值,怀疑是线路浮空或者磁铁问题,后续证明磁铁位置没有问题。
#include "DSP2833x_Device.h"
#include "DSP2833x_Spi.h"
#include "DSP2833x_Device.h"
typedef unsigned char Uint8;
//typedef unsigned int Uint16;
//typedef unsigned long Uint32;//简易延时,替代DELAY_US
#define DELAY_US(n) {Uint32 i;for(i=0;i<((Uint32)n*150U);i++);}// ============ MA732 CS引脚 GPIO19 ============
#define MA732_CS_L() GpioDataRegs.GPACLEAR.bit.GPIO19 = 1;
#define MA732_CS_H() GpioDataRegs.GPASET.bit.GPIO19 = 1;Uint16 SpiA_WriteRead16(Uint16 txData) //SPI 16 位读写、CS 宏、简易 DELAY_US
{
while(SpiaRegs.SPISTS.bit.BUFFULL_FLAG == 1);
SpiaRegs.SPITXBUF = txData;
while(SpiaRegs.SPISTS.bit.INT_FLAG == 0);
return SpiaRegs.SPIRXBUF;
}Uint8 MA732_ReadRegister(Uint8 regAddr)
{
Uint16 rxData1, rxData2;
// 读命令:010 + 5-bit地址 + 8-bit 0
Uint16 txCmd = (0x2 << 13) | ((Uint16)regAddr << 8);// ===== 第一帧 =====
MA732_CS_L(); // CS 拉低,开始通信
DELAY_US(5); // 满足 tScL > 80ns
rxData1 = SpiA_WriteRead16(txCmd); // 发送读命令,丢弃返回的 Angle
// 注意:这里不拉高 CS!// ===== 空闲等待 =====
DELAY_US(5); // 必须 > 750ns,这里给 5us 绝对够// ===== 第二帧 =====
rxData2 = SpiA_WriteRead16(0x0000); // 发送 0x0000,读取寄存器值
MA732_CS_H(); // 两帧都发完了,再拉高 CS
DELAY_US(5);// 返回高 8 位(寄存器值)
return (rxData2 >> 8) & 0x00FF;
}Uint16 MA732_ReadAngle(void)
{
Uint16 rxData;
MA732_CS_L();
DELAY_US(5);
rxData = SpiA_WriteRead16(0x0000); // 直接读,MOSI 全 0
MA732_CS_H();
DELAY_US(5);// 修正:MA732 返回 16 位,高 14 位是角度,低 2 位是 0
// F2833x SPI 接收是左对齐,所以直接返回即可
return rxData & 0xFFFC;
}void MA732_Spi_Init(void)
{
EALLOW;
// CS GPIO19
GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO19 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO19 = 1;
MA732_CS_H();// SPIA 引脚复用
GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1;
GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1;
GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1;// 方向设置
GpioCtrlRegs.GPADIR.bit.GPIO16 = 1; // MOSI 输出
GpioCtrlRegs.GPADIR.bit.GPIO17 = 0; // MISO 输入
GpioCtrlRegs.GPADIR.bit.GPIO18 = 1; // SCLK 输出// 上拉设置:SCLK 使能上拉
GpioCtrlRegs.GPAPUD.bit.GPIO16 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO17 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0;
EDIS;SpiaRegs.SPICCR.bit.SPICHAR = 15; //16-bit char bits
SpiaRegs.SPICCR.bit.SPILBK = 0;
SpiaRegs.SPICCR.bit.CLKPOLARITY = 0;
SpiaRegs.SPICCR.bit.SPISWRESET = 0;SpiaRegs.SPICTL.bit.SPIINTENA = 0; //Disable SPI INT
SpiaRegs.SPICTL.bit.TALK = 1; //Enable TX
SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; //Mater
SpiaRegs.SPICTL.bit.CLK_PHASE = 1;
SpiaRegs.SPICTL.bit.OVERRUNINTENA = 0;//禁用溢出中断SpiaRegs.SPIBRR = 36; // SPI Baud Rate = LSPCLK/(36+1)=37.5MHz/37=1MHz
SpiaRegs.SPIPRI.bit.FREE = 1; // Set so breakpoints don't disturb xmission
SpiaRegs.SPICCR.bit.SPISWRESET = 1; // Relinquish SPI from Reset
}volatile Uint8 ma732_reg27_value;
volatile Uint16 ma732_angle;void main(void)
{
InitSysCtrl();
DINT;
IER = 0x0000;
IFR = 0x0000;MA732_Spi_Init();
for(;;)
{
//SpiA_WriteRead16(0x0000); // 临时测试:连续发,不要DELAY
ma732_reg27_value = MA732_ReadRegister(0x1B);
ma732_angle = MA732_ReadAngle();
DELAY_US(1000);
}}
2026.10.19
上方SPI代码没有成功读取角度值,通过示波器可以检查到CS,MOSI,SCLK都有数据,但在MISO数据只有高低
3.1 SSI通讯
SSI 是一种两线制、同步串行接口,仅用于读取数据。传感器在系统中作为外部 SSI 主机的从机工作
完整读取 = 1 个 dummy 时钟 + 16 个数据时钟 = 17 个时钟,MSB 在第 2 个时钟出现
重要提示:本传感器实现的 SSI 与标准 SSI 不同,因此可能无法与标准 SSI 器件配合工作。
本传感器的 SSI 采用 SSCK 低电平空闲,而标准协议中 SSCK 采用高电平空闲;
本传感器要求在一帧 SSI 的起始处于 SSCK 上给出一个哑上升沿(dummy rising edge),标准 SSI 无此要求。
Dummy:主机先发1 个时钟脉冲,这个脉冲不输出有效角度数据,只是用来触发编码器内部移位寄存器准备数据
MSB:Most Significant Bit,最高有效位.高位先出
下方代码以经过AI和实操认证
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"/* ---------------- 用户可调参数 ---------------- */
#define SSI_HALF_CYC 44 /* SCK 半周期: 44 CPU cycles @150MHz = 0.29us
(要求 >=0.1us; 改主频时按比例修改) */
#define SSI_DEAD_US 45 /* 帧间死区, 要求 >= 40us (t_p) */
#define SSI_POLL_US 200 /* 主循环轮询周期 200us = 5kHz */
#define SSI_MAX_STEP 0x2000 /* 相邻两帧最大合理跳变(16bit): 8192=45°,
超过记一次 glitch, 用于发现位错位/干扰 *//* ---------------- 引脚宏 ---------------- */
#define SSI_SCK_H() (GpioDataRegs.GPASET.bit.GPIO18 = 1)
#define SSI_SCK_L() (GpioDataRegs.GPACLEAR.bit.GPIO18 = 1)
#define SSI_DAT() (GpioDataRegs.GPADAT.bit.GPIO17)
#define SSI_CS_H() (GpioDataRegs.GPASET.bit.GPIO19 = 1) /* 仅兼容已接线硬件 *//* 确定延时的半周期: RPT 循环, 每次循环 1 cycle, 不受编译器影响 */
#define SSI_STR2(x) #x
#define SSI_STR(x) SSI_STR2(x)
static inline void SSI_DLY(void)
{
asm(" RPT #" SSI_STR(SSI_HALF_CYC) " || NOP");
}/* ---------------- 函数声明 ---------------- */
void SSI_GpioInit(void);
Uint16 SSI_ReadFrame(void); /* 返回 16 位原始字 (含低 2 位补 0) */
Uint16 SSI_ReadAngle(Uint16 *raw16); /* 带校验, 返回 1=本次数据可信 */
void SSI_CaptureBits(Uint16 nclk); /* 调试: 抓原始比特流 *//* ---------------- 观测变量 (Expressions 窗口直接看) ---------------- */
volatile Uint16 ssi_raw; /* 16 位原始字 = angle14 << 2 */
volatile Uint16 ssi_angle14; /* 14 位角度 0..16383 */
volatile float ssi_deg; /* 角度 0..360 度 */
volatile float ssi_speed_dps; /* 角速度 度/s (带符号) */
volatile Uint16 ssi_mgl; /* 磁场低于 MGLT 指示 (可选) */
volatile Uint16 ssi_mgh; /* 磁场高于 MGHT 指示 (可选) */volatile Uint16 ssi_align_err; /* 低 2 位不为 0 的帧数 (位错位/干扰) */
volatile Uint16 ssi_glitch_cnt; /* 相邻帧跳变超限的次数 */
volatile Uint16 ssi_ok; /* 最近一帧是否可信 1/0 */volatile Uint16 ssi_bits[34]; /* 调试: 原始比特流 */
/******************************************************************************
* GPIO 初始化
* 要点: 先把输出锁存写低, 再打开方向, 避免上电 SCK 冒出毛刺沿
* (MA732 会把第一个上升沿当 dummy, 毛刺会打乱帧对齐)
******************************************************************************/
void SSI_GpioInit(void)
{
/* 输出锁存先置初值 (不需要 EALLOW) */
SSI_SCK_L();
SSI_CS_H();EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; /* 不开这个输入永远读到 0 */
GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 0; /* SSD 普通 IO */
GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 0; /* SSCK 普通 IO */
GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 0; /* CS 普通 IO(兼容) */
GpioCtrlRegs.GPAMUX2.bit.GPIO20 = 0; /* MGL 可选 */
GpioCtrlRegs.GPAMUX2.bit.GPIO21 = 0; /* MGH 可选 */GpioCtrlRegs.GPADIR.bit.GPIO17 = 0; /* 输入 */
GpioCtrlRegs.GPADIR.bit.GPIO18 = 1; /* 输出 */
GpioCtrlRegs.GPADIR.bit.GPIO19 = 1;
GpioCtrlRegs.GPADIR.bit.GPIO20 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO21 = 0;GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0; /* SSD 上拉 */
GpioCtrlRegs.GPAPUD.bit.GPIO20 = 0;
GpioCtrlRegs.GPAPUD.bit.GPIO21 = 0;EDIS;
}/******************************************************************************
* 读一帧: 17 个时钟 (1 dummy + 16 数据), 上升沿后采样, MSB first
* 帧尾 SSCK 保持高 >= 40us (t_p), 再回空闲低
******************************************************************************/
Uint16 SSI_ReadFrame(void)
{
Uint16 v = 0;
Uint16 i;
Uint16 b;SSI_SCK_L();
SSI_DLY(); /* 空闲低保持, 保证下一沿是"起始沿" */for (i = 0; i < 17; i++)
{
SSI_SCK_H();
SSI_DLY(); /* 上升沿后 0.29us 采样, 远大于 t_ssd=15ns */
b = SSI_DAT() ? 1u : 0u;
SSI_SCK_L();
SSI_DLY(); /* 低电平 0.29us, 满足 t_ssckL >= 0.1us */if (i != 0) /* 跳过第 1 个 dummy 时钟 */
{
v = (Uint16)((v << 1) | b);
}
}SSI_SCK_H(); /* 死区: SSCK 高 >= 40us 后才允许下一帧 */
DELAY_US(SSI_DEAD_US);
SSI_SCK_L();return v;
}/******************************************************************************
* 带校验读取
* 校验 1: 16 位字低 2 位必须为 0 (14 位数据左对齐补 0), 否则判为错位帧
* 校验 2: 与上帧差值(环绕)不得超过 SSI_MAX_STEP
* 返回: 1 = 数据可信, *raw16 有效; 0 = 本帧丢弃, 沿用上帧
******************************************************************************/
Uint16 SSI_ReadAngle(Uint16 *raw16)
{
static Uint16 prev = 0;
static Uint16 have = 0;
Uint16 raw;
Uint16 ok = 1;
Uint16 diff;
int16 d;raw = SSI_ReadFrame();
/* --- 校验 1: 补 0 位 --- */
if ((raw & 0x0003u) != 0u)
{
ssi_align_err++;
ok = 0;
}/* --- 校验 2: 跳变检查 (16 位环绕差值) --- */
if (have)
{
diff = (Uint16)(raw - prev);
d = (int16)diff;
if (d > (int16)SSI_MAX_STEP || d < -(int16)SSI_MAX_STEP)
{
ssi_glitch_cnt++;
ok = 0;
}
}if (ok)
{
prev = raw;
have = 1;
*raw16 = raw;
}
else
{
*raw16 = prev; /* 不可信时沿用上一帧, 输出不跳变 */
}return ok;
}/******************************************************************************
* 调试: 抓 nclk 个原始比特 (SCK 从空闲低开始打 nclk 个脉冲, 上升沿采样)
* 用法: SSI_CaptureBits(34); 在 Expressions 里看 ssi_bits[], 数有效位数
******************************************************************************/
void SSI_CaptureBits(Uint16 nclk)
{
Uint16 i;if (nclk > 34) { nclk = 34; }
SSI_SCK_L();
SSI_DLY();for (i = 0; i < nclk; i++)
{
SSI_SCK_H();
SSI_DLY();
ssi_bits[i] = SSI_DAT() ? 1u : 0u;
SSI_SCK_L();
SSI_DLY();
}SSI_SCK_H();
DELAY_US(SSI_DEAD_US);
SSI_SCK_L();
}/******************************************************************************
* main
******************************************************************************/
void main(void)
{
Uint16 raw;
Uint16 ok;
int16 d;
static Uint16 prev_raw = 0;
static Uint16 have_prev = 0;
float dt_s;InitSysCtrl();
/* 跑 Flash 时必须打开:
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
InitFlash();
*/DINT;
IER = 0x0000;
IFR = 0x0000;SSI_GpioInit();
/* 预热: 丢掉上电后前几帧 (芯片内部滤波器稳定) */
{ Uint16 k; for (k = 0; k < 10; k++) { (void)SSI_ReadFrame(); } }dt_s = (float)SSI_POLL_US * 1e-6f;
for (;;)
{
ok = SSI_ReadAngle(&raw);ssi_raw = raw;
ssi_angle14 = (Uint16)(raw >> 2);
ssi_deg = (float)ssi_angle14 * (360.0f / 16384.0f);if (have_prev)
{
d = (int16)(Uint16)(raw - prev_raw); /* 16 位环绕差值 */
ssi_speed_dps = (float)d * (360.0f / 65536.0f) / dt_s;
}
prev_raw = raw;
have_prev = 1;ssi_ok = ok;
/* 磁场告警 (可选, 接线才有效) */
ssi_mgl = GpioDataRegs.GPADAT.bit.GPIO20;
ssi_mgh = GpioDataRegs.GPADAT.bit.GPIO21;DELAY_US(SSI_POLL_US);
}
}