资源库
AN-2619:TMC8100支持Tamagawa 绝对式编码器协议
描述
TMC8100 内置一款可编程微控制器,针对速率高达 的串行同步及异步绝对式编码器协议进行了优化。它可替代专用的编码器协议接口芯片和 实施方案,同时支持通过系统内更新来适配不同编码器的功能,或切换至其他编码器协议。 是一款紧凑、经济且灵活的通信解决方案,能为工业驱动器增添绝对式编码器支持功能。
本应用笔记详细介绍了 支持 绝对式编码器的软件参考实现方案。
产品特性
- 产品特性实现方案
- 支持绝对式编码器
- 支持数据读取及动态循环冗余校验 校验和计算
应用
- 伺服驱动器位置反馈
- 伺服驱动器电机控制
系统描述
伺服电机驱动器(例如在工业应用中)通常需要精准、可靠且低延迟的位置反馈。长期以来,带有增量式A/B/N输出的光学编码器一直是行业标准。尽管如此,绝对式位置编码器正逐渐普及,这类编码器往往具备额外功能和不同的接口协议(多为厂商专有协议)。例如, 的 协议便是用于编码器与控制器之间数字数据串行传输的一种协议。 协议的物理层基于 标准,可传输位置值、诊断信息,并支持读写编码器内部存储器。
采用 协议的编码器通过单屏蔽四芯电缆与 评估板连接(图 )。四芯电缆的构 成如下:
- +5V 和GND :编码器电源和接地连接
- DATA+和DATA- :用于数据通信的差分 信号(双向)
参考实现方案的特性包括:
- T-Format协议支持的2.5Mbps数据传输速率
- 发送命令(ID0、ID1、ID2、ID3、ID6、ID7、ID8、IDC、IDD)并接收回复
- 数据的打包和解包
- 动态计算CRC校验和,并添加至传输数据中
- 动态计算CRC校验和,并与接收的校验和进行比对
参考实现方案以源代码形式提供。用户可在此基础上,根据应用需求进行修改。
系统概述
所提供的软件专为配合 使用而设计,并已通过Tamagawa 的 TS5667n120 编码器进行测试。
TMC8100-EVAL-KIT 应用笔记所使用的核心硬件元件包括TMC8100 芯片和RS485 收发器,后者用于在TMC8100 与连接 器处的差分RS485 信号(即DATA+ 和DATA- )之间进行转换(图 )。
软件包含 TMC8100 的固件,实现了支持协议所需的控制器功能。此固件需在设备上电后下载至TMC8100 中。此外,还提供一款基于 Python 脚本的图形用户界面(GUI) ,可用于选择和下载固件,并且之后还可用于对固件功能进行演示和测试。
Tamagawa T-Format协议
Tamagawa 是一家专注于编码器技术的制造商,所生产的编码器提供增量式或绝对式位置输出。本参考实现方案聚焦于采用RS485 线路驱动器、以数字输出形式提供绝对位置信息的编码器。所采用的通信协议格式称为T-Format 。
使用 T-Format 与编码器进行通信时,通常的流程是:控制器先向编码器发送请求,编码器返回回复后,控制器方可发送下一个请求。
T-Format 通信协议大致可分为三类请求:数据读取、复位、内置 EEPROM的访问。不同的请求通过唯一的 ID代码进行区分。
| 事务类型 | ID CODE | DESCRIPTION |
| 数据读取 | 0 | 单圈绝对数据(ABS) |
| 1 | 多圈数据(ABM) | |
| 2 | 编码器 ID (ENID) | |
| 3 | ABS + ABM + ENID + 编码器错误状态 (ALMC) | |
| EEPROM 写入 | 6 | 写入EEPROM 地址 (ADF)和数据(EDF)] |
| EEPROM 读取 | D | 读取 EEPROM [地址 (ADF)] |
| 复位 | 7 | 复位ABS 值 |
| 8 | 复位ABM 值 | |
| C | 复位错误 |
数据读取时, TMC8100向编码器发送一个控制字段 (CF)数据报(表2 ),编码器则返回包含该CF 副本、状态字段(SF) 、若干与请求对应的数据包及CRC 校验和(CRC) 的回复(表3 )。在EEPROM 写入操作中,TMC8100 发送的内容包括CF ,其后紧跟 EEPROM 地址字段(ADF) 、 EEPROM 数据字段(EDF) 及CRC 。 EEPROM读取操作包含控制字段(CF) 、 EEPROM地址字段 (ADF)及 CRC。上述两种操作中,编码器的回复均包含EEPROM 地址字段 (ADF)、 EEPROM数据字段 (EDF)及CRC 校验和。复位操作时, TMC8100发送 CF,而编码器的回复中始终包含单圈绝对位置值(ABS) 。
| ID 代码 | 事务类型 | TMC8100 传输的字段 | |||
| 0 | 数据读取 | CF | |||
| 1 | 数据读取 | CF | |||
| 2 | 数据读取 | CF | |||
| 3 | 数据读取 | CF | |||
| 6 | EEPROM 写入 | CF | ADF | EDF | CRC |
| D | EEPROM 读取 | CF | ADF | CRC | |
| 7 | 复位 | CF | |||
| 8 | 复位 | CF | |||
| C | 复位 | CF | |||
CF: 控制字段
ADF: EEPROM 地址字段
EDF: EEPROM 数据字段
CRC: CRC字段
| ID 代码 | 事务类型 | 传输的字段(编码器) | ||||||||||
| 0 | 数据读取 | CF | SF | ABS0 | ABS1 | ABS2 | CRC | |||||
| 1 | 数据读取 | CF | SF | ABM0 | ABM1 | ABM2 | CRC | |||||
| 2 | 数据读取 | CF | SF | ENID | CRC | |||||||
| 3 | 数据读取 | CF | SF | ABS0 | ABS1 | ABS2 | ENID | ABM0 | ABM1 | ABM2 | ALMC | CRC |
| 6 | EEPROM 写入 | CF | ADF | EDF | CRC | |||||||
| D | EEPORM读取 | CF | ADF | EDF | CRC | |||||||
| 7 | 复位 | CF | SF | ABS0 | ABS1 | ABS2 | CRC | |||||
| 8 | 复位 | CF | SF | ABS0 | ABS1 | ABS2 | CRC | |||||
| C | 复位 | CF | SF | ABS0 | ABS1 | ABS2 | CRC | |||||
ABS: 单圈绝对位置
ABM: 多圈位置
ENID: 编码器 ID
ALMC: 编码器错误
例如,图3 展示了读取编码器单圈绝对位置( 代码 )的请求与回复格式。
通信采用异步方式。每个字段包含10位: 1个起始位、 8位数据( LSB在前)及 1个停止位。
控制字段(CF)包含一个起始位、三个同步位、ID代码(本例中为ID代码0)及一个停止位(图4)。
编码器的绝对位置值被拆分为连续的三个字节(ABS0至ABS2),且按照最低有效字节优先的顺序传输(图5)。
最后一个数据字段后传输的CRC校验码,用于覆盖并保护控制字段(CF)、状态字段(SF)及数据字段(ABS0、ABS1和ABS2)中的8位数据(不含起始位和停止位)(图6)。
本节旨在简要概述T-Format协议。有关更多详细信息,请参见Tamagawa提供的技术规格文档。
软件概述
TMC8100的Tamagawa协议固件以两种形式提供:源代码“tmc8100-eval_tamagawa_encoder_demo_v10.asm”和英特尔十六进制文件格式的机器码代码“tmc8100-eval_tamagawa_encoder_demo_v10.hex”。为便于功能测试与评估,还提供了Python脚本“tmc8100-eval_tamagawa_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_tamagawa_encoder_demo_v10.hex”。随后点击“Load + Execute”(加载+执行)按钮(2),文件内容将通过USB和Landungsbruecke/LB接口,借助引导加载程序写入TMC8100的SRAM程序存储器,并启动程序执行。请注意,此程序使用专有的通信协议访问编码器。如需再次将TMC8100切换至引导加载模式(以便下载相同或不同的程序等),需进行复位或断电重启。
窗口中部的Tamagawa T-Format区域左侧设有独立按钮,对应不同的命令(ID0至IDD)。按下ID0按钮会发送相应命令,并从编码器读取单圈绝对位置(显示在“ABS”标签旁)和状态标志(显示在“SF”标签旁)。编码器发送的CRC校验和,以及TMC8100接收串行数据时同步计算的校验和,分别显示在“CRC calculated”(计算的CRC)和“CRC received”(接收的CRC)标签旁。
除了图形用户界面(GUI)中显示的已提取数据/相关数据外,命令行窗口还会显示原始通信数据及一些附加信息,对于修改或扩展TMC8100程序示例可能有所帮助。
固件实现
示例源代码“tmc8100-eval_tamagawa_encoder_demo_v10.asm”可用作起点,并可根据应用需求进行修改。汇编器(Assembler)可用于对源代码进行编译转换。
流程图(图7)概述了示例代码的整体结构,重点展示ID代码0命令的传输和回复接收过程。
固件源代码开头定义了一些值(例如支持的软件版本和协议),并列出了TMC8100内部外设单元的寄存器地址,以此提升代码的可读性。
SOFTWARE_VERSION_MAJOR = $01 SOFTWARE_VERSION_MINOR = $00 PROTOCOL_3 = $54 ; "T" PROTOCOL_2 = $41 ; "A" PROTOCOL_1 = $4D ; "M" PROTOCOL_0 = $54 ; "T" . . . . ; spi register address SPI_BUFFER_0 = $30
时钟选择与初始化
TMC8100始终以内置振荡器启动,上电/复位后,引导程序会将锁相环(PLL)配置为75MHz的系统时钟频率。对于Tamagawa协议,需要更精确的时钟源。本例中,使用的是TMC8100-EVAL-KIT上配备的16MHz晶体振荡器,当然也可选择外部时钟作为替代方案。为简化后续的时钟计算或分频器设置,将PLL输出及系统频率设定为100MHz。
第一步,将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 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收发器与编码器进行通信。该RS485收发器的控制方式如下:通过DIRECT_OUT(1)输出串行发送数据,通过DIRECT_OUT(3)实现发送器使能,通过DIRECT_IN(1)输入串行数据(此输入信号无需配置)。
; set TXD / DIRECT_OUT(1) = 1 SFSET WAIT1SF NO_WAIT, 0, 1 ; set TXD_EN / DIRECT_OUT(3) = 0 SFCLR WAIT1SF NO_WAIT, 0, 3 ; configure TXD_EN / DIRECT_OUT(3) as output LDI $00, r0 ST DIRECT_ALT_FUNCTION, r0
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 ; ID0 - read encoder absolute data in one revolution LDI $80, r1 COMP EQ r0, r1 JC tamagawa_id0 ; ID1 - read encoder multi-turn data . . . . JA CMD_LOOP
将32位SPI输入缓冲区内容的高8位与$80进行比对,$80是本例代码中定义的、用于向编码器发送ID0请求(以读取单圈绝对位置(ABS))的SPI命令。如果比对结果匹配,程序会跳转到“tamagawa_id0”地址执行。该地址处的程序代码(后续章节将详细阐述)会通过DIRECT_OUT(1)发送ID0命令数据报,并通过DIRECT_IN(1)接收编码器的回复。接收到的数据会被组装成32位SPI数据报,并存入SPI输出缓冲区。与此同时,SPI_BUFFER_FULL/GPIO会从低电平变为高电平,以此指示有新数据可供SPI事务处理。在后续的SPI事务中,这些数据可被读取。在读取完前一条命令的所有数据之前,不应发送新命令。由于一次SPI事务始终在双向传输数据,且通常需要多次事务才能读取完所有回复数据,因此建议对除了最后一次外的所有事务使用“虚拟”命令(例如0x00 0x00 0x00 0x00),命令循环不会对这类虚拟命令进行解析。最后一次读取事务中,可包含下一条命令。
用于测试编码器实现的Python脚本“tmc8100-eval_tamagawa_encoder_demo.py”(适用于TMC8100-EVAL-KIT)提供了一个带ID0按钮的图形用户界面(GUI)。按下此按钮后,Landungsbruecke(简称LB)会发送一个SPI数据报0x80 0x00 0x00 0x00(摘录自脚本“tmc8100-eval_tamagawa_encoder_demo.py”):
def spi_tamagawa_id0(): # get ABS value value = SpiWriteCommand([0x80, 0x00, 0x00, 0x00]) WaitForGPIO6() . . . .
随后,程序会等待编码器的回复。一旦TMC8100的SPI输出缓冲区中有回复数据可用,SPI_DATA_AVAILABLE/GPIO6引脚就会从低电平切换到高电平。为了读取这些数据,Python程序会使用SPI“虚拟”命令0x00 0x00 0x00 0x00,该命令不会被TMC8100固件解译。
# get ABS value
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"ID0: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
. . . .
# get CRC + SF value
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"ID0: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
. . . .
下表概述了示例程序支持的所有SPI命令及相应的回复数据。SPI命令始终包含在一个32位数据报中,而回复最多可能占用五个32位数据报。SPI 32位数据报以四个连续的十六进制数表示,每个字节对应一个十六进制数,采用MSB在前的格式以提升可读性。对于回复,32位数据报依据被放入SPI缓冲区或可被读取的顺序显示。
| SPI命令(32位 | SPI回复(32位) |
| 0x80 0x00 0x00 0x00 ID代码0请求 - 读取编码器单圈绝对位置数据 |
1. 0x10 ABS0 ABS1 ABS2 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x81 00 00 00 ID代码1请求 - 读取编码器多圈数据 |
1. 0x20 ABM0 ABM1 ABM2 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x82 00 00 00 ID代码2请求 - 读取编码器ID |
1. 0x40 0x00 0x00 ENID 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x83 00 00 00 ID代码3请求 - 读取编码器绝对位置、多圈数据、ID和错误标志 |
1. 0x10 ABS0 ABS1 ABS2 2. 0x40 0x00 0x00 ENID 3. 0x20 ABM0 ABM1 ABM2 4. 0x50 0x00 0x00 ALMC 5. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x86 00 EDF ADF ID代码6请求 - 向7位地址写入8位数据 |
1. 0x50 0x00 EDF ADF 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x87 00 00 00 ID代码7请求 - 复位错误标志 |
1. 0x10 ABS0 ABS1 ABS2 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x88 00 00 00 ID代码8请求 - 复位编码器绝对位置计数器 |
1. 0x10 ABS0 ABS1 ABS2 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x8c 00 00 00 ID代码C请求 - 复位编码器多圈计数器 |
1. 0x10 ABS0 ABS1 ABS2 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0x8d 00 00 ADF ID代码D请求 - 从7位地址读取8位数据 |
1. 0x50 0x00 EDF ADF 2. 0x70 SF 接收到的CRC 计算出的CRC |
| 0xff 00 00 00 获取固件版本 |
1. 0xff 0x00 主版本号 次版本号 |
| 0xfe 00 00 00 获取编码器协议 |
1. 0x54 (“T”) 0x41 (“A”) 0x4d (“M”) 0x54 (“T”) |
ID代码0请求
当TMC8100软件处于等待SPI命令的状态时,一旦接收到新的SPI命令,且该32位数据报的最高字节(即最先接收到的字节)等于$80,程序执行将跳至代码中的“tamagawa_id0”地址,并开始通过DIRECT_OUT(1)和连接的RS485收发器向编码器准备并发送ID代码0请求。
; set timer to 100MHz / 40 = 2.5MHz LDI 39, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W LDI 2, r0 ; enable timer STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W ; switch on driver SFSET WAIT1SF NO_WAIT, 0, 3 . . . .
对于编码器协议,所需的传输数据速率为2.5Mbit/s。由于内部系统时钟设为100MHz,用于位移出操作的系统定时器被设为39,以获得100/2.5=40的分频系数。随后,定时器启用,并通过设置DIRECT_OUT(3)输出,开启外部RS485驱动器。
; start bit + sync code LDI %0000_0100, r0 ; id0 + parity LDI %0000_0000, r1 REP 4, 1 SHRO WAIT1SF WAIT_OVERFLOW_TIMER, r0, FLAG_OUT1 ; shift out start bit + sync code REP 5, 1 SHRO WAIT1SF WAIT_OVERFLOW_TIMER, r1, FLAG_OUT1 ; shift out ID0 + parity WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION ; wait time for last bit before stop bit ; stop bit SFSET WAIT1SF NO_WAIT, 0, 1 . . . . WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_STOP_TIMER ; end of command - switch off driver SFCLR WAIT1SF NO_WAIT, 0, 3
在等待一些时间,以便RS485收发器输出稳定后,通过SHRO指令将起始位(“0”)和3位同步码(“010”)从系统寄存器r0经DIRECT_OUT(1)移出。该指令使用REP 4, 1执行四次。每次执行时,程序会等待系统定时器溢出(参数WAIT_OVERLFLOW_TIMER),然后从r0寄存器移出LSB。随后,采用类似方式移出包含ID代码0和奇偶校验位的数据报的剩余5位,这次使用系统寄存器r1,并通过REP 5, 1重复执行SHRO指令五次。DIRECT_OUT(1)串行输出随后将停止位切换为“1”,最后将DIRECT_OUT(3)置为低电平,以关闭RS485收发器并准备接收来自编码器的数据。
ID代码0回复
为准备接收回复,需初始化CRC计算模块,以便在串行数据移入过程中实时进行CRC计算。
; initialize CRC for 8bit CRC LDI $0, r0 STS r0, SYSTEM_CRC, SYSTEM_CRC_CTRL_W ; reset CRC block LDI %0000_0001, r0 ; CRC polynomial: x^8 + x^0 STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; LSB LDI %0000_0001, r0 ; CRC polynomial: x^8 + x^0 STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W LDI %0000_0000, r0 STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; MSB
首先,复位用于CRC计算的线性反馈移位寄存器(LFSR),并将该协议中用于计算CRC的多项式加载到系统CRC寄存器(SYSTEM_CRC_POLYNOM_W)中。该寄存器大小为32位,在CRC模块复位后,通过四次字节宽度的写入操作向同一寄存器填充数据,且采用LSB优先的方式。需要设置第0位和第8位,以配置编码器协议所使用的生成多项式(x^8 + x^0)。
随后,程序开始准备接收编码器发来的第一个数据报(控制字段 CF)。
; reset timer and set timer limit to middle of bit LDI 18, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W ; wait for falling edge (start bit) WAIT0SF WAIT_IN1, WAIT_START_TIMER ; wait for middle of start bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION ; set limit to 100MHz / 39 = 2.5MHz and no timer reset LDI 39, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W REP 8, 1 ; wait for timer overflow and shift in sync code 010 and cc0..cc4 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r1 ; CF -> r1 ; wait for timer overflow, stop bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_STOP_TIMER
配置系统定时器后,程序进入等待状态(WAIT0SF),监测DIRECT_IN(1)上编码器回复消息起始位的下降沿,一旦检测到便立即启动定时器(WAIT_START_TIMER)。定时器已预先设置为大约在前一次溢出时间的一半时溢出(将限值设为18,而非39),以此到达起始位的中间位置。一旦到达该位置,限值会重新设为39,以便在每个输入数据位的中间位置触发一次定时器溢出事件(图9)。接下来,通过重复命令REP 8, 1配合右移输入命令SHRI,将后续8位输入数据以LSB优先的方式移入系统寄存器r1,同时将这些数据传入CRC单元进行CRC计算。当到达最终停止位的中间位置时,定时器停止。此时,接收的控制字段数据已存入寄存器r1中,且针对接收的前8位数据计算得到的CRC校验和也已生成。
编码器回复的第二个数据报包含状态字段(SF),对应预期接收的第二个字节。再次将定时器溢出时间设为起始位的中间位置,一旦在DIRECT_IN(1)上检测到下降沿,立即启动定时器。
; reset timer and set timer limit to middle of bit LDI 18, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W ; wait for falling edge (start bit) WAIT0SF WAIT_IN1, WAIT_START_TIMER ; wait for middle of start b WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION ; set limit to 100MHz / 39 = 2.5MHz and no timer reset LDI 39, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W REP 8, 1 ; wait for timer overflow and shift in dd0..dd3, ea0..ea1, ca0..ca1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r2 ; SF -> r2 ; wait for timer overflow, stop bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_STOP_TIMER
通过REP指令配合SHRI指令,将8位数据移入寄存器r2,同时传入CRC单元。当到达停止位的中间位置时,定时器停止。最终,接收的状态字段存入寄存器r2中,此时CRC校验和已基于控制字段的8位数据与状态字段的8位数据完成计算。
接下来,预期接收的是单圈内24位绝对编码器位置值,该值分为三个数据报,每个数据报包含一个字节,且采用LSB优先的顺序(分别为ABS0、ABS1、ABS2)。这段程序代码的结构与此前处理状态字段数据报的代码极为相似。
; reset timer and set timer limit to middle of bit LDI 18, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W ; wait for falling edge (start bit) WAIT0SF WAIT_IN1, WAIT_START_TIMER ; wait for middle of start bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION ; set limit to 100MHz / 39 = 2.5MHz and no timer reset LDI 39, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W REP 8, 1 ; wait for timer overflow and shift in d0..d7 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r3 ; ABS0 -> r3 ; wait for timer overflow, stop bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_STOP_TIMER
编码器位置信息依次存储在系统寄存器r3(ABS0)、r4(ABS1)以及最后的r5(ABS2)中。CRC校验和的计算会持续进行,将这三个字节也纳入校验范围。
作为ID代码0请求的编码器回复的最后一个数据报,系统将接收CRC校验和。该数据报的处理代码与前几个数据报的处理代码结构基本相同。
; reset timer and set timer limit to middle of bit LDI 18, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W ; wait for falling edge (start bit) WAIT0SF WAIT_IN1, WAIT_START_TIMER ; wait for middle of start bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION ; set limit to 100MHz / 39 = 2.5MHz and no timer reset LDI 39, r0 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W REP 8, 1 ; wait for timer overflow and shift in rc0..rc7 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1, r6 ; CRC -> r6 ; wait for timer overflow, stop bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_STOP_TIMER
请注意,此次通过SHRI指令移入寄存器的位不会同时并行移入CRC单元(移位命令SHRI的最后一个参数使用FLAG_IN1而非FLAG_IN1_CRC)。随后,接收到的CRC值将存储在系统寄存器r6中。
至此,所有接收到的数据均已存储在系统寄存器r1至r6中。下一步,这些值将被复制到SPI缓冲区,以供连接的微控制器/运动控制器读取。
; copy reply to SPI transmit buffer LDI %0001_0000, r0 ; its the ABS value ST SPI_BUFFER_3, r0 ; 0x10 -> ABS value ST SPI_BUFFER_2, r3 ; ABS0 ST SPI_BUFFER_1, r4 ; ABS1 ST SPI_BUFFER_0, r5 ; ABS2
第一个32位SPI数据报的低24位填充绝对位置信息,MSB设置为固定值0x10。当读取该SPI数据报时,可通过此固定MSB值明确识别数据报内容。
; copy crc value to SPI transmit buffer LDI %0111_0000, r0 ; crc values ST SPI_BUFFER_3, r0 ; 7 -> CRC + STATUS Field ST SPI_BUFFER_2, r2 ; LDS SYSTEM_CRC, SYSTEM_CRC_RESULT0_R, r0 ST SPI_BUFFER_1, r6 ; CRC received REV r0, r0 ST SPI_BUFFER_0, r0 ; CRC calculated JA CMD_LOOP
第二个32位SPI数据报的LSB包含CRC系统模块计算得出的CRC值。通过指令“LDS SYSTEM_CRC, SYSTEM_CRC_RESULT0_R, r0”,将计算得到的CRC值复制到系统寄存器r0中。计算得到的CRC值的位顺序与接收到的CRC值相反,因此使用指令“REV r0, r0”对寄存器位进行反转。完成之后,寄存器r0中存储的计算CRC校验和应与接收到的CRC值完全一致(前提是数据传输过程未受干扰)。
在实际应用中,需要对比计算出的CRC校验和与接收到的CRC校验和。若两者不匹配,可能意味着整个数据报/编码器信息存在异常,此时可选择丢弃/忽略该信息(例如设置错误标志)。
在本示例代码中,32位SPI数据报的LSB填充计算出的CRC值,其相邻的高字节填充接收到的CRC码,再高一位的字节则复制了接收到的状态字段位(SF)。最后,将数据报的MSB设置为固定值0x70,以此标识该数据报的内容,之后再将其复制到SPI输出缓冲区中。
附录
汇编器
为便于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
预处理器会将此命令之后在源文件(+通过#include语句包含的文件)中找到的任何<label>替换为 [<replacement text>]。替换文本为<label>后至少以一个空格分隔的、直至行尾(或注释起始处)的字符序列。仅注释内容和引号内文本不参与自动替换。标签可在源文件中通过另一个带有相同<label>的#define命令重新定义。
此外,允许定义无替换文本的<label> ,此时替换文本为空。这在配合条件预处理器命令(如#ifdef/#ifndef)时可能会很有用。
#ifdef, #ifndef, #else, #endif
#ifdef <label> <code block 1> #else <code block 2> #endif
若<label> 此前已被定义,则会解析<code block 1> 的内容并生成汇编输出,同时忽略<code block 2>。中间文件(“
#ifndef <label> <code block 1> #else <code block 2> #endif
若 <label> 此前已被定义,则会解析<code block 2>的内容并生成汇编输出,同时忽略<code block 1>。中间文件(
注:在使用#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命令的地址)。两种跳转方式均受支持:一是向后跳转(如上例所示),即标签在实际作为指令一部分使用之前已完成初始化;二是向前跳转,即标签在被某条指令引用之后才完成初始化。














