AN-2636: BiSS-C Absolute Encoder Protocol Support for TMC8100

Description

The TMC8100 includes a programmable microcontroller optimized for serial synchronous and asynchronous absolute encoder protocols up to 16Mbit/s. It can replace dedicated encoder protocol interface ICs and field-programmable gate array (FPGA) implementations while supporting in-system updates for different encoder capabilities or for switching to other encoder protocols. The TMC8100 is a compact, cost-effective, and flexible communication solution for adding absolute encoder support to industrial drives.

This application note provides details on the TMC8100 software implementation example supporting the absolute position encoder with a BiSS-C interface.

an-2636-tmc8100-eval-kit-absolute-encoder-biss-c-connection.png
Figure 1. TMC8100-EVAL-KIT Connection with Absolute Position Encoder with BiSS-C Interface

System Description

Servo motor drives (example, in industrial applications) typically require accurate, reliable, and low-latency position feedback. For a long time, optical encoders with incremental A/B/N output have been standard in the industry. Nevertheless, absolute position encoders are gaining traction, often with vendor-specific additional functionality and different interface protocols. An example is the BiSS-C protocol for synchronous serial transfer of digital data between the encoder and controller. For point-to-point operation, it uses two separate connections: for the clock generated by the controller and for the encoder data shifted out from the encoder. The physical layer is based on the RS-422 standard, and can transmit position values and diagnostic information from the encoder to controller. It can also read and write registers inside the encoder (example, for encoder configuration).

This example focuses on reading and extracting the encoder position and status information.

The encoder with the BiSS-C interface connects to the TMC8100-EVAL-KIT through a single cable with six wires (Figure 1). The six wires are:

  • +5V and GND: Encoder power supply and ground connection.
  • MA / CLK+ and CLK-: Differential RS-422 signals for transmission of clock from controller to encoder.
  • SL / DATA+ and DATA-: Differential RS-422 signals for transmission of data from encoder to controller.

The reference implementation features:

  • Output clock signal with required frequency, number of pulses, and polarity.
  • Separate internal sample clock for incoming data synchronized to the header of the incoming data for line delay compensation.
  • On-the fly CRC checksum calculation for incoming data.
  • 1Mbps/2Mbps/5Mbps/10Mbps data rate examples, as supported by the BiSS-C encoder.
  • Packing and unpacking of data.
  • Conversion of 13-bit encoder position data from gray code to binary code.

The reference implementation is available as source code. Users may use this as a starting point and apply changes as required by the application.

System Overview

The provided software has been designed to operate in conjunction with the TMC8100-EVAL-KIT and tested with the WDGF 58M-10-1300-11-BIA-G21-T3 encoder from Wachendorff with the BiSS-C interface.

an-2636-core-hardware-components-and-connections.png
Figure 2. Core Hardware Components and Connections

The core hardware components required from the TMC8100-EVAL-KIT are the TMC8100 and the two RS485 transceivers for converting between the TMC8100, and the differential RS485 signals MA (CLK+ and CLK-) and SL (DATA+ and DATA-) at the connector (Figure 2). For this protocol, the RS485 transceiver for the MA/CLK signals is configured as output/transmitter, and the RS485 for the SL/DATA signal as input/receiver permanently.

The software includes the firmware for the TMC8100, with the implementation of the controller functionality for the BiSS-C point-to-point protocol support. This firmware must be downloaded to the TMC8100 after power-up. An additional GUI (graphical user interface) for selecting and downloading the firmware and demonstrating and testing the functionality of the firmware afterwards together with an encoder is available as Python script.

BiSS-C Protocoll

BiSS (Bidirectional/Serial/Synchronous) is a digital, serial interface protocol for fast process data transmission particularly used in motor feedback systems. Together with the transmission of, example, encoder data and status information, the BiSS protocol supports register read and write operations without interrupting the encoder data stream. This application note focuses on point-to-point communication between the TMC8100 as the controller and the encoder with the BiSS-C interface, and the read-out of the encoder position data and status information.

Reading encoder data is controlled by the TMC8100 by sending out clock signals using MA (connector pins CLK+/CLK-). The encoder then generates its reply signal on the SL output (connected to pins DATA+/DATA-), shifting out the serialized information with the rising edge of the incoming clock signal.

The transfer of the encoder information in one frame can be divided into four main phases: Idle, header, data channels, and timeout (Figure 3).

an-2636-biss-frame-example-wdgf-58m-encoder.png
Figure 3. BiSS Frame Example with Encoder WDGF 58M-10-1300-11-BIA-G21-T3

When idle, there is no data transmission, and both lines, MA and SL, are constantly high ('1'). To fetch a new encoder value, the TMC8100 generates clock pulses on the MA line. The first falling edge of the clock line indicates a new frame and start of the header. The first rising edge of the clock signal then triggers latching and processing of new position data within the encoder. With the second rising edge of the clock signal, the encoder pulls the SL line low (ACK bit(s), always '0').

With the first rising edge of the SL signal (START bit, always '1'), the encoder indicates the start of data transmission. As the encoder requires some time for internal processing, the start bit may be delayed at higher bus clock frequencies and more than one ACK bit inserted after the falling edge of the SL line (in Figure 3, there are two ACK bits before the START bit as example). The TMC8100 firmware uses this rising edge at the beginning of the START bit for synchronization with the incoming data stream and adjustment of the sample point, as the incoming data may be shifted/delayed with respect to the clock not just because of additional ACK bits but also due to signal travel time along the cable (line delay compensation).

The first bit shifted out by the encoder after the START bit is the CDS bit/part of the control communication from the encoder to controller. After this bit, the encoder position information (13-bit single turn/ST) is transferred with MSB first. Error (ERR) and Warning (WARN) bits follow, which are low active ('1' – no error/warning). Finally, the 6-bit CRC checksum is transmitted. The checksum is calculated from the data channel information starting with the ST value using the CRC polynomial P(x) = x^6 + x^1 + x^0. The CRC result is inverted before transmission.

After this data, there is a timeout phase with SL pulled low ('0') by the encoder for a fixed amount of time. As soon as the SL line is pulled high ('1') again, another frame may be started for the next encoder value.

Software Overview

The firmware implementation for the BiSS-C protocol for the TMC8100 is available as source code "tmc8100-eval_biss-c_encoder_demo_v10.asm" and as machine code in intel hex file format "tmc8100-eval_biss-c_encoder_demo_v10.hex". For first tests/evaluation of the functionality, a Python script "tmc8100-eval_biss-c_encoder_demo_v10.py" is available. This is expected to be executed on a PC connected to the TMC8100-EVAL-KIT using a USB with the encoder attached, as shown in Figure 1.

The Python script program requires a Python interpreter installed on the PC and makes use of the "intelhex" and "pySerial" Python libraries, among others. It uses "tkinter" for the graphical user interface.

After connecting the encoder to the TMC8100-EVAL-KIT, the USB to the PC, and applying +5V to the TMC8100-EVAL-KIT, the Python script may be executed from the command line (Figure 4).

an-2636-tmc8100-eval-kit-python-script-startup.png
Figure 4. Start Python Script with TMC8100-EVAL-KIT Connected

At first, the virtual COM port for the USB connection to the TMC8100-EVAL-KIT must be selected. In this example, it is "COM6". The lines of output in the terminal window already indicate successful connection to the Landungsbruecke (LB) and detection of the TMC8100 with chip ID and revision number.

Afterwards, the graphical user interface automatically starts in a separate window.

an-2636-python-graphical-user-interface.png
Figure 5. Python GUI

First, the hex file "tmc8100-eval_biss-c_encoder_demo_v10.hex" with the example code for the TMC8100 must be selected with the "…" button in the "Select Input File" frame (1). By pressing the "Load + Execute" button (2) as the next step, the content of the file is written using the USB and Landungsbruecke (LB) into the SRAM program memory of the TMC8100 with the help of the bootloader. The program execution is started. Note that this program comes with its own communication protocol for encoder access. To put the TMC8100 into the bootloader mode again, example, for downloading a different program, a reset or power-cycle of the TMC8100 is required. Pressing "Load + Execute" in the Python GUI automatically resets the TMC8100 before downloading a new firmware.

an-2636-python-gui-firmware-loaded-encoder-readout.png
Figure 6. Python GUI with Firmware Loaded after Encoder Read-Out

The "BiSS-C" frame in the middle of the window offers two command buttons to initiate an encoder read-out. Pressing "Read Encoder" instructs the firmware loaded into TMC8100 to send out the respective number of clock cycles and read back the absolute position within one rotation (Figure 6 - shown next to the "ST"/encoder single turn label). This is shown here in gray code format (ST (raw/gray code/13-bit encoder value), as received directly from the encoder and as a 13-bit value after conversion (ST (converted/binary/13-bit encoder value)). The conversion is done inside the TMC8100 firmware and the original gray code number is transmitted (shown here just for reference).

The two error and warning bits transmitted during encoder read-out are displayed below the encoder position value. Both are '1' in this example (11b = 3d). They are transmitted low-active.

For debug purposes, the received CRC checksum and the calculated one (from the received ST and error/warning bits) are also displayed.

For information purposes, the number of clock edges the firmware uses for one BiSS-C frame/encoder read-out is reported.

Pressing "Read Encoder continuously" triggers the read-out of the encoder value continuously by the Python program (Figure 7). This updates the encoder position ST value (binary only) and an analog dial-wheel with a red position marker indicating the current absolute 360° angle position. The other values are not updated for performance reasons. To stop continuous encoder read-out and before pressing any other button, the Python program must be terminated/restarted.

an-2636-python-gui-continuous-encoder-readout-analog-dial-feedback.png
Figure 7. Python GUI with Continuous/Automatic Encoder Read-Out and Analog Dial Wheel Position Feedback

In parallel to the GUI with the extracted/relevant data, the raw communication data with some additional info is shown in the command line window, which might be helpful when modifying/extending the TMC8100 program example (Figure 8).

Note: Output to the command line window is suppressed during continuous encoder read-out for performance reasons.

an-2636-command-line-raw-data-encoder.png
Figure 8. Command Line Window with Raw Data Received from Encoder

Firmware Implementation

The example source code "tmc8100-eval_biss-c_encoder_demo_v10.asm" may be used as the starting point and modified according to application requirements. An Assembler is available to translate the source code.

The flow chart (Figure 10) gives an overview of the example code.

The firmware source code starts with the definition of some constant values (example, software version and protocol supported) and the register addresses of peripheral units inside the TMC8100 for better readability (Figure 9). 

an-2636-firmware-source-code-constant-values-definition.png
Figure 9. Firmware Source Code – Definition of Constant Values

To simplify configuration, the BiSS-C clock frequency (BISS_FREQ_DIV) can be set here and the number of clock edges for one frame (BISS_CLK_EDGES). Constant "BISS_FREQ_DIV" is the divider for the system clock frequency (set to 100MHz for this example firmware program). Example, to get a BiSS-C clock frequency of 1MHz, the divider must be set to 100 - 1 (as the clock divider counts from 0 up to the limit given by BISS_FREQ_DIV). The number of clock edges (BISS_CLK_EDGES) can be set to a minimum of 52 at lower BiSS-C frequencies with this encoder and must be set to a higher value at higher bus frequencies for proper data transfer due to the internal processing delay with additional ACK bits. As the idle state of the clock line is '1' before and after transmission of one BiSS-C frame, the number of clock edges should be always even.

an-2636-firmware-implementation-overview.png
Figure 10. Firmware Implementation Overview

SPI Configuration

The standard SPI signals (SPI_CSN, SPI_SCLK, SPI_SDI, and SPI_SDO) available with the TMC8100 do not require any configuration. They have fixed functionality and pin assignment for the package. Nevertheless, an additional signal, SPI_DATA_AVAILABLE, is available that can be configured as an alternate function of pin GPIO(6) and indicates data that is written to the SPI output buffer (output high '1') by the firmware inside the TMC8100. This allows for feedback to the attached microcontroller, which may then initiate an SPI datagram/transaction to fetch the reply data for a previously transmitted command from the TMC8100. This functionality is already configured by the bootloader after power-up and is included here for completeness (Figure 11).

an-2636-spi-configuration.png
Figure 11. SPI Configuration

Clock Selection and Initialization

The TMC8100 always starts running on the internal oscillator and the bootloader configures the PLL for a system clock frequency of 75MHz after power-on/reset. In this example, the crystal oscillator is used with the 16MHz crystal available on the TMC8100-EVAL-KIT. The PLL output and system frequency are set to 100MHz to simplify clock calculation/divider settings afterwards. For the BiSS-C interface, a crystal clock is not strictly necessary as the reference clock for communication is provided by the TMC8100 itself. Therefore, using the internal clock of the TMC8100 together with the PLL might be an alternative here.

As first step, pins GPIO0 and GPIO1 are configured for an external crystal in combination with the internal crystal oscillator (Figure 12).

an-2636-gpio0-gpio1-external-crystal-configuration.png
Figure 12. Configure GPIO0/1 for External Crystal

As next step, the PLL feedback divider is configured for 100MHz PLL output frequency (PLL_FB_100), and the clock circuitry for the crystal oscillator (XTAL) and the PLL input divider are set to get 1MHz clock frequency at the input of the PLL (Figure 13). As the clock block is addressed indirectly for each register write access, four commands are necessary: a pair of load (LDI) and store (ST) instructions to set the register address first, and afterwards a pair of load and store instructions to set the new register value.

an-2636-xtal-pll-100mhz-system-clock-configuration.png
Figure 13. Configure XTAL and PLL for 100MHz System Clock

The last write access also triggers the internal state machine of the clock block to apply all the changes. As this includes the start-up of the crystal oscillator and PLL lock, it is necessary to check the status register of the clock block and wait until the new 100MHz system clock is available (Figure 14). Therefore, the address of the configuration register (CLK_CTRL_PLL_CFG) of the clock block is selected and a program loop reads out this register until bit-7 (TEST1 $7, r0) is cleared before moving on with further program execution.

an-2636-pll-enable.png
Figure 14. Enable PLL

DIRECT_IN/DIRECT_OUT Pin Configuration

As shown in Figure 2, both the RS485 transceivers on the TMC8100-EVAL-KIT are used for communication with the encoder. The upper one in the block diagram is used for transmitting the clock signal from the TMC8100 to the encoder. Transmit enable is switched on permanently for this RS485 transceiver. DIRECT_OUT(0) is used here for clock output (alternate function of this output selected with command "ST DIRECT_ALT_FUNCITON, r0").

The lower RS485 transceiver in the block diagram is used for receiving data from the encoder. The transmitter is permanently disabled here. DIRECT_IN(1) is used for serial data input, while DIRECT_OUT(1) and DIRECT_OUT(3) are not necessary in this application, and are set to fixed levels (both low '0').

As the clock line (MA/CLK+) is at high level '1' in idle state, the corresponding output DIRECT_OUT(0) is inverted.

an-2636-direct-in-direct-out-configuration.png
Figure 15.Configure DIRECT_IN/DIRECT_OUT

SPI Command Loop

After the configuration of the TMC8100, the program waits for commands received through the SPI in an endless loop. All SPI transactions are expected to be 32-bit datagrams. All commands of this example code fit into one datagram. For simplification, just the upper 8-bit (MSB, received first through SPI) is tested for command selection and execution. The command loop starts with reading out the status register (SPI_STATUS) of the SPI peripheral block and waiting until bit-0 of the status register changes to one (WAIT1 $0, r0), indicating that an SPI datagram is received and available in the SPI input buffer (SPI_BUFFER).

an-2636-spi-command-loop.png
Figure 16. SPI Command Loop

The upper 8-bit of the contents of the 32-bit SPI input buffer are compared against $80 (the SPI command defined in this example code for reading out the encoder data, including the flags and position value). If this comparison is successful, program execution jumps to address "BISS_encoder". The program code at this address (described in more detail as folowing) then sends out the predefined number of clock cycles using DIRECT_OUT(0) and collects the reply data from the encoder using DIRECT_IN(1). The received data is assembled into 32-bit SPI datagrams and put into the SPI output buffer. At the same time, SPI_BUFFER_AVAILABLE/GPIO6 changes from low '0' to high '1' to indicate new data available for SPI transaction. With the next SPI transaction(s), this data can be read out. It is expected that a new command is not sent before all the data from the previous command is read out. As one SPI transaction always transfers data in both directions and there is usually more than one transaction necessary to read out all the reply data, it is recommended to use a "dummy" command (example, 0x00 0x00 0x00 0x00), which is not interpreted by the command loop for the additional read-outs. The last transaction for read-out may already include the next command.

The Python script available to test the encoder implementation "tmc8100-eval_biss-c.py" with the TMC8100-EVAL-KIT offers a GUI with a "Read Encoder" button. Pressing this instructs the Landungsbruecke (LB) to send out an SPI datagram 0x80 0x00 0x00 0x00 (extract from "tmc8100-eval_biss-c.py").

an-2636-python-read-encoder-position-values.png
Figure 17. Python - Read Encoder Position Values

Afterwards, the program waits for the reply of the encoder. The pin SPI_DATA_AVAILABLE/GPIO6 switches from low '0' to high '1' as soon as the reply data is available in the SPI output buffer of the TMC8100. For reading out this data, the Python program uses the SPI "dummy" commands 0x00 0x00 0x00 0x00 not interpreted by the TMC8100 firmware.

 # 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 + Warning, CRC received and CRC calculated
 value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
 print(f"Error + CRC: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
 . . . .
 # get number of clock edges
 value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
 print(f"Clock edges: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
 . . . .

The following table provides an overview of all the SPI commands supported by the example program and reply data available. SPI commands always fit into one 32-bit datagram, while the reply may take up to four 32-bit datagrams (in this example). SPI 32-bit datagrams are given as four consecutive hex numbers (one hex number per byte with the MSB first for better readability). For the reply, the 32-bit datagrams are shown in the order they are put into the SPI buffer/can be read out (first-in first-out (FIFO)).

Table 1. Overview of all the SPI Commands Supported by the Example Program and Reply Data Available
SPI-COMMAND (32-BIT) SPI-REPLY (32-BIT)
0x80 0x00 0x00 0x00
Read encoder absolute position value within one rotation (ST) and flags/status/information.
  1. 0x10 0x00 ST (MSB) ST (LSB)
    (Raw position value in gray code from encoder)
  2. 0x20 0x00 ST (MSB) ST (LSB)
    (Converted binary encoder position value)
  3. 0x70 Flags CRC(RX) CRC(CALC)
    Warning/Error and CRC (received and calculated)
  4. 0x71 0x00 0x00 EDGES
    Number of clock edges sent to encoder
0xff 0x00 0x00 0x00
Get firmware version.
  1. 0xff 0x00 VERSION_MAJOR VERSION_MINOR
0xfe 0x00 0x00 0x00
Get encoder protocol.
  1. 0x42 ("B") 0x49 ("I") 0x53 ("S") 0x53 ("S")

When pushing "Read encoder continuously", the first command in the table 0x80 0x00 0x00 0x00 is sent repeatedly by the Python program.

Read Encoder Data

As soon as a new SPI command is received while the TMC8100 software is waiting for SPI commands, and the upper byte/ byte received first from the 32-bit datagram is equal to $80, the program execution jumps to address "BISS_encoder" in program code, and starts with initializing the CRC block for on-the-fly CRC calculation while the serial data is shifted in. The CRC polynom is set to P(x) = x^6 + x^1 + x^0 and the start value is set to zero.

BISS_encoder:
      . . .
      ; initialize crc block
      LDI $0, r0
      STS $0, SYSTEM_CRC, SYSTEM_CRC_CTRL_W ; reset CRC block
      LDI $0, r0
      STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W ; LSB, initialize CRC start with zero
      STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W
      STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W
      STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W ; MSB
      LDI %0100_0011, r0 ; CRC polynomial: x^6 + x^1 + x^0
      STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; LSB
   LDI %0000_0000, r0
   STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W
   STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W
   STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; MSB

With the BISS_FREQ_DIV and BISS_TOGGLE_DIV constant settings in the beginning of the program source file, the BiSS-C clock output signal on DIRECT_OUT(0) is configured for 1MHz. At each overflow of the system counter, the clock output toggles. Therefore, the system counter is set to double the output clock frequency using BISS_TOGGLE_DIV (in this case, 2MHz).

The number of clock edges is defined using the BISS_CLK_EDGES constant. For a 1MHz BiSS-C clock frequency, this is set to 54 edges in this example. At higher clock frequencies, the number of edges must be increased (examples given in the source code) to compensate for the additional ACK bits/longer header due to the internal processing time of this encoder. The number of edges (clock line rising and falling) should be always an even number to make sure the clock line is high '1' during idle state before and after transmission of one frame.

As final step of the system timer, counter initialization clock generation is started.

; init counter
LDI BISS_TOGGLE_DIV, r0 ; set toggle rate for BiSS clock generator
STS r0, SYSTEM_TIMER, SYSTEM_TIMER_COUNTER_LIMIT_W
; number of clock edges:
LDI BISS_CLK_EDGES, r0 ; number of rising and falling edges
STS r0, SYSTEM_TIMER, SYSTEM_TIMER_PULS_COUNTER_LIMIT_W
LDI 1, r0 ; enable counter
STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W

As next step, the reply data from the encoder is captured on input DIRECT_IN(1).

The firmware waits until the feedback data signal SL/DATA+ changes from high '1' to low '0' indicating the start of acknowledge from the encoder. At this point, the encoder has already internally latched/started processing of the new encoder value. With the data signal rising again, the actual data transmission begins with the START bit and the firmware sets the internal system timer to overflow at half of one clock cycle/at the middle of the START bit. The system timer uses the same clock as the system counter generating the BiSS-C clock to get the same time measurements (just shifted in time in case of additional ACK bits and signal propagation delay due to the length of the encoder cable). At the middle of the START bit, the timer limit value is updated to one full clock cycle. This way, the next overflow of the system timer happens at the middle of the next bit (CDS).

an-2636-encoder-serial-data-in.png
Figure 18. Encoder Serial Data In
 LDI BISS_TOGGLE_DIV, r0 ; from rising edge of start bit to middle of start bit
 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W
 WAIT0SF WAIT_IN1, WAIT_NO_ACTION ; wait for falling edge of data signal '0'
 WAIT1SF WAIT_IN1, WAIT_START_TIMER ; wait for rising edge of start bit '1' and start timer
 ; wait until middle of start bit
 WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION
 LDI BISS_FREQ_DIV, r0 ; set sample point to middle of next bit / 1 clock cycle
 STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W
 LDI 0, r1
 ; middle of CDS - shift CDS into r1
 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r1, FLAG_IN1
 ; encoder data 13bit
 ; MSB - shift upper 5bit of encoder data into r2
 LDI 0, r2
 REP 5, 1
 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r2, FLAG_IN1_CRC
 ; LSB - shift lower 8bit of encoder data into r3
 LDI 0, r3
 REP 8, 1
 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r3, FLAG_IN1_CRC
 ; Error + Warning - shift bits into r4
 LDI 0, r4
 REP 2, 1
 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r4, FLAG_IN1_CRC
 ; CRC - shift 6 CRC bits into r5 / received CRC
 LDI 0, r5
 REP 6, 1
 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r5, FLAG_IN1

The CDS value is shifted into system register r1 at the overflow of the system timer with command SHLI WAIT1SF, WAIT_OVERFLOW_TIMER, r1, FLAG_IN1.

After the CDS bit, the next 13-bits belong to the encoder position data encoded in gray code. They are shifted in two chunks. First the 5-bits of the MSB are shifted into register r2 and at the same time into the CRC unit for on-the fly CRC calculation using the command SHLI WAIT1SF WAIT_OVERFLOW_TIMER r2, FLAG_IN1_CRC repeated five times (with command REP 5, 1). Afterwards, the lower 8-bits are shifted into system register r3 and the CRC unit.

The encoder position value is followed by two status bits (Error and Warning) shifted into r4 (and the CRC unit), and finally the 6-bit CRC code shifted into register r5 (received CRC).

Convert Gray Code to Binary Code

As the next step, the encoder counter value in r2 and r3 is converted from gray code to binary code. The conversion is done bit-by-bit starting with the MSB in r2 and in-place (the result overwrites register contents r2 and r3). To remember the original/encoder counter value received for debug purposes, the contents of register r2 and r3 are copied to the data memory before conversion.

The bit-by-bit conversion uses the algorithm shown as following. This algorithm offers a high degree of flexibility and can be easily adjusted to different bit length.

an-2636-gray-code-to-binary-conversion-algorithm.png
Figure 19. Gray Code to Binary Conversion Algorithm

Source code example for converting one bit:

TEST1 7, r2
MOVF 0, r0
TEST1 6, r2
MOVF 0, r1
XOR r0, r1, r0
TEST1 0, r0
MOVF 6, r2

The MSB of r2 is tested (TEST1 7, r2) and the processor flag set according to the value of the bit. The flag is then copied to bit 0 of the auxiliary register r0 (MOVF 0, r0). With the next two instructions, bit 6 of register r2 is copied to bit 0 of register r1 using the same approach. Bitwise XOR operation is then applied to registers r0 and r1, and the result written back to r0. The value of bit 0 in r0 is then copied back to the system flag again (TEST1 0, r0) and finally copied to bit 6 of register r2. These steps are then repeated for the remaining bits in r2 and afterwards for r3.

SPI Reply Datagram

All data received and calculated is now available. As the next step, these values are copied to the SPI buffer for read-out by the attached microcontroller/motion controller.

The first 32-bit SPI datagram is filled up with the absolute position information in gray code (received value from encoder) copied to the lower 16-bit and a fixed value of 0x10 as MSB. When the SPI datagram is read out, this fixed MSB value can be used to clearly identify the contents of this datagram.

; copy ST / raw / gray code to SPI transmit buffer
LDI $10, r0
ST  SPI_BUFFER_3, r0
LDI $0, r0
ST  SPI_BUFFER_2, r0
LD  DATA_MEM_ADDR, r0
LD  DATA_MEM_ADDR + 1, r1
ST  SPI_BUFFER_1, r0
ST  SPI_BUFFER_0, r1

The second 32-bit SPI datagram contains the converted absolute position information in binary code (r2|r3) for the lower 16-bit and a fixed value of 0x20 as MSB.

; copy ST / binary code to SPI transmit buffer
LDI $20, r0
ST  SPI_BUFFER_3, r0
LDI $0, r0
ST  SPI_BUFFER_2, r0
ST  SPI_BUFFER_1, r2
ST  SPI_BUFFER_0, r3

The third 32-bit SPI datagram contains the Error and Warning bits, and the CRC checksum value received and calculated (LSB). The calculated checksum is inverted before transmission to match the received CRC checksum. The MSB contains a fixed value of 0x70.

; copy status and crc value to SPI transmit buffer
LDI $70, r0 ; crc values
ST  SPI_BUFFER_3, r0 ; 7 -> CRC + STATUS Field
ST  SPI_BUFFER_2, r4 ; Error + Warning
LDS SYSTEM_CRC, SYSTEM_CRC_RESULT0_R, r0
ST  SPI_BUFFER_1, r5 ; CRC received
LDI %0011_1111, r1
XOR r0, r1, r0 ; CRC is inverted
ST  SPI_BUFFER_0, r0 ; CRC calculated

Finally, the fourth 32-bit SPI datagram contains the number of clock edges set for constant BISS_CLK_EDGES in the source code for debug purposes.

; copy number of clock edges (r7) to SPI transmit buffer
LDI $71, r0 ; clock edges values
ST  SPI_BUFFER_3, r0 ;
LDI $0, r0
ST  SPI_BUFFER_2, r0
ST  SPI_BUFFER_1, r0
LDI BISS_CLK_EDGES, r0
ST  SPI_BUFFER_0, r0

There is another version of the example source code file available ("tmc8100-eval_biss-c_encoder_demo_with_ACK_length_measurement.asm") that measures the number of ACK bits from the encoder as part of the first encoder read-out after power-up/firmware download, and calculates the number of rising and falling edges accordingly for all further BiSS frames. This can give an indication of how many clock cycles are required as a minimum for reliable communication with the encoder.

Appendix

Assembler

For program development and modification of the examples/reference programs for the TMC8100, an assembler is available. This PC-based command line tool expects as parameter the file name and optional path of the assembler source code file. In case the file with the provided file name is not found, the ending *.asm is added automatically to the provided file name. From this input file, the assembler generates the output file(s).

In this example, the name of the input file with the assembly source code is "tmc8100-eval_abn_demo.asm". The number of machine instructions (16-bit) generated from this file is 83, which is 4.1% of the available program memory (SRAM). The size of the program memory in the TMC8100 is 2K × 16-bit (a maximum of 2048 instructions).

There is one generated default output file that has the name of the input file with *.hex as extension instead of *.asm. This output file is a text file with the program code in standard intel hex file format, example, for loading the program code into the program memory of the TMC8100 on the TMC8100-EVAL-KIT using the TMCL-IDE or one of the Python scripts available with the example code.

The assembler also supports a number of flags for generating additional output files in different formats and additional information.

  • Option "-i": file "<filename>.i" is generated as output from the preprocessor. Contains the contents of the assembly source file with all preprocessor commands (# …) being processed (example, "#include" file contents merged into the source file) and comments (example, /* .. */ | // | ;) removed.
  • Option "-l": file "<filename>.log" is generated with the assembly source code (without comments and preprocessor commands) with additional instruction encoding and the program memory address of the instructions.
  • Option "-c": file "<filename>.c" is generated with C-code to support program development for the controller used to bootstrap the TMC8100. This file contains an array of integer numbers with the generated machine code.
  • Option "-m"—machine flag. For support of different implementations/instruction set (default is TMC8100).
  • Option "-h"—print help test as shown in the screenshot

Syntax/Commands Supported


The assembler currently supports all TMC8100 instructions as described in the TMC8100 data sheet.

Preprocessor Commands

Comments


The preprocessor removes all comments from the input file(s) for further processing. Currently, the following options are supported:

  • /* <comment> */ - block comment - can include more than one line (C-language style)
  • // <comment> - comment until end of line (C/C++ - language style)
  • ; <comment> - comment until end of line

#include

#include "<filename>"—contents of file given with name and optional path in <filename> is inserted at the position of the #include preprocessor directive for further processing. The line with the #include directive is removed.

Note: Only quotation marks are allowed around <filename> and there should be no other commands/assignments within this line as they are removed/ignored from further processing.

#define

#define <label> [<replacement text>]

The preprocessor replaces any <label> found in the source file (+ files included with the #include statement) after this command with <replacement text>). As replacement text, the character sequence after <label> separated with at least one space from <label> until end of line (or start of comment) is taken. Only comments and text in quotation marks are excluded from automatic replacement. Label(s) may be redefined afterwards in the source file using another #define with the same <label>.

It is possible to define a <label> without replacement text. In this case, the replacement text is empty. This might be useful, example, in combination with the conditional #ifdef / #ifndef preprocessor commands.

#ifdef, #ifndef, #else, #endif


#ifdef <label> <code block 1> #else <code block 2> #endif

In case <label> is defined earlier, the contents of <code block 1> are interpreted and assembler output generated and <code block 2> is ignored. The intermediate file ("<filename>.i") just shows <code block 1> and not <code block 2>. The code blocks may contain several lines of assembler instructions, and more. In case <label> is not defined, it is the other way round.

#ifndef <label> <code block 1> #else <code block 2> #endif

 

In case <label> is defined earlier, the contents of <code block 2> are interpreted and assembler output generated and <code block 1> is ignored. The intermediate file (<filename>.i) just shows <code block 2> and not <code block 1>. The code blocks may contain several lines of assembler instructions, and more. In case <label> is not defined, it is the other way round.

Note: It is sufficient to mention the <label> name before using #define <label>. It is not necessary to provide a replacement text/value.

The #else part is optional. #ifdef or #ifndef blocks may be nested.

Assembler Commands

Numbers


To simplify specification of numbers as binary, decimal, and hexadecimal, different formats are supported. These are as follows:

  • 0x123.. or $123.. are interpreted as hexadecimal numbers (character expected: 0 to 9, a to f or A to F).
  • %1010.. is interpreted as binary number (characters expected: 0 and 1). The character '_' can be inserted for better readability (example, %1010_0011 for an 8-bit number).
  • 123.. is interpreted as decimal number (characters expected: 0 to 9).

Identifiers


Identifiers may be used for better readability instead of numbers. Identifiers must start with a letter (a to z/A to Z) or '_'. Afterwards, numbers are also allowed (0 to 9). Note that character names are not case sensitive.

Identifiers may be assigned a value using the equal sign. For example,

SPI_BUFFER_0 = $30

Some names should not be used as identifiers:

Assembler instructions. This includes all names listed in the TMC8100 data sheet and also the instruction names preceded with a 'C' (indicating conditional execution of the command).

Identifier r0...r7 are predefined as register 0...7 for specifying all general-purpose registers available.

Labels and identifiers should not have the same name.

Labels


Labels are used as placeholders for program memory addresses. Labels do not have to be assigned a value. They are initialized with the current program memory address while the assembler translates the assembly source into machine code.

Example for an endless loop:

WAIT:
  JA WAIT

Note the ':' character after the label name. The assembler automatically initializes the label WAIT with the program memory address of the next instruction (which, in this case, is the JA WAIT command). Jump backs (as shown above), where the label is initialized before it is actually used as part of an instruction and jump forwards (where the label is initialized after it is referenced with an instruction) are both supported.