ADI Trinamic Blog

We transform digital information into physical motion.

 
  • Newsletter
  • Blog Ethics
  • Legal/Impressum
  • Main Website
Menu
  • Newsletter
  • Blog Ethics
  • Legal/Impressum
  • Main Website
  • Real-time Motor and Motion Control with a Raspberry Pi without a real-time OS

    Setup Example

    Single-board computer (SBC) gained more and more popularity during the last years due its opensource software and the simple hardware. With these SBC it is possible to develop quickly and easily prototype systems or even full products. The standard Linux distributions have no real-time capabilities. For many projects like a digital weather station, this is not crucial. For motion control, on the other hand, it is mandatory to have real-time access for making clean acceleration ramps, position control and closed-loop operation. All of this is possible with motion control ICs from TRINAMIC without needing any real-time operating system on the SBC.

    The project below shows how to do precise autonomous cyclic movements with the TMC4361A without any MCU power. The Raspberry Pi 3 is used to set up the needed registers and from there the TMC4361A takes care of the movement. The used system consists besides the Raspberry Pi 3 of a TMC4361A-BOB and a TMC5160-BOB, which is used a plain pre-driver stage, so the internal motion controller is deactivated. The usage of the TRINAMIC’s API – TMC-API – is minimizing the software effort.

    The motor will do alternating exactly 5 rotations at 2.5 RPS and 5 rotations at 5 RPS. The switching points are handled by the TMC4361A in hardware. So there is no need for real-time software capabilities. The following steps need to be done to setup the TMC4361A:

    1. Basic configuration of TMC4361A for controlling the TMC5160 family in step/dir mode
    2. Basic configuration of TMC5160 for step/dir mode via cover datagrams
    3. Activation of circular motion, see chapter 8.5.2. of TMC4361A Datasheet
    4. Setting up a one stage shadow register set, see chapter 9.2.1 of TMC4361A Datasheet
    5. Configuring a cyclic pipeline structure, see chapter 9.3.4. and 9.3.5. of TMC4361A Datasheet

    Software Preparation

    In this tutorial we are using a fresh Raspbian Stretch System (Version November 2017 – Release date 2017-11-29) with the latest updates:

    sudo apt update
     
    sudo apt upgrade

    Download and install the bcm2835 library. Note XXX is a placeholder for the latest version. For this guide, version “1.52” was used. In this case, XXX is replaced by “1.52”. You can check the latest version by scrolling to the bottom of the following page: http://www.airspayce.com/mikem/bcm2835/

    wget http://www.airspayce.com/mikem/bcm2835/bcm2835-XXX.tar.gz
    
    tar zxvf bcm2835-XXX.tar
    
    cd bcm2835-XXX
    
    ./configure
    
    make
    
    sudo make check
    
    sudo make install

    Create a project folder and download the latest TMC-API. Note X.XX is a placeholder for the latest version. For this guide, version “3.02” was used. In this case, X.XX is replaced by “3.02”. You can check the latest version by scrolling to the bottom of the following page: https://www.trinamic.com/support/software/access-package/

    mkdir ~/TMC_EXAMPLE
    
    cd ~/TMC_EXAMPLE
    
    wget https://www.trinamic.com/fileadmin/assets/Products/Eval_Software/TMC-API_Release_vX.XX.zip
    
    unzip TMC-API_Release_vX.XX.zip
    
    mv TMC-API_Release_vX.XX TMC-API

    Enable step/dir Mode on TMC5160-BOB

    The TMC5160-BOB allows the TMC5160 to use in 2 different modes: With integrated motion controller and in step/dir mode. For controlling the TMC5160 by the TMC4361A the TMC5160 has to be in step/dir. On the TMC5160-BOB this can easily be done by desoldering the R2 and soldering it to R1.

    TMC5160-BOB: R1 and R2 are red marked 

    Wiring

    The wiring is very simple. You will need 18 jumper wires. As a reference, you can use the TMC4361A-BOB_datasheet_Rev1.0.pdf and TMC5160-BOB_datasheet_Rev1.0.pdf.

    Raspberry Pi 3 GPIO Header – Source: https://www.element14.com/community/docs/DOC-73950/l/raspberry-pi-3-model-b-gpio-40-pin-block-pinout

    Rasbperry Pi to TMC4361A-BOB

    Signal Raspberry Pi 3 TMC4361-BOB
    VCC_IO 3.3v (1) VCC_IO (1)
    GND GND (39) GND (2)
    TMC4361A CLK GPIO04 (7) CLK (3)
    TMC4361A NRST GPIO15 (10) NRST (4)
    SPI Chip select (CS) SPI0 CE0 N (24) CSN (5)
    SPI CLK SPI0 SCLK (23) SCK (6)
    MOSI SPI0 MOSI (19) SDI (7)
    MISO SPI0 MISO (21) SDO (8)

    TMC4361A-BOB

    Rasbperry Pi to TMC5160-BOB

    Signal Raspberry Pi 3 TMC5160-BOB
    VCC_IO GPIO02 (3) VCC_IO (1)
    TMC5160 CLK GPIO14 (8) CLK (8)
    TMC5160 DRV_ENN GPIO03 (5) DRV_ENN(9)

    TMC4361A-BOB to TMC5160-BOB

    Signal TMC4361A TMC5160-BOB
    GND GND (2) GND (2)
    SPI Chip select (CS) CSN_DRV (18) CSN (3)
    SPI CLK SCK_DRV (19) SCK (4)
    MOSI SDI_DRV (21) SDI (5)
    MISO SDO_DRV (20) SDO (6)
    STEP STPOUT (22) REFL (10)
    DIR STPDIR (23) REFR (11)

    TMC5160-BOB

    Raspberry Pi Code

    An example code to initialize the TMC5160 is shown below. These files need to be placed in the same project folder as the TMC-API, in this case into ~/TMC_EXAMPLE. First, +3.3V is supplied to VCC_IO, the driver stages are enabled by pulling down DRV_ENN and the internal clock is used by pulling down CLK16. Afterwards, the TMC5160 is initialized and a simple move to position cycle is executed. The sequence will rotate a 200 full stepper motor 10 revolutions clockwise and 10 revolutions counterclockwise – depending on the wiring of the stepper motor. Please refer to the TMC5160 datasheet or the TMCL-IDE as a reference for the different registers.

    You can also download the source files directly with your Pi:

    cd ~/TMC_EXAMPLE
    
    wget http://blog.trinamic.com/wp-content/uploads/2018/02/TMC4361A_and_TMC5160_TMCAPI_EXAMPLE.tar.gz
    
    tar zxvf TMC5160_TMCAPI_EXAMPLE.tar.gz
    

     

    #include <stdio.h>
    #include <wiringPi.h>
    #include <bcm2835.h>
    #include <signal.h>
    #include "SPI_TMC.h"
    
    // Include the IC(s) you want to use
    #include "TMC-API/tmc/ic/TMC5160/TMC5160.h"
    #include "TMC-API/tmc/ic/TMC4361A/TMC4361A.h"
    
    #define MOTOR0			0 // Motor 0
    
    void resetMotorDrivers(uint8 motor);
    void signal_callback_handler(int signum);
    
    int main(int argc, char **argv) 
    {
    	// Register signal and signal handler
    	signal(SIGINT, signal_callback_handler);
    	
    	if (!bcm2835_init())
          return 1;
    
    	wiringPiSetupGpio();
      	
    	// Initiate SPI 
    	bcm2835_spi_begin();
    	bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // MSB first
      	bcm2835_spi_setDataMode(BCM2835_SPI_MODE3); // SPI Mode 3
      	bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256); // 1 MHz clock
      	bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // define CS pin
    	bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // set CS polarity to low
    
    	/***** IO control of TMC4361A-BOB & TMC5160-BOB *****/
    	pinMode(2, OUTPUT);
    	digitalWrite(2, HIGH); // TMC5160: Apply VCC_IO voltage 
    	pinMode(3, OUTPUT);
    	digitalWrite(3, LOW); // TMC5160: Use internal clock 
    	pinMode(14, OUTPUT);
    	digitalWrite(14, LOW); // TMC5160: Enable driver stage
    
    	pinMode (4, GPIO_CLOCK);
    	gpioClockSet (4, 9600000); // TMC4361A: Set clock to 9.6MHz
    	pinMode(15, OUTPUT);
    	digitalWrite(15, HIGH); // TMC4361A: Low active reset
    
    	// Reset the TMC4361A & TMC5160
    	resetMotorDrivers(MOTOR0);
    
    	/***** Configuring TMC4361A & TMC5160 *****/
    	// CLK_FREQ=9.6MHz
    	tmc4361A_writeInt(TMC4361A_CLK_FREQ, 0x00927C00);
    
    	// SR_ENC_IN=2, FILT_L_ENC_IN=2, SR_REF=0, FILT_L_REF=0, SR_S=4, FILT_L_S=5, SR_ENC_OUT=0, FILT_L_ENC_OUT=0
    	tmc4361A_writeInt(TMC4361A_INPUT_FILT_CONF, 0x00540022);
    
    	// SPI_OUTPUT_FORMAT=12, DISABLE_POLLING=1, COVER_DATA_LENGTH=0, SPI_OUT_LOW_TIME=4, SPI_OUT_HIGH_TIME=4, SPI_OUT_BLOCK_TIME=8
    	tmc4361A_writeInt(TMC4361A_SPIOUT_CONF, 0x8440004C);
    
    	// DIRECT_ACC_VAL_EN=1, DIRECT_BOW_VAL_EN=1, STDBY_CLK_PIN_ASSIGNMENT=3
    	tmc4361A_writeInt(TMC4361A_GENERAL_CONF, 0x00006006);
    
    	// STP_LENGTH=4, DIR_SETUP_TIME=6
    	tmc4361A_writeInt(TMC4361A_STP_LENGTH_ADD, 0x00060004);
    
    	/***** TMC5160 Settings *****/
    	// EN_PWM_MODE=1 enables stealthChop™, MULTISTEP_FILT=1, DIRECT_MODE=0 (off)
    	tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, TMC5160_GCONF | 0x80);
    	tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, 0x0000000C);
    	delay(1); // COVER_DONE flag: ~90µs -> 1 ms more than enough
    
    	// TOFF=3, HSTRT=4, HEND=1, TBL=2, CHM=0 (spreadCycle™)
    	tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, TMC5160_CHOPCONF | 0x80);
    	tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, 0x000100C3);
    	delay(1); // COVER_DONE flag: ~90µs -> 1 ms more than enough
    
    	// IHOLD=8, IRUN=15 (max. current), IHOLDDELAY=6
    	tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, TMC5160_IHOLD_IRUN | 0x80);
    	tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, 0x00080F0A);
    	delay(1); // COVER_DONE flag: ~90µs -> 1 ms more than enough
    
    	// TPOWERDOWN=10: Delay before power down in stand still
    	tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, TMC5160_TPOWERDOWN | 0x80);
    	tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, 0x0000000A);
    	delay(1); // COVER_DONE flag: ~90µs -> 1 ms more than enough
    
    	// TPWMTHRS=5000
    	tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, TMC5160_TPWMTHRS| 0x80);
    	tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, 0x00001388); 
    	delay(1); // COVER_DONE flag: ~90µs -> 1 ms more than enough
    	/***** END of TMC5160 Settings *****/
    
    	//MOTION_PROFILE = 2 (s-shaped ramp), OPERATION_MODE = 0 (velocity mode)
    	tmc4361A_writeInt(TMC4361A_RAMPMODE, TMC4361A_RAMP_SSHAPE);
    	
    	// Values for speed and acceleration of 1st profile	
    	tmc4361A_writeInt(TMC4361A_AMAX, 0x00001000); // AMAX = 4096 pps²
    	tmc4361A_writeInt(TMC4361A_DMAX, 0x00001000); // DMAX = 4096 pps²
    	tmc4361A_writeInt(TMC4361A_BOW1, 0x00000200); // BOW1 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_BOW2, 0x00000200); // BOW2 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_BOW3, 0x00000200); // BOW3 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_BOW4, 0x00000200); // BOW4 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_VMAX, 0x01F40000); // VMAX = 128k pps = 2.5 RPS (1.8° motor)
    
    	// Values for speed and acceleration of 2nd profile
    	tmc4361A_writeInt(TMC4361A_SH_REG1, 0x00001000); // AMAX = 4096 pps²
    	tmc4361A_writeInt(TMC4361A_SH_REG2, 0x00001000); // DMAX = 4096 pps²
    	tmc4361A_writeInt(TMC4361A_SH_REG8, 0x00000200); // BOW1 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_SH_REG9, 0x00000200); // BOW2 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_SH_REG10, 0x00000200); // BOW3 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_SH_REG11, 0x00000200); // BOW4 = 512 pps³
    	tmc4361A_writeInt(TMC4361A_SH_REG0, 0x03E80000); // VMAX = 256k pps = 5 RPS (1.8° motor)
    
    	/***** Setting up the TMC4361A for circular motion *****/
    	// START_EN = 8, TRIGGER_EVENTS = 8, PIPELINE_EN = 2, CYCLIC_SHADOW_REGS = 1, XPIPE_REWRITE_REG = 1
    	tmc4361A_writeInt(TMC4361A_START_CONF, 0x01042110);
    
    	// XRANGE = 256k (10 rotations)
    	tmc4361A_writeInt(TMC4361A_X_RANGE_WR, 0x0003E800);
    
    	// CIRCULAR_MOVEMENT_EN = 1
    	tmc4361A_writeInt(TMC4361A_REFERENCE_CONF, 0x00400000);
    
    	// POS_COMP = -256k (5 rotations)	
    	tmc4361A_writeInt(TMC4361A_POS_COMP, 0xFFFC1800);
    
    	// 1st pipeline stage of POS_COMP = 0
    	tmc4361A_writeInt(TMC4361A_X_PIPE0, 0x00000000);
    
    	printf("Press \"CTRL + C\" to stop the motor(s) and to close the program\n");
    	delay(1);
    	
    	while(1){}
    
    	// End SPI communication
      	bcm2835_spi_end();
       	bcm2835_close();
    
    	return 0;
    }
    
    void resetMotorDrivers(uint8 motor)
    {
    	if(!tmc4361A_readInt(TMC4361A_VACTUAL)) 
    	{
    		digitalWrite(2, LOW); // TMC5160: Reset
    		delay(10);
    		digitalWrite(2, HIGH); 
    		delay(10);
    		digitalWrite(15, LOW); // TMC4361A Reset
    		delay(10);
    		digitalWrite(15, HIGH);
    		delay(10);
    		tmc4361A_writeInt(TMC4361A_RESET_REG, 0x52535400);
    	}
    }
    
    void signal_callback_handler(int signum)
    {
    	tmc4361A_writeInt(TMC4361A_VMAX, 0x00000000); // VMAX = 0 pps
    	digitalWrite(14, HIGH); // TMC5160: Disable driver stage
    	
    	// Exit program
    	exit(signum);
    }

     

    /*
     * SPI_TMC.h
     *
     *  Created on: 12.01.2018
     *      Author: MN
     */
    #ifndef SPI_TMC_H
    #define SPI_TMC_H
    
    #include "TMC-API/tmc/helpers/API_Header.h"
    
    void initSPI(void);
    
    // TMC5160 SPI wrapper
    void tmc5160_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4);
    void tmc5160_writeInt(uint8 motor, uint8 address, int value);
    int tmc5160_readInt(u8 motor, uint8 address);
    
    // TMC4361A SPI wrapper
    void tmc4361A_writeDatagram(uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4);
    void tmc4361A_writeInt(uint8 address, int value);
    int tmc4361A_readInt(uint8 address);
    unsigned char tmc4361A_cover(unsigned char data, unsigned char lastTransfer);
    
    // General SPI functions
    void tmc40bit_writeInt(u8 motor, uint8 address, int value);
    int tmc40bit_readInt(u8 motor, uint8 address);
    
    #endif /* SPI_TMC_H */

     

    #include <bcm2835.h>
    #include "SPI_TMC.h"
    #include "TMC-API/tmc/ic/TMC4361A/TMC4361A_Register.h"
    
    // TMC5160 SPI wrapper
    void tmc5160_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4)
    {
        int value = x1;
    	value <<= 8;
    	value |= x2;
    	value <<= 8;
    	value |= x3;
    	value <<= 8;
    	value |= x4;
    
        tmc40bit_writeInt(motor, address, value);
    }
    
    void tmc5160_writeInt(uint8 motor, uint8 address, int value)
    {
        tmc40bit_writeInt(motor, address, value);
    }
    
    int tmc5160_readInt(u8 motor, uint8 address)
    {
        tmc40bit_readInt(motor, address);
        return tmc40bit_readInt(motor, address);
    }
    
    // TMC4361 SPI wrapper
    void tmc4361A_writeDatagram(uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4)
    {
        int value = x1;
    	value <<= 8;
    	value |= x2;
    	value <<= 8;
    	value |= x3;
    	value <<= 8;
    	value |= x4;
    
        tmc40bit_writeInt(0, address, value);
    }
    
    void tmc4361A_writeInt(uint8 address, int value)
    {
        tmc40bit_writeInt(0, address, value);
    }
    
    int tmc4361A_readInt(uint8 address)
    {
        tmc40bit_readInt(0, address);
        return tmc40bit_readInt(0, address);
    }
    
    unsigned char tmc4361A_cover(unsigned char data, unsigned char lastTransfer)
    {
    	static uint64 coverIn = 0;     // read from squirrel
    	static uint64 coverOut = 0;    // write to squirrel
    	static uint8 coverLength = 0;  // data to be written
    
    	uint8 out = 0; // return value of this function
    
    	// buffer outgoing data
    	coverOut <<= 8;    // shift left by one byte to make room for the next byte
    	coverOut |= data;  // add new byte to be written
    	coverLength++;     // count outgoing bytes
    
    	// return read and buffered byte to be returned
    	out = coverIn >> 56;  // output last received byte
    	coverIn <<= 8;        // shift by one byte to read this next time
    
    	if(lastTransfer)
    	{
    		/* Write data to cover register(s). The lower 4 bytes go into the cover low register,
    		 * the higher 4 bytes, if present, go into the cover high register.
    		 * The datagram needs to be sent twice, otherwise the read buffer will be delayed by
    		 * one read/write datagram.
    		 */
    
    		// Send the buffered datagram & wait a bit before continuing so the 4361 can complete the datagram to the driver
    		// measured delay between COVER_LOW transmission and COVER_DONE flag: ~90�s -> 1 ms more than enough
    		// todo CHECK 3: Delay measurement only done on TMC4361, not 4361A - make sure the required delay didnt change (LH) #1
    		if(coverLength > 4)
    			tmc4361A_writeInt(TMC4361A_COVER_HIGH_WR, coverOut >> 32);
    		tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, coverOut & 0xFFFFFFFF);
    		delay(1);
    
    		// Trigger a re-send by writing the low register again
    		tmc4361A_writeInt(TMC4361A_COVER_LOW_WR, coverOut & 0xFFFFFFFF);
    
    		// Read the reply
    		coverIn = 0;
    		if(coverLength > 4)
    			coverIn |= (uint64) tmc4361A_readInt(TMC4361A_COVER_DRV_HIGH_RD) << 32;
    		coverIn |= tmc4361A_readInt(TMC4361A_COVER_DRV_LOW_RD);
    		coverIn <<= (8-coverLength) * 8; // Shift the highest byte of the reply to the highest byte of the buffer uint64
    
    		// Clear write buffer
    		coverOut = 0;
    		coverLength=0;
    	}
    
    	return out; // return buffered read byte
    }
    
    
    // General SPI decription
    void tmc40bit_writeInt(u8 motor, uint8 address, int value)
    {
        char tbuf[5];
        tbuf[0] = address | 0x80;
        tbuf[1] = 0xFF & (value>>24);
        tbuf[2] = 0xFF & (value>>16);
        tbuf[3] = 0xFF & (value>>8);
        tbuf[4] = 0xFF & value;
    
        bcm2835_spi_writenb (tbuf, 5);
    }
    
    int tmc40bit_readInt(u8 motor, uint8 address)
    {
        char tbuf[5], rbuf[5];
        int value;
    	// clear write bit
    	tbuf[0] = address & 0x7F;
        
        bcm2835_spi_transfernb (tbuf, rbuf, 5);
    
    	value =rbuf[1];
    	value <<= 8;
    	value |= rbuf[2];
    	value <<= 8;
    	value |= rbuf[3];
    	value <<= 8;
    	value |= rbuf[4];
    
    	return value;
    }

     

    TARGET_EXEC ?= TMCAPI_EXAMPLE
    BUILD_DIR ?= ./bin
    CC = gcc
    CXX = g++
    
    # C Fags
    CFLAGS			+= -Wall 
    CFLAGS			+= -g
    LDFLAGS			+= -lbcm2835 
    LDFLAGS			+= -lwiringPi
    
    # define the C source files
    SRCS				+= main.c
    SRCS				+= SPI_TMC.c
    # used functions from TMC_API
    SRCS				+= TMC-API/tmc/helpers/Debug.c
    #SRCS 			+= TMC-API/tmc/ic/TMC2130/TMC2130.c
    #SRCS 			+= TMC-API/tmc/ic/TMC2208/TMC2208.c
    #SRCS 			+= TMC-API/tmc/ic/TMC2224/TMC2224.c
    #SRCS 			+= TMC-API/tmc/ic/TMC2660/TMC2660.c
    #SRCS 			+= TMC-API/tmc/ic/TMC5130/TMC5130.c
    SRCS 			+= TMC-API/tmc/ic/TMC5160/TMC5160.c
    #SRCS				+= TMC-API/tmc/ic/TMC4330/TMC4330.c
    #SRCS				+= TMC-API/tmc/ic/TMC4331/TMC4331.c
    #SRCS				+= TMC-API/tmc/ic/TMC4361/TMC4361.c
    SRCS				+= TMC-API/tmc/ic/TMC4361A/TMC4361A.c
    #SRCS				+= TMC-API/tmc/ic/TMC4670/TMC4670.c
    #SRCS				+= TMC-API/tmc/ic/TMC4671/TMC4671.c
    #SRCS				+= TMC-API/tmc/ic/TMC4672/TMC4672.c
    #SRCS				+= TMC-API/tmc/ic/TMCC160/TMCC160.c
    #SRCS				+= TMC-API/tmc/ic/TMC5041/TMC5041.c
    #SRCS				+= TMC-API/tmc/ic/TMC5062/TMC5062.c
    #SRCS				+= TMC-API/tmc/ic/TMC5072/TMC5072.c
    
    OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
    DEPS := $(OBJS:.o=.d)
    
    $(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
    	$(CC) $(OBJS) -o $@ $(LDFLAGS)
    
    # assembly
    $(BUILD_DIR)/%.s.o: %.s
    	$(MKDIR_P) $(dir $@)
    	$(AS) $(ASFLAGS) -c $< -o $@ $(LDFLAGS)
    
    # c source
    $(BUILD_DIR)/%.c.o: %.c
    	$(MKDIR_P) $(dir $@)
    	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
    
    # c++ source
    $(BUILD_DIR)/%.cpp.o: %.cpp
    	$(MKDIR_P) $(dir $@)
    	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ $(LDFLAGS)
    
    
    .PHONY: clean
    
    clean:
    	$(RM) -r $(BUILD_DIR)
    
    -include $(DEPS)
    
    MKDIR_P ?= mkdir -p

    Compiling and running the code

    Use the following command to compile the code.

    cd ~/TMC_EXAMPLE
    
    make

    Now you are able to execute this example.

    sudo ~/TMCAPI_test/bin/TMCAPI_EXAMPLE

    Be aware that the motor runs as soon as you execute the program.

    Related Pages

    TMCL-IDE

    TMC4361A

    TMC4361A-BOB

    TMC5160

    TMC5160-BOB

    TRINAMIC Technology Access Package -TMC-API

    www.trinamic.com

    Share this:

    • Twitter
    • Facebook
    • LinkedIn
    • Pinterest
    • Print
    • Email

    Related

    April 9, 2018 / Mario Nolten / 17

    Categories: Code Snippet, Products, Software, Tutorial

    Tags: BOB, Raspberry Pi, TMC4361A, TMC5160

    Your Project in Motion with Powerful Prototypes How to replace an Allegro A4988 with the TMC2208

    Comments are currently closed.

    17 thoughts on “Real-time Motor and Motion Control with a Raspberry Pi without a real-time OS”

    • Radek says:
      May 15, 2018 at 4:34 pm

      Hello Mario,
      Nice article. I have to think about writing code on BBB to handle a closed loop or use TMC4361 instead.
      One more question, I like the terminal adapter board you use. Where can I buy this?
      Regards
      Radek

    • Mario Nolten says:
      May 16, 2018 at 8:26 am

      Thanks Radek.
      The adapter board is called “Schraubstock”. It will be soon available at our distribution channels.
      Best regards,
      Mario

    • J says:
      June 12, 2018 at 9:40 am

      Hey Mario,

      i tried the Program on my Raspberry Pi, but an error acquired:

      mkdir -p bin/TMC-API/tmc/ic/TMC5160/
      gcc -Wall -g -c TMC-API/tmc/ic/TMC5160/TMC5160.c -o bin/TMC-API/tmc/ic/TMC5160/TMC5160.c.o -lbcm2835 -lwiringPi
      TMC-API/tmc/ic/TMC5160/TMC5160.c: In function ‘tmc5160_initConfig’:
      TMC-API/tmc/ic/TMC5160/TMC5160.c:74:2: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
      for(int i = 0; i < REGISTER_COUNT; i++)
      ^
      TMC-API/tmc/ic/TMC5160/TMC5160.c:74:2: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
      Makefile:48: recipe for target 'bin/TMC-API/tmc/ic/TMC5160/TMC5160.c.o' failed
      make: *** [bin/TMC-API/tmc/ic/TMC5160/TMC5160.c.o] Error 1

      can you help me at this ?

      Best regards

      J

    • Mario Nolten says:
      June 13, 2018 at 12:42 pm

      Hi J,
      please add an additional LDFLAG to the Makefile:
      LDFLAGS += -std=gnu99

      Best regards,
      Mario

    • J says:
      June 14, 2018 at 9:49 am

      Hi Mario Nolten,

      Thanks for your answer. It help a lot!

      I wanted to use the code with the tmc4361 and with tmc2130. I changed a bit in the code the name tmc5130 for tmc2130 and change the register values but i get one problem :

      mkdir -p bin/TMC-API/tmc/ic/TMC2130/
      gcc -Wall -g -c TMC-API/tmc/ic/TMC2130/TMC2130.c -o bin/TMC-API/tmc/ic/TMC2130/TMC2130.c.o -lbcm2835 -lwiringPi -std=gnu99
      gcc ./bin/main.c.o ./bin/SPI_TMC.c.o ./bin/TMC-API/tmc/helpers/Debug.c.o ./bin/TMC-API/tmc/ic/TMC2130/TMC2130.c.o ./bin/TMC-API/tmc/ic/TMC4361A/TMC4361A.c.o -o bin/TMCAPI_EXAMPLE -lbcm2835 -lwiringPi -std=gnu99
      ./bin/TMC-API/tmc/ic/TMC2130/TMC2130.c.o: In function `tmc2130_writeConfiguration’:
      /home/pi/TMC_EXAMPLE/TMC-API/tmc/ic/TMC2130/TMC2130.c:86: undefined reference to `tmc2130_writeInt’
      collect2: error: ld returned 1 exit status
      Makefile:40: recipe for target ‘bin/TMCAPI_EXAMPLE’ failed
      make: *** [bin/TMCAPI_EXAMPLE] Error 1

      It would be very helpful.

      Best regards

      J

    • Mario Nolten says:
      June 15, 2018 at 9:21 am

      Hi J,
      first you need add the following line to the Makefile / remove the “#” at the beginning:
      SRCS += TMC-API/tmc/ic/TMC2130/TMC2130.c

      Then you need to write the SPI Wrapper functions for the TMC2130 in SPI.c. For this you can just copy the code for the TMC5160 and rename it to TMC2130.

      SPI.h:
      …
      // TMC2130 SPI wrapper
      void tmc2130_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4);
      void tmc2130_writeInt(uint8 motor, uint8 address, int value);
      int tmc2130_readInt(u8 motor, uint8 address);
      …

      SPI.c:
      …
      // TMC2130 SPI wrapper
      void tmc2130_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4)
      {
      int value = x1;
      value <<= 8;
      value |= x2;
      value <<= 8;
      value |= x3;
      value <<= 8;
      value |= x4;

      tmc40bit_writeInt(motor, address, value);
      }

      void tmc2130_writeInt(uint8 motor, uint8 address, int value)
      {
      tmc40bit_writeInt(motor, address, value);
      }

      int tmc2130_readInt(u8 motor, uint8 address)
      {
      tmc40bit_readInt(motor, address);
      return tmc40bit_readInt(motor, address);
      }
      …

      Best regards,
      Mario

    • J says:
      June 21, 2018 at 9:55 am

      Hey Mario Nolten,

      Thank you very much! It works!

      But it works only in one direction, shouldn’t it change the direction ?
      (I only change the value for the speed and acceleration in the skript, then the motor start to move)
      Hope you can help me at that.

      Best regards,
      J

    • Mario Nolten says:
      June 21, 2018 at 3:12 pm

      Hi J,

      it should be working in both direction. Can you double-check if the dir signal is connected properly?

      Best regards,
      Mario

    • J says:
      October 2, 2018 at 4:24 pm

      Hey Mario,

      it’s a long time, i didn’t reply your last comment. I get some other work to do, but now i had some time to continue on my project.
      I found the problem ^^, my motor was to slow. So i change the VMAX and i can see the variation of the moving.

      Thank you for your big help!

      Best regards,
      J

    • FRANK says:
      October 16, 2019 at 4:13 pm

      Where I can find the TMC-API_Release_vX.XX.zip??
      Thank you

    • Servet Coskun says:
      December 11, 2019 at 5:00 pm

      How could I drive 2 stepper motors at the same time using the SPI protocol?

    • Lars Jaskulski says:
      January 13, 2020 at 11:54 am

      Hi Servet,

      The ways how to do so are either to connect two CS signals to each board or to connect the boards in “daisy chain” mode. Daisy chain means that you are connecting one CS signal to the different CS of the boards and connect the first board’s MISO pin to the MOSI pin of the µController. The first boards MOSI goes to the second board’s MISO and the MOSI back to the µControllers MISO. With this approach you will need to send the datagram for both chips at once as soon as you put the CS to low respectively you send 80 bits instead of 40 bits.

      Hope that helps.

      Best regards,
      Lars

    • Lars Jaskulski says:
      January 13, 2020 at 11:56 am

      Hi Frank,

      The latest version can be found here: https://github.com/trinamic/TMC-API

      Best regards,
      Lars

    • Flo says:
      March 17, 2020 at 3:13 pm

      Do the resetMotorDrivers(uint8 motor) function work ?

      I´m trying to do the same with an Infineon microcontroller but it cant pull it to low….

    • Mario Nolten says:
      March 18, 2020 at 4:46 pm

      Hi Flo,

      to reset the ICs, you just need to toggle VCCIO. This should be doable with every microcontroller. Did you check if you defined the pin on the Infineon MCU as Output and if it has normal high (+3V3 or +5V) and low level (0V) if nothing is connected to the pin? E.g. with a Multimeter?

      Best regards,
      Mario

    • Lachlan says:
      March 25, 2020 at 11:45 pm

      Just an FYI, the readInt function listed here will not work for reading registers when they return negative values (e.g. address 0x22 / TMC5160_VACTUAL can be a negative number) IF they are not 4 bytes long.

      An example of returned data:

      Byte 0 (status,ignore) 1 2 3 5
      Value 5 *0* 255 89 252

      If the register is only giving data for the last 3 bytes, then the first will be the status (5 in example) and the second will be 0 – *this is the problem*. All signed data types like the int store their value as 2s complement and the 5160 returns its values in 2s complement. Using this bit shifting technique as is to create the resultant int will by default use byte 1 (0) as the resultant int’s most significant byte (bits 24-31). However for negative values in 2s complement, it should be 255 (11111111). So all you need to do is for registers that you know aren’t 4 bytes and could return negative numbers, you need to check if the first byte (in this case byte 2) has it’s MSB as 1. Do this by checking where byte 2 >= 128. If it is < 128, then proceed normally with the given code, however if it is not (i.e. we have a negative number), simply set byte 1 to be 255. When converting to the INT, the sign and magnitude will be correct.

      In the case where the register returns a 4 byte number and it's negative, the 5160 will read out byte 1 as 255.

    • Mario Nolten says:
      March 26, 2020 at 1:40 pm

      Hi Lachlan,

      thanks for pointing this out.

      Best regards,
      Mario

