Close

TMC5160 motion controller mode

A project log for DIY Polarimetric Camera

Building a camera, ...scrap that, a microscope... that turns polarization into color

es-pronkE/S Pronk 02/15/2021 at 18:020 Comments

The Trinamic TMC5160 has a motion controller mode which is very cool. It allows you to set things like acceleration parameters, destination, speed via SPI, instead of using the STEP / DIR pins.

I have been using the TMC5160 SilentStepStick as a board for many applications but by default  it isn't configured as a motion controller and it doesn't expose a pin to change it.

You can cut a trace to put it in motion controller mode, and even though the documentation says it needs to be connected to ground, I've never actually had to do so (your results may vary).

made with the polarisation microscope

Check out the datasheet on this page and check page 34, register IOIN. This register can be read out to see if the cut was successful, Bit 6 should be 0 now and the motion controller should be active.

In order to test it out I wrote a simple program in C that runs on my raspberry pi. It initializes the TMC and accellerates until a maximum velocity is reached. After pressing enter it will decelerate and stop.

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>


static const char* s_spidev = "/dev/spidev0.0";
static uint8_t s_mode = 3;
static uint8_t s_bits = 8;
static uint16_t s_delay = 0;
static uint32_t s_speed = 1000000;

static int transfer(int fd, uint8_t reg, uint32_t val)
{
	printf("Transfer\n");

	uint8_t tx[5] = { reg, 
		(val>>24) & 0xFF, 
		(val>>16) & 0xFF,
		(val>> 8) & 0xFF,
		(val>> 0) & 0xFF };

	uint8_t rx[5] = {0};

	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long) tx,
		.rx_buf = (unsigned long) rx,
		.len = 5,
		.delay_usecs = s_delay,
		.speed_hz = 0,
		.bits_per_word = 0,
	};

	int rc = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (rc == 1) return rc;

	printf("%02X %02X%02X%02X%02X -> %02X %02x%02x%02x%02x\n",
			tx[0], tx[1], tx[2], tx[3], tx[4],
			rx[0], rx[1], rx[2], rx[3], rx[4]);
	return 0;
}

int main()
{
	int rc;
	int fd = open(s_spidev, O_RDWR);
	if (fd < 0) { perror("SPI OPEN"); return -1; }

	rc = ioctl(fd, SPI_IOC_WR_MODE, &s_mode);
	if (rc < 0) { perror("SPI WR MODE"); return -1; }

	rc = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &s_bits);
	if (rc < 0) { perror("SPI BIT PER WORD"); return -1; }

	rc = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &s_speed);
	if (rc < 0) { perror("SPI MAX SPEED HZ"); return -1; }

	transfer(fd, 0x01, 0x00000000);// READ GSTAT
	transfer(fd, 0x04, 0x00000000);// READ IOIN
	transfer(fd, 0x8b, 0x000000EE);// WRITE GLOBAL SCALER: 0xEE
	transfer(fd, 0x90, 0x000003E1);// WRITE IHOLD:0x01 IRUN:0x1F
	transfer(fd, 0x93, 0x00014000);// WRITE STEALTHCHOP UPPER VELOCITY
	transfer(fd, 0x94, 0x00018000);// WRITE COOLSTEP LOWER VELOCITY
	transfer(fd, 0x95, 0x0000002F);// WRITE COOLSTEP UPPER VELOCITY
	transfer(fd, 0xA0, 0x00000001);// WRITE RAMPMODE: 0 = POSITION MODE / 1 = VELOCITY MODE
	transfer(fd, 0xA3, 0x00000500);// WRITE START VELOCITY
	transfer(fd, 0xA4, 0x00000600);// WRITE A1 ACCELLERATION (FROM VSTART TO V1 IN POSITION MODE)
	transfer(fd, 0xA5, 0x00010000);// WRITE V1 VELOCITY (POSITION MODE)
	transfer(fd, 0xA6, 0x00000300);// WRITE AMAX ACCELLERATION (FROM V1 TO VMAX IN POSITION MODE, GENERAL IN VELOCITY MODE)
	transfer(fd, 0xA8, 0x00000F00);// WRITE DMAX DECELLERATION (FROM VMAX TO V1 IN POSITION MODE, GENERAL IN VELOCITY MODE)
	transfer(fd, 0xAA, 0x00000D00);// WRITE D1 DECELLERATION (FROM V1 TO VSTOP IN POSITION MODE)
	transfer(fd, 0xAB, 0x00000600);// WRITE VSTOP VELOCITY (POSITION MODE)
	transfer(fd, 0xAC, 0x00000800);// WRITE TZEROWAIT
	transfer(fd, 0xB3, 0x00000000);// WRITE DCSTEP = OFF
	transfer(fd, 0xB4, 0x00000000);// WRITE SWITCH MODE NO SWITCH STUFF
	transfer(fd, 0xEE, 0x0000001C);// WRITE DC CONTROL, GUESS
	transfer(fd, 0xEC, 0x10410155);// WRITE CHOPCONF
	transfer(fd, 0xA1, 0x00000000);// WRITE ACTUAL POSITION
	transfer(fd, 0xA7, 0x00060000);// WRITE VMAX / TARGET VELOCITY
	transfer(fd, 0xAD, 0x00400000);// WRITE TARGET POSITION (POSITION MODE)
	transfer(fd, 0x00, 0x00000000);// WRITE GCONF, diag0 = interrupt, diag1 = position compare
	printf("Waiting... ");
	fgetc(stdin);
	transfer(fd, 0xA7, 0x00000000);// WRITE VMAX / TARGET VELOCITY
	transfer(fd, 0xAD, 0x00000000);// WRITE TARGET POSITION (POSITION MODE)
	printf("Done.\n");
	close(fd);
	return 0;
}

 Next up: setting X_COMPARE to get a sync signal, and updating it in an interrupt handler.

Discussions