资讯详情

资讯详情

DSP28335通过SPIA配置AD2S1210采集旋变位置的完整方案

简介面向电机控制与嵌入式开发者的资源包聚焦于DSP28335通过SPIA模块配置AD2S1210旋变解码芯片、实时采集电机位置信息的关键流程。压缩包共1个文件为docx格式文档大小仅1.79MB内容浓缩但完整便于直接查阅。目前已有1053人学习是旋变解码调试中值得参考的实战笔记。文档从SPI外设时钟使能、GPIO初始化、移位时钟极性设置讲起随后给出SPI_Byte收发函数、ad2s1210_Init复位函数、读写函数与A0/A1模式切换实现并通过寄存器配置示例说明激励频率、分辨率、DOS/LOS阈值等参数的设定方法配合时序细节帮助减少初期排错时间。从业者可据此快速搭建位置反馈链路特别适合入门旋变解码及开展DSP28335电机控制项目。1. 为什么伺服驱动里这组组合绕不开DSP28335 通过 SPIA 模块配置 AD2S1210 采集位置信息在伺服驱动、机械臂关节和机床主轴这类场景里旋变作为位置反馈元件比光电编码器更耐振动、抗油污、适应宽温度范围但它输出的是正余弦模拟信号必须经过一颗旋变数字转换器才能变成 MCU 能读的位置值。DSP28335 通过 SPIA 模块配置 AD2S1210 采集位置信息就是典型的“MCU 主 SPI RDC 从机”方案DSP 负责发控制字、收解算结果AD2S1210 负责把 SIN/COS 模拟量实时转成 16 位绝对角度。这个方法最大的价值在于占用 IO 少、数据实时性好、角度分辨率可调到 16 位适合正在做位置环或者想把老设备编码器升级成旋变的工程师。我第一次在没有参考板的情况下硬啃卡了快一周最后问题出在最容易被忽视的 CS 时序和 SPI 时钟相位上。这篇文章把从选型到排查的完整路径写清楚照着做能少走不少弯路。2. AD2S1210 到底怎么被 SPI 驱动寄存器、控制字和模式选型2.1 先读懂位置数据从哪来寄存器映射与分辨率选择AD2S1210 的本质是把旋变次级的 SIN/COS 两路模拟信号做 Sigma-Delta 解算然后输出数字位置或速度。用 SPI 模式时DSP 是主机AD2S1210 是从机每次访问都是“8 位控制字 可选数据字节”的帧结构。芯片内部有 8 个寄存器其中和位置采集最相关的就是位置数据寄存器。根据数据手册的寄存器映射表和公开例程常见地址分配如下寄存器地址寄存器名称读/写作用0x00控制寄存器读/写分辨率、工作模式、相位输出选择0x01故障配置寄存器读/写配置 DOS/LOT 报警条件0x02故障寄存器读读取过压、失速等故障状态0x03位置数据高字节读当前位置码 MSB0x04位置数据低字节读当前位置码 LSB0x05速度数据高字节读当前速度值 MSB0x06速度数据低字节读当前速度值 LSB分辨率在控制寄存器里配置可选 10/12/14/16 位。我的习惯是直接用到 16 位这样一圈的量化是 65536 个码对应角度分辨率约 0.0055 度。16 位模式下位置寄存器 MSB 和 LSB 拼起来就是完整位置码如果是 12 位或 14 位低字节的低几位无效读取后做右移处理即可。读位置数据的控制字在大量公开例程里是固定的读 0x03 发 0x83读 0x04 发 0x84。控制字最高位为读/写标志低几位放寄存器地址。要特别注意写配置寄存器时控制字不是简单地 0x70 加地址那种直觉写法不同批次手册对控制字中间字段的定义有差异。所以我的建议是读位置用 0x83/0x84 这套通用命令先跑通写配置字时务必对照你手上那版数据手册的 SPI Control Word 表格核对不要照着网上旧代码直接抄。2.2 DSP28335 的 SPIA 为什么能直接挂时钟极性和波特率公式DSP28335 片上 SPI 模块有 SPIA 和 SPIB 两路标题锁定了 SPIA。SPIA 的引脚映射到 GPIO16 到 GPIO19分别对应 SPISIMOA、SPISOMIA、SPICLKA 和 SPISTEA。这颗芯片的 SPI 初始化有几个关键点字符长度、主从模式、时钟极性和时钟相位。AD2S1210 的 SPI 从机时序典型是 SCLK 空闲为高、数据在下降沿被移出、在上升沿被采样对应 SPI Mode 3也就是 CLKPOL1、CLKPHA1。如果你把时钟相位配成 Mode 0 或 Mode 1DSP 发送的控制字和接收的位置数据会在错误边沿上被采样表现出来就是角度值乱跳或者 MSB 错位。这个问题后面避坑章节还会专门展开。波特率方面DSP28335 的 SPI 时钟来自 LSPCLK而 LSPCLK 又是 SYSCLKOUT 经过 LOSPCP 分频得到的。常见配置下系统时钟 90MHzLSPCLK 默认等于 SYSCLKOUT/4。SPI 波特率公式是SCLK LSPCLK / (SPIBRR 1)但有一个特殊规则当 SPIBRR 等于 0、1、2 时实际分频系数固定为 4。不少人在这一步翻车以为自己配了 90MHz 分频实际跑出来的频率和预期差一倍。SPIBRR 写入值实际分频系数当 LSPCLK25MHz 时的 SCLK0 / 1 / 246.25MHz346.25MHz11122.08MHz24251MHz49500.5MHzAD2S1210 的 SPI 时钟上限一般标到 10MHz但实际做板子时考虑到走线长度、连接器接触电阻和噪声我一般把 SCLK 压在 1 到 2MHz。不要一味追求高波特率位置数据在低速下读得更稳。2.3 不用并口而用 SPIA 的三个理由AD2S1210 本身也支持并行接口但在 DSP28335 这种封装上并行口会占用大量 GPIO而且和控制逻辑混在一起很容易出线序错误。用 SPIA 的第一个理由就是省 IO四根 SPI 线加一根手动 CS总共五根线上位。第二个理由是初始化简单。并行接口需要配置 A0/A1 地址线、片选、读/写、使能等一整套时序对时序要求高SPI 模式下这些逻辑都被串行协议消化了DSP 端只需要配好 SPICCR、SPICTL 和 SPIBRR 三个主要寄存器。第三个理由和实时性有关。SPI 读位置数据的单次访问只需要两个字节时钟周期在 SCLK 2MHz 的情况下一次完整的位置快照MSB 加 LSB大概在 4 微秒左右完成这个延迟对电流环和速度环叠加来说是完全可以接受的。如果你用并行口省下的时间微乎其微却换来了成倍的接线和配置复杂度不划算。3. 动手配置 DSP28335 的 SPIA 并读出角度3.1 硬件连接与引脚分配硬件连接是整个方案的地基。DSP28335 的 SPIA 引脚和 AD2S1210 的对应关系以及我建议的 GPIO 分配如下AD2S1210 引脚DSP28335 引脚说明SCLKGPIO18 / SPICLKASPI 时钟AD2S1210 作为从机接收SDIGPIO16 / SPISIMOADSP 发送控制字和数据进来SDOGPIO17 / SPISOMIAAD2S1210 返回数据给 DSP/CSGPIO20普通 GPIO手动控制整帧期间保持低电平RESETGPIO21普通 GPIO上电复位控制可选SAMPLEGPIO22普通 GPIO采样触发可用 PWM 或保持默认这里最需要强调的就是 /CS 的选择。DSP28335 的硬件 SPISTEA 引脚可以自动控制片选但它会在每个字符传输结束后释放 CS而 AD2S1210 要求 CS 在整个“控制字数据”的帧内持续拉低。所以不要用 SPISTEA改用普通 GPIO 手动拉低拉高。GPIO20 在默认情况下是通用输入输出引脚不需要额外复用配置直接设成输出即可。RESET 和 SAMPLE 不是必须的。RESET 可以在上电后由 DSP 拉一下做可靠复位SAMPLE 如果信号质量要求高可以用 PWM 模块定时触发采样保持普通应用不接或者固定在有效电平也能跑。EXC 激励输出和 SIN/COS 输入端关系到模拟信号质量这部分的坑我在第四章展开。3.2 SPI-A 初始化代码与每一行参数的意图下面这段代码是 SPIA 作为主机、8 位字符、SPI Mode 3、查询方式的初始化。代码按照 DSP28335 寄存器级操作书写方便你对官方头文件里的结构体定义。// spi_a_init.c #include DSP2833x_Device.h // 手动CS使用GPIO20, 避免SPISTEA自动控制导致的帧中断 #define CS_AD2S1210_LOW() GpioDataRegs.GPACLEAR.bit.GPIO20 1 #define CS_AD2S1210_HIGH() GpioDataRegs.GPASET.bit.GPIO20 1 void spi_a_init_ad2s1210(void) { // 1. 引脚复用GPIO16SPISIMOA, GPIO17SPISOMIA, GPIO18SPICLKA GpioCtrlRegs.GPAMUX2.bit.GPIO16 1; GpioCtrlRegs.GPAMUX2.bit.GPIO17 1; GpioCtrlRegs.GPAMUX2.bit.GPIO18 1; // 2. GPIO20作为普通GPIO, 方向为输出, 初始拉高 GpioCtrlRegs.GPAMUX2.bit.GPIO20 0; GpioCtrlRegs.GPADIR.bit.GPIO20 1; CS_AD2S1210_HIGH(); // 3. 软件复位期间完成全部配置 SpiaRegs.SPICCR.bit.SPISWRESET 0; // 4. CLKPOL1, 8位字符长度 SpiaRegs.SPICCR.all 0x0047; // 5. 主机模式, CLKPHA1, 对应SPI Mode 3 SpiaRegs.SPICTL.all 0x0006; // 6. 波特率LSPCLK25MHz时, 25/(111)≈2.08MHz SpiaRegs.SPIBRR 0x000B; // 7. 释放软件复位, 使能SPI SpiaRegs.SPICCR.bit.SPISWRESET 1; }第 4 步的 0x0047 是十六进制写法二进制对应 0100 0111bit6 是 CLKPOL置 1 表示 SCLK 空闲为高低 4 位 0x7 表示字符长度为 8 位。第 5 步的 0x0006 二进制是 0000 0110bit1 是 MASTER 主从选择位置 1 表示 DSP 作为主机bit2 是 CLKPHA置 1 表示数据在 SCLK 第二个边沿被采样。合起来就是标准 SPI Mode 3。第 6 步的 SPIBRR 值要特别说明这只是一种典型配置。如果你的 LSPCLK 不是 25MHz请用公式 SCLK LSPCLK / (SPIBRR 1) 重新计算。可以先查一下工程里 SysCtrlRegs 的 LOSPCP 分频配置确认 LSPCLK 实际值再回头定 SPIBRR。我曾在一台默认配置的设备上按 25MHz 算好分频实际跑起来发现 LSPCLK 是 11.25MHz导致初始化和读取全部超时。3.3 读取位置数据的完整函数一帧 CS 周期内完成 MSBLSB读取位置数据最关键的一点是MSB 和 LSB 必须在一个 CS 周期内连续读完否则旋变在转动时两次读到的位置可能相差几个 LSB拼接出来的角度会抖动。我的做法是 CS 拉低后连续发四字节前两个字节是读 MSB 的命令和数据后两个字节是读 LSB 的命令和数据。// ad2s1210_position.c // 单字节SPI收发, 主机模式下发一个字节同时收到一个字节 Uint16 spi_a_xfer(Uint16 dat) { SpiaRegs.SPITXBUF dat; // 写入发送缓冲, 启动传输 while (SpiaRegs.SPISTS.bit.INT_FLAG 0) {} // 等待接收完成标志 return SpiaRegs.SPIRXBUF; // 读接收缓冲, 读后自动清标志 } // 一次CS低电平内完成位置快照: 读MSB(0x83) 读LSB(0x84) Uint16 ad2s1210_read_position_snapshot(void) { Uint16 msb 0; Uint16 lsb 0; CS_AD2S1210_LOW(); spi_a_xfer(0x83); // 控制字: 读位置数据高字节寄存器 msb spi_a_xfer(0x00); // 第二个字节时钟, 从机返回MSB spi_a_xfer(0x84); // 控制字: 读位置数据低字节寄存器 lsb spi_a_xfer(0x00); // 第四个字节时钟, 从机返回LSB CS_AD2S1210_HIGH(); DELAY_US(1); // 帧间隔不小于1us return (msb 8) | lsb; // 16位位置码 }第一次调用 spi_a_xfer 发送 0x83 时接收到的数据是从机对控制字的回显或者无效字节直接丢弃第二次调用发送 0x00SPI 时钟继续工作此时 AD2S1210 把 MSB 放到数据线上DSP 从 SPIRXBUF 读到的才是有效 MSB。后续读 LSB 同理。这就是 SPI 全双工的特性每发一个字节同时收一个字节收发是配对的。关于防护如果初始化代码里开了 SPI FIFO查询标志位的位置会发生变化上面的 while 循环可能永远等不到 INT_FLAG。我的建议是传统查询方式下不要开 FIFO或者开了 FIFO 就改用 SPIFFRX 的 RXFFST 标志判断。另一个容易踩的细节是 SPITXBUF 和 SPIDAT 的区别直接写 SPITXBUF 就可以启动发送不需要再操作 SPIDAT有些旧例程里的双缓冲写法在这里会多一次无效操作。4. 配置与调参避坑最常见的几个翻车现场4.1 时钟相位配反角度读出来是乱的现象初始化完成后读取的角度在固定位置附近飘或者数值完全不随旋变转动变化用示波器抓 SDO 线上确实有波形但数据就是不对。原因SPI Mode 配错了。AD2S1210 的典型时序要求 SCLK 空闲为高、在上升沿采样数据。如果 SPICTL 里的 CLKPHA 没有置 1DSP 会在下降沿采样而 AD2S1210 在下降沿正好在切换数据采样点落在数据变化过程中读回来的位自然错乱。解决把 SPICCR.bit.CLKPOL 置 1SPICTL.bit.CLKPHA 置 1组合成 Mode 3。这个配置最好在初始化时一次到位不要跑起来以后再热切换热切换会让当前 SPI 状态机处在不确定状态。判断相位是否正确还有一个土办法让旋变停在某个固定位置连续读 100 次角度如果每次读回的值都一致说明采样点基本没问题如果出现偶发跳变先怀疑时钟相位而不是波特率。4.2 CS 被 SPISTEA 接管整帧被拆断现象单独读 MSB 或单独读 LSB 都能得到看起来合理的数据但把两个字节合起来以后角度完全不连续或者某一次读操作返回值全部是 0xFF。原因AD2S1210 要求 CS 在整个读帧内保持低电平但 DSP28335 的硬件 SPISTEA 在 8 位字符模式下每个字节传输结束后会自动拉高 CS导致从机把一次多字节访问当成多次独立访问数据帧被拆散。解决CS 不用 SPISTEA改用普通 GPIO 手动控制。整帧期间保持低电平帧结束拉高再给至少 1 微秒的帧间隔。同样道理写配置寄存器时也不能依赖硬件 CS。这个坑的隐蔽之处在于如果接线刚好把 SPISTEA 接到了 AD2S1210 的 /CS单字节读写可能一切正常多字节读写就开始出问题特别容易被误判成 SPI 频率太高。4.3 SCLK 分频算错偶发错码现象低速转动时角度正常高速转动或长线传输时偶尔出现一个大跳变跳变幅度正好是几千个 LSB而且不是每次都出现。原因SCLK 实际频率超出预期。DSP28335 的 SPIBRR 在值等于 0、1、2 时分频系数并不是 SPIBRR1而是固定为 4。如果 LSPCLK 又是 90MHz/4 得到的 22.5MHz那 SPIBRR2 时 SCLK 就是 5.625MHz接近 AD2S1210 的 SPI 上限在走线稍长、边沿不够陡的情况下就会偶发采样错误。解决先用示波器或者逻辑分析仪实测 SPICLKA 引脚上的 SCLK 频率确认和预期一致。再保守一点把 SCLK 降到 1MHz 左右也就是 SPIBRR 取 24LSPCLK25MHz 时。对位置读取来说1MHz 下的单次位置快照也就 4 微秒完全够用。不要为了“看起来快”去压波特率位置环真正需要的是稳定不是极限吞吐。4.4 上电后第一次读取全是 0xFFFF现象DSP 上电后立刻执行初始化加读取第一次读回来全 0xFFFF延时几百毫秒后再读就正常了之后一直正常。原因AD2S1210 上电后内部需要一个建立过程包括复位释放、激励信号稳定、内部解算链路自检。如果读取发生在这个过程完成之前数据寄存器还没准备好读回来的就是无效值。解决初始化代码在配置完 SPI 寄存器之后先给 AD2S1210 的 RESET 引脚一个明确的复位脉冲然后延时 2 到 5 毫秒再去读位置。即使不复位至少也要在首次读取前做一次延时。这个延时可以放在主程序初始化阶段不算浪费。如果你的系统有故障检测还可以在首次读取后检查返回值是否是 0xFFFF是的话做一次重新初始化并重读。4.5 SIN/COS 信号幅度不对角度在特定扇区跳变现象读取的角度整体趋势正确但在 0 度、90 度、180 度、270 度附近出现固定的小阶跃幅度在几十个 LSB 左右像是模拟信号到了边界后被“掰”了一下。原因AD2S1210 解算依赖 SIN/COS 两路信号的幅度和正交性。如果旋变激励 EXC 输出带载能力不足或者 SIN/COS 输入路径上的电阻分压不匹配、接线过长引入干扰解算器在象限切换点附近会失稳表现出来就是固定位置附近的周期性跳变。解决EXC 激励输出端加一级同相缓冲电路增强驱动能力SIN/SINLO、COS/COSLO 按数据手册推荐的输入阻抗匹配接入走线尽量短并做成差分对。另外检查供电纹波AD2S1210 的模拟供电和基准电压对纹波敏感开关电源输出直接供电时建议加一级 LDO 加电容滤波。这个坑排查最耗时因为它被 SPI 层完全掩盖读出来的角度看起来“好像没问题”但闭合位置环后就会在特定位置抖动经验不足时很容易去调环路参数而不是查模拟前端。5. 验证方法、零位校准和滤波技巧5.1 用已知角度对拖验证位置数据线性度SPI 通讯通了不代表位置数据是准的。我的验证方法是用一个已知精度的光电编码器机械上通过联轴器和旋变同轴安装缓慢转动转轴同时记录 AD2S1210 读数和编码器读数转一整圈后看差值曲线。正常情况是两者差值在几个 LSB 以内平滑变化如果差值出现周期性尖峰优先回到第四章的模拟前端检查。这个验证不用一次做到精确量化先把“转一圈回来起始位置读数一致”作为基本判定标准再做逐点的线性度对比。如果读到的值不是单调递增而是出现了非单调区间说明 SIN/COS 信号已经有明显失真或接线有短路这种情况下做任何闭环调试都是浪费时间的。5.2 零位校准与一阶滤波把原始码变成稳定角度旋变安装后机械零位和 DSP 读到的位置码零点通常不重合。我的做法是在设备装配完成后把旋变转到机械零位执行一次校准把当前读数存为偏移量。后续角度计算就是原始码减偏移量再换算成角度同时叠加一阶低通滤波消除最后一点噪声。// position_smoothing.c static float pos_offset 0.0f; static float pos_filtered 0.0f; // 机械零位校准时调用一次 void ad2s1210_zero_calibrate(void) { pos_offset (float)ad2s1210_read_position_snapshot(); } // 返回滤波后的角度, 单位: 度 float ad2s1210_get_angle_deg(void) { float raw (float)ad2s1210_read_position_snapshot(); float deg (raw - pos_offset) / 65536.0f * 360.0f; // 一阶低通, 系数0.1约对应1ms时间常数, 按需调整 pos_filtered 0.1f * (deg - pos_filtered); return pos_filtered; }注意这里的减法运算没有做跨零回绕处理。如果旋变工作在连续多圈的场景原始码从 65535 回 0 时raw 减去 offset 会得到一个接近正负 360 度的跳变。机械范围不超过一圈时无所谓超过一圈就得自己维护一个多圈累计变量或者判断差值绝对值大于 180 度时做加减 360 度的修正。滤波系数从 0.1 起步试如果位置环动态要求高可以关掉滤波直接读原始值如果只是做角度显示或慢速定位滤波可以开大一点。我在这套方案上的最后习惯是先把原始码通过串口打印出来观察一整个运动周期确认无跳变后再把数据给控制环不要还没验证直接进闭环。这是我反复强调也真实犯过的教训SPI 层的数据问题症状永远先出现在控制环上而你往往会先去调 PID 参数浪费一整天。先保证读数是干净的再做别的。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →