AN-2614: TMC8100 支持SSI绝对式编码器协议
描述
TMC8100 内置一款可编程微控制器,针对速率高达16Mbit/s的串行同步及异步绝对式编码器协议进行了优化。它可替代专用的编码器协议接口集成电路(IC)和现场可编程门阵列(FPGA)实施方案,同时支持通过系统内更新来适配不同编码器的功能,或切换至其他编码器协议。TMC8100是一款紧凑、经济且灵活的通信解决方案,能为工业驱动器增添绝对式编码器支持功能。
本应用笔记详细介绍了TMC8100的软件参考实现方案,支持带有同步串行接口(SSI)的绝对位置编码器。
产品特性
- SSI实现方案
- 示例使用了1MHz时钟频率
- 格雷码到二进制码的转换
- 输入滤波器
- SSI时钟频率最高可达16MHz
应用
- 电机位置反馈
系统描述
伺服电机驱动器(例如在工业应用中)通常需要精准、可靠且低延迟的位置反馈。长期以来,带有增量式A/B/N 输出的光学编码器一直是行业标准。尽管如此,绝对式位置编码器正逐渐普及,这类编码器往往具备额外功能和不同的接口协议(多为厂商专有协议)。例如,SSI 协议便是用于编码器与控制器之间数字数据串行传输的一种协议。它采用两条独立的连接线路:一条用于传输控制器生成的时钟信号,另一条则用于传输编码器输出的数据。其物理层基于RS422标准,可将位置值和诊断信息从编码器传输至控制器。
本示例中使用了带SSI接口的编码器,通过一根六芯电缆TMC8100-EVAL-KIT相连(em>图1)。六芯电缆的构成如下
- +24V和GND:编码器电源和接地连接
- DATA+和DATA-:差分RS-422信号,用于从编码器向控制器传输数据
- CLK+和CLK-:差分RS-422信号,用于从控制器向编码器传输时钟
参考实现方案的特性包括:
- 生成具有所需频率、脉冲数和极性的时钟信号
- SSI编码器支持的1Mbps数据速率
- 数据的打包和解包
- 将16位编码器位置数据从格雷码转换为16位二进制码
参考实现方案以源代码形式提供。用户可在此基础上,根据应用需求进行修改。
系统概述
所提供的软件专为配合TMC8100-EVAL-KIT使用而设计,并已通过SSI的Ri360P0-QR14-ESG25X2编码器 (Turck) 进行测试。
TMC8100-EVAL-KIT 所需的核心硬件元件包括 TMC8100 芯片和两颗RS485 收发器,后者用于在TMC8100与连接器处的差分RS485信号(即DATA+和DATA-,以及CLK+和CLK-)之间进行转换 (图2)。
软件包含TMC8100的固件,实现了支持SSI协议所需的控制器功能。此固件需在设备上电后下载至TMC8100中。此外,还提供一款基于Python 脚本的图形用户界面(GUI),可用于选择和下载固件,并且之后还可配合编码器对固件功能进行演示和测试。
同步串行接口协议
SSI 协议采用由控制器生成的时钟信号,编码器将其作为触发与参考基准,向外输出位置数据及附加信息(如状态/错误代码)。在空闲状态下,无论是控制器发往编码器的时钟信号,还是编码器返回至控制器的数据信号,均处于高电平(逻辑‘1’)状态。
当时钟信号出现第一个下降沿时,编码器数据会被传输至编码器内部的输出移位寄存器。随后,随着时钟信号的每个上升沿,数据将按最高有效位(MSB)在前、最低有效位(LSB)在后的顺序逐位移出。本示例中传输的数据为25位。其中高2位(第23、22位)包含诊断信息位,低16位则为采用格雷码编码的编码器位置信息(表 1)。数据传输完成后,数据线会先保持低电平一段固定时间,而后再切换回高电平(空闲状态)。此后,当下一个时钟线出现下降沿时,即可开始读取下一个编码器位置数据。
| 数据位 | 描述 |
| 第24位(MSB) | 传输的第一位,未使用 |
| 第23位 | 定位元件超出测量范围 |
| 第22位 | 定位元件处于测量范围内,但信号质量较差(例如距离过远) |
| 第21位至第16位 | 未使用 |
| 第15位至第0位 | 编码器位置信息(格雷码) |
当下一个SSI数据报的时钟下降沿启动时,若数据线在传输完LSB后仍处于低电平状态,可能会重复读取 到相同的编码器位置。本示例中,由于数据报未包含校验和,这种特性可用于检查编码器数据的完整性。 尽管数据位数为25位,但实际时钟周期数为26 个,比数据位数多1个。这是因为第一个数据位是随时钟 的第一个上升沿(而非下降沿)移出的,而最后一个数据位则随倒数第二个上升沿移出。
支持SSI协议的编码器及制造商种类繁多,其数据报位数、用于编码器位置信息的位数、数据编码方式(例 如采用二进制而非格雷码)、附加信息(例如状态/错误位、多圈数据)及校验方式(例如奇偶校验/CRC) 往往存在差异。关于具体的协议实现细节,请参考编码器制造商提供的文档。
软件概述
TMC8100 的SSI协议固件以两种形式提供:源代码“tmc8100-eval_ssi_encoder_demo_v10.asm”和英特尔十 六进制文件格式的机器码代码“tmc8100-eval_ssi_encoder_demo_v10.hex”。为便于功能测试与评估,还提供了Python脚本“tmc8100-eval_ssi_encoder_demo_v10.py”。此脚本需在通过USB连接TMC8100-EVAL-KIT的PC上运行,需要连接编码器(如图1所示)。
运行该Python 脚本程序需要在PC上预装Python解释器,并安装“intelhex”和“pySerial”等 Python 库,它 使用“tkinter”作为图形用户界面。
将编码器连接到TMC8100-EVAL-KIT、通过 USB 将评估板连接至 PC,并为 TMC8100-EVAL-KIT 施加+5V 电压后,即可从命令行执行该Python脚本:

首先,需选择用于与TMC8100-EVAL-KIT建立USB连接的虚拟COM端口(本示例中为“COM4”)。终端窗口的输出内容会显示已成功连接到Landungsbruecke (LB),并检测到TMC8100,同时显示芯片ID和版本号。
随后,图形用户界面(GUI)会在独立窗口中自动启动。

首先,需在“ Select Input File”(选择输入文件)区域(1)点击“…”按钮,选择包含TMC8100示例代码的十六进 制文件“tmc8100-eval_ssi_encoder_demo_v10.hex”。下一步,点击“ Load + Execute”(加载+执行)按钮(2), 文件内容将通过USB和Landungsbruecke/LB接口,借助引导加载程序写入TMC8100的SRAM程序存储器,并启动程序执行。请注意,此程序使用专有的通信协议访问编码器。如需再次将 TMC8100 切换至引导加载模式(例如如下载另一个程序),需进行复位或断电重启。

窗口中间的 SSI 帧区域设有两个命令按钮,用于启动编码器读取。按下“Read Encoder”(读取编码器)按钮 后,TMC8100 固件会通过SSI发送相应数量的时钟周期,并读取单圈范围内的绝对位置(显示在“ST”标签旁)。此处分别显示了两组数据:直接从编码器接收的格雷码格式数据[ST(原始/格雷码/16位)],以及转换后的16位数值[ST(转换后/二进制/16 位)]。转换过程在TMC8100固件内部完成,原始格雷码数值在此处显示仅作参考之用。

按下“ Read Encoder continuously”(持续读取编码器)按钮后,Python程序会以固定速率触发编码器值的读取。除了显示位置值的数字外,带有红色位置标记的模拟刻度盘也会随编码器值同步更新,以此指示当 前360°绝对角度位置。若要停止编码器持续读取,必须终止或重启Python程序。
除了图形用户界面(GUI)中显示的已提取数据/相关数据外,命令行窗口还会显示原始通信数据及一些附加信息,对于修改或扩展TMC8100程序示例可能有所帮助。

出于性能考虑,在持续读取编码器期间,命令行窗口的输出会被抑制。
固件实现
示例源代码“tmc8100-eval_ssi_encoder_demo_v10.asm”可用作起点,并可根据应用需求进行修改。 汇编器可用于对源代码进行编译转换。
流程图 (图5)概述了示例代码。
固件源代码开头定义了一些值(例如支持的软件版本和协议),并列出了 TMC8100 内部外设单元的寄存器地址,以此提升代码的可读性。
SOFTWARE_VERSION_MAJOR = $01 SOFTWARE_VERSION_MINOR = $00 PROTOCOL_3 = $53 ; "S" PROTOCOL_2 = $53 ; "S" PROTOCOL_1 = $49 ; "I" PROTOCOL_0 = $20 ; " " ; system register SYSTEM_CORE = $0 SYSTEM_TIMER = $1 SYSTEM_CRC = $2
SPI 配置
TMC8100 自带的标准SPI信号(SPI_CSN、SPI_SCLK、SPI_SDI、SPI_SDO)无需额外配置,其功能及引脚 分配在封装中已固定。不过,另有一个 SPI_DATA_AVAILABLE 信号可替代 GPIO(6)进行配置,用于指示 TMC8100 内部固件已向SPI输出缓冲区写入数据(输出高电平“1”)。这一机制可向所连接的微控制器提供 反馈,使其可启动SPI数据报/事务,从TMC8100中读取此前发送命令的回复数据。上电后,该功能会由 引导加载程序自动配置,此处列出仅为保证内容完整性:
LD GPIO0_ALT1_FUNCTION, r0 LD GPIO_OUT_ENABLE, r1 SET $4, r0, r0 ; GPIO_ALT1_FUNCTION(5 downto 4) = "01" -> connect spi_data_available to GPIO(6) CLR $5, r0, r0 ST GPIO0_ALT1_FUNCTION, r0 SET $6, r1, r1 ; GPIO(6) / SPI_DATA_AVAILABLE -> output ST GPIO_OUT_ENABLE, r1
时钟选择与初始化
TMC8100始终以内置振荡器启动,上电/复位后,引导程序会将锁相环(PLL)配置为75MHz的系统时钟频率。 本例中,使用的是TMC8100-EVAL-KIT上配备的16MHz晶体振荡器,为简化后续的时钟计算或分频器设置, 将PLL输出及系统频率设定为100MHz。对于SSI而言,晶振时钟并非必需,因为通信所需的参考时钟由 TMC8100 自身提供。因此,这种情况下可选用TMC8100的内部时钟搭配锁相环(PLL)作为替代方案。
第一步,将GPIO0和GPIO1引脚配置为外部晶体与内置晶体振荡器配合使用的模式。
LDI $03, r0 ; enable input for GPIO0/GPIO1 ST GPIO_IN, r0 LDI $03, r0 ; disable pull-up for GPIO0/GPIO1 ST GPIO_PU, r0
下一步,将PLL反馈分频器配置为100MHz的PLL输出频率(PLL_FB_100),并为晶体振荡器(XTAL)配置时钟电路,同时设置PLL输入分频器,使PLL输入时钟频率达到1MHz。由于时钟模块的每个寄存器需通过间接方式访问,写入操作需要执行四个命令,先通过一对加载(LDI)和存储(ST)指令设置寄存器地址,再通过另一对LDI和ST指令设置新的寄存器值。
LDI PLL_FB_CFG, r0 ; set pll feedback divider ST CLK_ADDR, r0 LDI PLL_FB_100, r0 ST CLK_DOUT, r0 ; will trigger write access to clk register LDI CLK_CTRL_SOURCE, r0 ST CLK_ADDR, r0 LDI $26, r0 ; use XTAL ST CLK_DOUT, r0 ; will trigger write access to clk register LDI CLK_CTRL_OPT, r0 ; enable clk fsm ST CLK_ADDR, r0 LDI $40, r0 ST CLK_DOUT, r0 ; will trigger write access to clk register LDI CLK_CTRL_PLL_CFG, r0 ST CLK_ADDR, r0 LDI %1011_1101, r0 ; RDIV = 15 (assuming 16MHz external / XTAL clock) ; and select PLL output, start FSM (commit = 1) ST CLK_DOUT, r0 ; will trigger write access to clk register
最后一次写入操作还会触发时钟模块的内部状态机,以应用所有更改。由于这一过程包括晶体振荡器的启动和PLL的锁定,因此需要检查时钟模块的状态寄存器,等待100MHz的新系统时钟就绪。为此,需选定时钟模块配置寄存器(CLK_CTRL_PLL_CFG)的地址,程序会进入一个循环,不断读取该寄存器,直到第 7 位(TEST1 $7, r0)被清零,才会继续执行后续程序。
LDI CLK_CTRL_PLL_CFG, r0 ST CLK_ADDR, r0 NOP NOP WAIT_FOR_PLL: LD CLK_DIN, r0 NOP TEST1 $7, r0 JC WAIT_FOR_PLL
DIRECT_IN/DIRECT_OUT 引脚配置
如 图2所示,TMC8100-EVAL-KIT上的两颗RS485收发器均用于与编码器通信。框图中上方的收发器负责将时钟信号从TMC8100传输至编码器。该RS485收发器的发送使能端始终保持开启状态。DIRECT_OUT(0)被用作时钟输出(通过命令“ST DIRECT_ALT_FUNCTION, r0”选择该输出的复用功能),而DIRECT_IN(0)则用于将RS485收 发器输出的时钟信号回环至TMC8100,以实现驱动器延迟补偿。这是一项可选功能,并非必需,因为时钟回环也可在内部完成。
框图中下方的RS485收发器用于接收来自编码器的数据。此处的发送器处于禁用状态。DIRECT_IN(1)用作串行数据输入,而DIRECT_OUT(1)和 DIRECT_OUT(3)在本应用中无需使用,故设置为固定电平(均为低电 平“0”)。
由于时钟线(CLK+)在空闲状态下为高电平“1”,对应的输出 DIRECT_OUT(0)会被反向。对于数据和时钟环回 输入,硬件中的滤波器已启用。
; configure DIRECT_OUT(0) as inverted LDI $10, r0 ST DIRECT_POLARITY, r0 ; set DIRECT_OUT(1) = 0 -> always low SFCLR WAIT1SF NO_WAIT, 0, 1 ; set DIRECT_OUT(3) = 0 -> always low SFCLR WAIT1SF NO_WAIT, 0, 3 ; configure DIRECT_OUT(0) as clock output of the system timer and DIRECT_OUT(3) as output LDI $01, r0 ST DIRECT_ALT_FUNCTION, r0 ; enable input filter for DIRECT_IN LDI %0000_0001, r0 STS r0, SYSTEM_CORE, SYSTEM_CORE_INPUT_FILTER_W
SPI 命令循环
配置好TMC8100后,程序会进入一个无限循环,等待通过SPI接收命令。所有SPI事务均采用32位数据报形式。本例代码中的所有命令均可容纳在一个数据报内。为简化操作,仅通过检测高8位(MSB,最先通过 SPI 接收)来选择和执行命令。命令循环的流程如下:首先读取 SPI 外设模块的状态寄存器(SPI_STATUS),并等待该状态寄存器的第0位变为1(WAIT1 $0, r0),1表示已接收到SPI数据报,且数据已存入SPI输入缓冲区(SPI_BUFFER)。
CMD_LOOP: ; wait for SPI command LDI SPI_STATUS, r0 WAIT1 $0, r0 LD SPI_BUFFER_3, r0 ; read encoder position data LDI $80, r1 COMP EQ r0, r1 JC read_encoder_data ; get software version . . . . JA CMD_LOOP
将32位SPI输入缓冲区内容的高8位与$80进行比对,$80是本例代码中定义的、用于读取编码器数据(含标志和位置值)的SPI命令。如果比对结果匹配,程序会跳转到“read_encoder_data”地址执行。该地址处的程序代码(后续图示将详细说明)会通过DIRECT_OUT(0)发送预设数量的时钟周期,并通过DIRECT_IN(1)收集编码器的回复数据。接收到的数据会被组装成32位SPI数据报,并存入SPI输出缓冲区。与此同时, SPI_BUFFER_FULL/GPIO6 会从低电平“0”变为高电平“1”,以指示有新数据可供SPI事务处理。在后续的SPI事务中,这些数据可被读取。在读取完前一条命令的所有数据之前,不应发送新命令。由于一次SPI事务始终在双向传输数据,且通常需要多次事务才能读取完所有回复数据,因此建议使用“虚拟”命令(例如 0x00 0x00 0x00 0x00),命令循环不会对这种命令进行解析。最后一次读取事务中,可包含下一条命令。
用于测试编码器实现方案的Python脚本“tmc8100-eval_ssi_encoder_demo.py”(适用于TMC8100-EVAL-KIT) 提供了带“Read Encoder”(读取编码器)按钮的图形界面(图4)。按下此按钮后,Landungsbruecke/LB 会发送SPI数据报0x80 0x00 0x00 0x00(摘自“tmc8100-eval_ssi_encoder_demo.py”)。
def spi_get_encoder_value(): # get encoder value command value = SpiWriteCommand([0x80, 0x00, 0x00, 0x00]) WaitForGPIO6() . . . .
随后,程序会等待编码器的回复。一旦 TMC8100 的 SPI 输出缓冲区中有回复数据可用, SPI_DATA_AVAILABLE/GPIO6 引脚就会从低电平“0”切换为高电平“1”。为了读取这些数据,Python 程序会 使用SPI“虚拟”命令0x00 0x00 0x00 0x00,该命令不会被TMC8100固件解译。
# get ST value (raw / gray code value)
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"ST (raw): {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
. . . .
# get ST value (binary value)
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"ST (binary): {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
. . . .
# get error flags
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"ERR: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
. . . .
下表概述了示例程序支持的所有SPI命令及相应的回复数据。本示例中,SPI命令始终包含在一个32位数据报中,而回复最多可能占用三个32位数据报。SPI 32位数据报以四个连续的十六进制数表示,每个字节对应一个十六进制数,采用MSB在前的格式以提升可读性。对于回复,32位数据报依据被放入SPI缓冲区的顺序(即先进先出,FIFO)进行显示/读取。
| SPI 命令(32位) | SPI 回复(32位) |
| 0x80 0x00 0x00 0x00 读取编码器错误标志及单圈范围内的 绝对位置值(ST) |
|
| 0xff 0x00 0x00 0x00 获取固件版本 |
|
| 0xfe 0x00 0x00 0x00 获取编码器协议 |
|
当按下“ Read Encoder”(持续读取编码器)按钮时,Python程序会重复发送表中的第一条指 令0x80 0x00 0x00 0x00。
读取编码器数据
当TMC8100 软件处于等待SPI命令的状态时,一旦接收到新的SPI命令,且该32位数据报的最高字节(即最先接收到的字节)等于$80,程序执行将跳至代码中的“read_encoder_data”地址,并开始通过 DIRECT_OUT(0)和连接的RS485 收发器向编码器准备并发送的时钟脉冲。
; set timer to 100MHz / 50 = 2MHz clock toggle rate -> 1MHz SSI clock LDI 49, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_COUNTER_LIMIT_W ; number of clock edges: 52 (25 x 2 + falling edge at the beginning and rising edge at the end) LDI 52, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_PULS_COUNTER_LIMIT_W LDI 1, r0 ; enable counter STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W . . . .
时钟信号配置为1MHz,这是本例中所用编码器的最高频率。系统计数器每次溢出时,时钟输出就会翻转。因此,系统计数器的频率需设为输出时钟频率的两倍(本例中为 2MHz)。时钟周期数等于从编码器移出的位数(本例中为25位),再加上起始处的下降沿和结束处的上升沿。这使得时钟输出总共产生26个时钟周期,包含52个上升沿和下降沿。计数器系统的最后一步是启动定时器初始化时钟生成过程。
接下来,编码器的回复数据会在输入端DIRECT_IN(1)被捕获。作为参考(同时也为提供某种延迟补偿),系统使用通过RS485收发器回环并输入到引脚DIRECT_IN(0)的时钟信号。注:若无法进行环回(例如使用RS422 发送器时),可直接使用内部时钟。
固件会等待时钟信号 CLK+从高电平“1”变为低电平“0”,0 标志着传输开始(WAIT0SF WAIT_IN0, WAIT_NO_ACTION)。编码器的串行数据会随时钟信号的下一个上升沿移出,并由TMC8100固件在时钟信号的下降沿捕获到输入端DIRECT_IN(1)。捕获的第一位是25位数据报的最高有效位/第24位(图6)。
; wait for clock signal low / start of transmission WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal low ; shift in error flags on falling edge of clock REP 3, 2 WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high SHLI WAIT0SF WAIT_IN0, r2, FLAG_IN1 ; shift DATA in MSB first on falling edge of clock signal ; shift in absolute multi turn counter value (not used) REP 6, 2 WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high SHLI WAIT0SF WAIT_IN0, r7, FLAG_IN1 ; shift DATA in MSB first ; shift in single turn counter value - MSB REP 8, 2 WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high SHLI WAIT0SF WAIT_IN0, r3, FLAG_IN1 ; shift DATA in MSB first OR r3, r3, r5 ; copy original value to r5 ; shift in single turn counter value - LSB REP 8, 2 WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high SHLI WAIT0SF WAIT_IN0, r4, FLAG_IN1 ; shift DATA in MSB first OR r4, r4, r6 ; copy original value to r6
接收到的前3位在时钟信号的后续下降沿被移入寄存器r2,这一过程通过“REP 3, 2”后的两条指令连续执行三次。其中第一条指令始终等待时钟信号变为高电平“1”(WAIT1SF …),第二条指令则在时钟下降沿将串行数据移入寄存器r2 (SHLI ..r2..),数据按从左到右的顺序移入,最高有效位(MSB)在前。
接下来,以“REP 6, 2”开头的三条指令将6位数据移入寄存器r7,本例中这些位未被使用。对于其他SSI编码器,这些位可能包含多圈计数器信息。
最后是两个代码块,每个块以“REP 8, 2”开头、包含三条指令,用于在时钟下降沿将编码器计数值分别移入寄存器r3 (MSB)和寄存器r4 (LSB)。接收的数据还会被复制到寄存器r5 (MSB)和r6 (LSB),以保留编码器输出的原始格雷码数据。
将格雷码转换为二进制码
下一步,将寄存器r3和r4中存储的编码器计数值从格雷码转换为二进制码。转换过程按位进行,从r3中的MSB开始,且为原地转换,转换结果会覆盖寄存器r3和r4中的原有内容。逐位转换采用图7所示的算法。该算法灵活性高,可轻松适配不同的位长度。
以下是单个位的转换代码示例:
TEST1 7, r3 MOVF 0, r0 TEST1 6, r3 MOVF 0, r1 XOR r0, r1, r0 TEST1 0, r0 MOVF 6, r3 . . .
先对r3的MSB进行检测(TEST1 7, r3),处理器会根据该位的值来设置标志位。随后,将该标志位复制到辅助寄存器r0的第0位(MOVF 0, r0)。接下来的两条指令采用相同方法,将寄存器r3的第6位复制到寄存器r1 的第0位。之后,对寄存器r0和r1执行按位异或(XOR)操作,并将结果写回r0。再将r0第0位的值重新复制到系统标志位(TEST1 0, r0),最后复制到寄存器r3的第6位。对 r3中剩余的位重复上述步骤,完成后再对r4执行相同操作。
SPI 回复数据报
至此,所有接收到的数据均已存储在系统寄存器r2至r6中。下一步,这些值将被复制到SPI缓冲区,以 供连接的微控制器/运动控制器读取。
; copy single turn 16bit raw value from encoder to SPI transmit buffer LDI %0001_0000, r0 ; ST value ST SPI_BUFFER_3, r0 ; 1 -> ST (raw) LDI $0, r0 ST SPI_BUFFER_2, r0 ST SPI_BUFFER_1, r5 ; ST - MSB ST SPI_BUFFER_0, r6 ; ST – LSB
第一个32位SPI数据报中,低16位填充的是从编码器接收的格雷码绝对位置信息,最高有效位(MSB)为固 定值0x10。当读取该SPI数据报时,可通过此固定MSB值明确识别数据报内容。
LDI %0010_0000, r0 ; ST SPI_BUFFER_3, r0 ; 2 -> ST (binary) LDI $0, r0 ST SPI_BUFFER_2, r0 ST SPI_BUFFER_1, r3 ST SPI_BUFFER_0, r4
第二个32位SPI数据报中,低16位为转换后的二进制码绝对位置信息,最高有效位为固定值0x20。
LDI %0111_0000, r0 ; flag value ST SPI_BUFFER_3, r0 ST SPI_BUFFER_2, r2 ; flags LDI $0, r0 ST SPI_BUFFER_1, r0 ; no CRC ST SPI_BUFFER_0, r0 ; no CRC
第三个(即最后一个)32位SPI数据报中,包含从编码器接收的标志位,最高有效位为固定值0x70。
附录
汇编器
为便于TMC8100 的程序开发和示例/参考程序的修改,提供了一个汇编器供使用。这是一个基于PC的命令行工具,需要以汇编源代码文件的文件名和可选路径作为参数。如果未找到指定的文件名,系统会自动在提供的文件名后添加*.asm后缀。汇编器将根据这个输入文件,生成相应的输出文件。

在本示例中,汇编源代码输入文件的名称为“tmc8100-eval_abn_demo.asm”。从该文件生成的机器指令(16 位)数量为83条,占可用程序存储(SRAM)的4.1%。TMC8100的程序存储器容量为2K x 16位,最多可存储2048条指令。
汇编器默认生成的输出文件与输入文件同名,仅将扩展名由*.asm 改为*.hex。该输出文件是一个文本文件,其中的程序代码采用标准的英特尔十六进制文件格式,例如可用于通过TMCL-IDE或示例代码中提供的某个Python 脚本,将程序代码加载到TMC8100-EVAL-KIT上TMC8100的程序存储器中。
此外,该汇编器支持通过设置一系列标志参数,生成不同格式的附加输出文件及额外的辅助信息。

- 选项“-i”:生成文件“
.i”作为预处理器的输出。该文件包含汇编源文件内容,其中所有预处 理命令(# ...)已被处理(例如,“#include”文件内容已合并到源文件中),且注释(例如,/* ..*/ | // | ;) 已被移除。 - 选项“-l”:生成日志文件“
.log”,其包含汇编源代码(不含注释和预处理命令),并附加指令编码及各指令的程序存储器地址。 - 选项“-c”:生成C代码文件“<filename.c>”,用于支持引导TMC8100所用控制器的程序开发。该文件包含一个整数数组,用于存储生成的机器码。 </filename.c>
- 选项“-rom”:生成 3 个附加文件;其中,“pelican_bootloader.hex”和“pelican_bootloader.bin”以十六进制/二进制数形式列出机器指令,每行一个数值,而“pelican_bootloader.vhd”则将机器指令存为逻辑代码,作为VHDL实体声明的一部分。
- 选项“-m”:机器标志,用于支持不同的实现/指令集(TM02适用于TMC8100)。
- 选项“-h”:打印帮助测试,如屏幕截图所示
支持的语法/命令
该汇编器目前支持TMC8100数据手册中所述的所有TMC8100指令。
预处理器命令
注释
预处理器会从输入文件中移除所有注释,以便进行后续处理。目前支持如下选项:
- /*
*/——块注释,可以包含多行内容(C语言风格) - //
——行尾注释,从//开始至本行结束(C/C++语言风格) - ;
——行尾注释,从;开始至本行结束
#include
#include "<filename>"——将
注:
#define
#define <label> [<replacement text>]
此外,允许定义无替换文本的<label>,此时替换文本为空。这在配合条件预处理器命令(如#ifdef/#ifndef) 时可能会很有用。
#ifdef, #ifndef, #else, #endif
#ifdef <label> <code block 1> #else <code block 2> #endif
若<label> 此前已被定义,则会解析<code block 1>的内容并生成汇编输出,同时忽略<code block 2>。中间 文件(“<<filename>>.i”)中仅显示 <code block 1>,不显示<code block 2>。代码块可以包含多行汇编指令等内容。若<label>尚未定义,则情况相反。
#ifndef <label> <code block 1> #else <code block 2> #endif
注:在使用#define <label>之前,只需提及<label>名称便已足够,无需提供替换文本/值。
#else 部分是可选的。#ifdef或#ifndef块可以嵌套使用。
汇编器命令
数字
为简化二进制、十进制和十六进制数字的表示,系统支持多种格式,具体如下:
0x123 或$123 解析为十六进制数(允许的字符:0-9、a-f或A-F)
%1010 解析为二进制数(允许的字符:0和1)。可以插入字符“_”以增强可读性。例如,%1010_0011表示 一个8位二进制数字
123 解析为十进制数(允许的字符:0-9)
标识符
标识符可替代数字使用,以提高代码的可读性。标识符必须以字母(a-z 或 A-Z)或下划线“_”开头,后续
可包含数字(0-9)。请注意,标识符名称不区分大小写。
可使用等号为标识符赋值;例如,
SPI_BUFFER_0 = $30
一些名称不应用作标识符:
- 汇编指令,包括TMC8100 数据手册中列出的所有名称,或是以“C”开头的指令名称(“C”表示该命令的条件执行)。
- 标识符r0至r7是预定义的,分别代表寄存器0至7,用于指定所有可用的通用寄存器
- 标签与标识符不可同名
标签
标签用作程序存储器地址的占位符。标签无需赋值,在汇编器将汇编源文件转换为机器码时,会自动以当
前程序存储器地址对其进行初始化。
无限循环示例:
WAIT:
JA WAIT
注意标签名称后带有“:”字符。汇编器会自动将标签WAIT初始化为下一条指令的程序存储器地址(本例中 为JA WAIT 命令的地址)。两种跳转方式均受支持:一是向后跳转(如上例所示),即标签在实际作为指令一部分使用之前已完成初始化;二是向前跳转,即标签在被某条指令引用之后才完成初始化。






