DaisyDriver 2.0

The Sixi 3 robot arm is the result of many years of study and research. I’m trying to build my dream machine that avoids every problem discovered in my previous robot arms. My short list of desirable things include:

  • Easy manufacturing = use less unique parts = use repeating components.
  • Easy maintenance = daisy chain components.
  • Safer = no loose wires, always know where the arm is, responsive behaviour. (safety third!)

To that end I’m on my second attempt to design and program the board of my needs. Here’s where I’m at today, December 31, 2022. Read on for all the details.

To achieve my goals, I’m making a board that has:

  • Daisy chain wiring: I’ve chosen CANbus.
  • Motor driver: I’ve chosen the TMC2130 with bump sensing and silent operation. The TMC2130 can be tweaked by using SPI communication.
  • A rotation sensor: Because the wires have to pass through the gearbox and the sensor I need a hollow-shaft rotation sensor. I’ve chosen the IPS2200 for it’s absolute position sensing. The IPS2200 can be tweaked using I2C communication.
  • A brain to bring these parts together. I’ve chosen the STM32F103C8T6. It has support for all three communication methods AND limited USB.

Daisy Driver schematic

This is a logical diagram of the major components and their electrical relationship. I had an electrical engineer put it together to my spec. I had another engineer give it a sanity check. When it seemed to make everyone happy I send it to PCBWay, who then mailed me five blank PCBs and two fully assembled PCBs.

Daisy Driver 2.0 being programmed with an STLINK v2

First Code Upload

I had a choice here between three coding tools: VSCode, STM32CubeIDE, and Arduino. I chose Arduino for being the friendliest interface, both in terms of the GUI and the API. The only point where STM32CubeIDE appears to outshine Arduino is that that I can set breakpoints and read memory in STM32CubeIDE. The API for STM32CubeIDE is so onerous that I choose to sacrifice that and use USB serial to get data from my board.

In the Arduino IDE under Preferences > Additional board manager URLs I added https://github.com/stm32duino/BoardManagerFiles/raw/master/package_stmicroelectronics_index.json. Then I set a few options under Tools:

  • board= generic STM32F1
  • optimize = smallest
  • board part number = F103C8
  • c runtime library = nano
  • upload method = swd u
  • sb support = cdc
  • generic uart = enabled
  • generic usb speed = low/full

STLink V2

My experience with Daisy Driver 1.0 taught me that the USB was probably not going to work and the best first way to check the board did anything is to use the STLINK v2. Using the wiring diagram from Kicad and an online tutorial for reference, I connected the STLink v2 and the Daisy Driver 2.0.

Finally I wrote a sketch to blink the light.

// RGB status led
#define PIN_PWM_RGB_B PA8  // 29
#define PIN_PWM_RGB_R PA9  // 30
#define PIN_PWM_RGB_G PA10  // 31

void setup() {
  pinMode(PIN_PWM_RGB_B,OUTPUT);
  pinMode(PIN_PWM_RGB_R,OUTPUT);
  pinMode(PIN_PWM_RGB_G,OUTPUT);
}

void loop() {
  analogWrite(PIN_PWM_RGB_B,255);
  analogWrite(PIN_PWM_RGB_G,255);
  analogWrite(PIN_PWM_RGB_R,255);
  delay(100);
  analogWrite(PIN_PWM_RGB_B,0);
  analogWrite(PIN_PWM_RGB_G,0);
  analogWrite(PIN_PWM_RGB_R,0);
  delay(100);
}

And uploaded it via the STLink v2.

Sketch uses 13788 bytes (42%) of program storage space. Maximum is 32768 bytes.
Global variables use 840 bytes (8%) of dynamic memory, leaving 9400 bytes for local variables. Maximum is 10240 bytes.
      -------------------------------------------------------------------
                       STM32CubeProgrammer v2.9.0                  
      -------------------------------------------------------------------

ST-LINK SN  : Q
ST-LINK FW  : V2J40S7
Board       : --
Voltage     : 3.13V
SWD freq    : 4000 KHz
Connect mode: Under Reset
Reset mode  : Hardware reset
Device ID   : 0x410
Revision ID : Rev X
Device name : STM32F101/F102/F103 Medium-density
Flash size  : 64 KBytes
Device type : MCU
Device CPU  : Cortex-M3
BL Version  : --



Memory Programming ...
Opening and parsing file: arduino_firmware.ino.bin
  File          : arduino_firmware.ino.bin
  Size          : 14080 Bytes
  Address       : 0x08000000 


Erasing memory corresponding to segment 0:
Erasing internal memory sectors [0 13]
Download in Progress:


File download complete
Time elapsed during download operation: 00:00:00.914

RUNNING Program ... 
  Address:      : 0x8000000
Application is running, Please Hold on...
Start operation achieved successfully

The board began to blink! Huzzah! New milestone for Daisy Driver! Here it is in video form doing a rainbow wave.

USB connection

A major fail in the previous design was the USB connection – it didn’t work. Without it I couldn’t debug much. That’s right, I still use printf() to debug a lot of my code. Reading and writing to the USB serial connection is a great way to test and control components.

To be fair, there is another way to talk to this brain chip. As mentioned at the top, STM32CubeIDE can set breakpoints and investigate the innards of the STM chip while it is in use BUT the human interface is so unfriendly that I choose to not use it.

I connected a USB micro cable from the PC to the Daisy Driver. There was no “new device connected” sound, there was nothing new in settings > devices. I turned to the internet for help. The Embedded Engineering Discord pointed out that I had a crossed wire in the USB system. It was like this in the bottom-center of the schematic diagram:

The USBLC6-2S6 is an ESD protection system. As I understand it, the component prevents bad voltage from either side of the USB connection flowing through to the other. EED pointed out that the connections should have have been like this:

Hardware hacking

The USBLC6 is not vital to running the board so I did was any good hacker would do and cut the chip out of the circuit. First I used a hot air gun to warm the solder and then I pried the chip off with tweezers.

Then I soldered new wires to correct the wiring. It was easiest to bend the horse shoe shape first, then both legs of the horseshoe together for symmetry, then cut them to length. All of this had to be done to connect the outside pads without touching the middle pads.

Now finally I have a connection sound! Unfortunately I also have “unknown device descriptor”. Well, it’s probably the driver. I found several tutorials that said “download and run https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/master/drivers/win” so I did. It did not change the device error. Digging even further, I turned to everyone’s old friend, Stack Exchange.

One of typical mistakes in bus-powered USB devices is mismanagement of pull-up resistor. Your description says: “1.5k pullup to 3.3”, which means that the USB connect event (DP pullup) will be seen immediately upon plug in. So the host will start enumeration process in 100ms. Yet your MCU will take some time to boot, so it won’t be ready with proper responses, and host inquiries will fail. By the time your MCU is ready to communicate, the host will declare the port as dead, and display error. When a demo board gets powered externally before connecting to USB host and is already up and running, the connection is fine.

To avoid this mishap, all reputable STM demo boards have the pull-up resistor controlled from GPIO pin, typically PB2 or PD2. Then the pull-up is set by code only when the USB stack is ready to respond.

So I modified my sketch to match.

void setup() {
  SERIALsetup();
  LEDsetup();
}

void SERIALsetup() {
  pinMode(PIN_BOOT1,OUTPUT);
  digitalWrite(PIN_BOOT1,LOW);
  Serial.begin(115200);
  while(!Serial.available());
  digitalWrite(PIN_BOOT1,HIGH);
  Serial.println("Hello, world.");
}

And voila, COM3 appeared! Serial data appeared! Huzzah!

IPS2200 absolute position sensor

Next I tested the angle sensor.


void testIPS22200B() {
  double r = ((double)analogRead(PIN_IPS_COS) - 350.0) / (650.0 - 350.0);
  double g = ((double)analogRead(PIN_IPS_SIN) - 350.0) / (650.0 - 350.0);
  r = min(1.0,max(r,0.0));
  g = min(1.0,max(g,0.0));
  analogWrite(PIN_PWM_RGB_B,r*255.0);
  analogWrite(PIN_PWM_RGB_R,g*255.0);
  analogWrite(PIN_PWM_RGB_G,0);

  Serial.print(analogRead(PIN_IPS_COS));
  Serial.print(F("\t"));
  Serial.print(analogRead(PIN_IPS_SIN));
  Serial.print(F("\t"));
  Serial.print(analogRead(PIN_IPS_COSN));
  Serial.print(F("\t"));
  Serial.println(analogRead(PIN_IPS_SINN));
}

This worked as expected. The analog 3.3v signals from the IPS200 are converted by the STM32’s Analog-to-Digital Converter (ADC) and read as a value in the range 1400-2700 of a possible 4096. Put another way, they were 2048 +/- 650. My goal is to use the widest range possible for more accurate readings, so getting to 2048 +/- 2047 would be ideal. A Renesas rep suggested I combine COS and -COSN to get double the value. That gets me +/-1300. A future tweak will be to use the I2C system to tweak the IPS200’s “half multiplier” setting which increases the output range of all sensor values.

TMC2130

With the sensor working and the serial working, it was also painless to set up the step/direction/enable pins of the TM2130 and get some movement. I read the sensor once at startup and convert that into motor step count. After that any change in the sensor becomes a new “motor step goal”. Effectively I can turn the sensor and the motor will respond in kind, in both directions. Works great.

CAN Bus

CAN Bus is a much bigger beast. It turns out that CAN Bus and USB Serial cannot be used at the same time, so I can’t keep using USB serial for debugging. Right now I think there will be room for two versions of the firmware – one with USB and one with CAN Bus. In a perfect world I’d be able to use both and then I would have any of the shenanigans that follow.

Block diagram of the wiring in a CANbus network courtesy of kmpdrivetrain.

CAN Hat + ChatGPT

If I could use Serial and CAN bus at the same time then I’d go PC > Daisy Driver 0 > ... > Daisy Driver..N. To work around this problem I’m using a Raspberry Pi CAN hat. The hats have, at various times, been made with different oscillators, clocks used to talk to the CAN network.

one PCB and one Pi Can hat connected via CAN bus

The oscillator is the silver tin at the top of the picture. It looks like a tiny can of sardines. The oscillator frequency is written on the top. Very faintly it says 12.000 meaning 12Mhz. Now the documentation from the manufacturer (Waveshare) says to use 8Mhz. I found some sources online that said to use 16Mhz. No one could agree. Both were tested without success. In my desperation I even tried ChatGPT.

Fortunately I had an extra 8GB SD card so I was able to flash a new copy of the rasberry pi OS to the new SD, reboot the pi, put the old SD on a USB stick and plug that in, then modify the old /boot/config.txt and finally get things working again. Long story short the oscillator value is now set to 12000000.

So that’s where I’m at so far. More as it happens. Happy 2023, everyone!