Recent Posts

  • Guest Blog: UBC Thunderbots
  • Guest Blog: TURAG
  • Guest Blog: School of Engineering Telecom Physics University of Strasbourg
  • Guest Blog: STAR Dresden
  • Pushing the Limits of Stepper Motor Control in 3D Printing

Social

  • View trinamic.mc’s profile on Facebook
  • View Trinamic_MC’s profile on Twitter
  • View trinamic’s profile on GitHub
  • View UC4SHA5_GAw1Wbm2T2NWpWbA’s profile on YouTube

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Newsletter

 

Info

Waterloohain 5
22769 Hamburg
+49 40 514 806 40

Tags

3D printer 3d printing arduino BLDC cDriver code code snippet coolStep drive Driver ease of use ethercat eval kit evaluation kit Field Oriented Control FOC GUI guide heliostat How-To IC linear stage motion controller motor driver open source hardware programming rtmi servo servo controller IC setup silentstepstick stealthChop stepper stepper motor stepper motor driver stepper motor driver ic stepper motors technology TMC4671 tmc5130 TMCL TMCL-IDE TRINAMIC TRINAMIC Motion Control tuning

Tag Cloud

    3D printer 3d printing arduino BLDC cDriver code code snippet coolStep drive Driver ease of use ethercat eval kit evaluation kit Field Oriented Control FOC GUI guide heliostat How-To IC linear stage motion controller motor driver open source hardware programming rtmi servo servo controller IC setup silentstepstick stealthChop stepper stepper motor stepper motor driver stepper motor driver ic stepper motors technology TMC4671 tmc5130 TMCL TMCL-IDE TRINAMIC TRINAMIC Motion Control tuning

Pages

  • Blog Ethics
  • Newsletter
  • Player Embed
  • Search Videos
  • User Videos
  • Video Category
  • Video Tag

Categories

  • Competition
  • Guest blog
  • Industry News
  • Jobs
  • Myths about Motors
  • Open Source Hardware
  • Products
  • Projects
    • DIY
    • University Projects
  • Research
  • Social
  • Software
  • technology
  • Trade Shows
  • Tutorial
    • Code Snippet
  • Uncategorized

Copyright © 2015 TRINAMIC BlogTheme created by PWT. Powered by WordPress.org

loading Cancel
Post was not sent - check your email addresses!
Email check failed, please try again
Sorry, your blog cannot share posts by email.