Use Raspberry Pi CM4 for Motor Control

Recently, a customer asked me to design a testing machine used in his factory. The machine needs three motors and some peripherals. There should be a LCD display for operator to give commands and watch the progress.

Finally, I decide to use three BLDC motors and Raspberry Pi CM4 as the main processor. A dedicated 7" LCD display can be attached to the DSI port of CM4IO. That is very convenient. In order to control BLDC, I design a HAT board to be attached on CM4IO. The below picture shows 3D model view in KiCAD. This board is capable of controlling three BLDCs with total power up to 500W.

All three motors are controlled by sensorless FOC algorithms. Two dedicated sensorless FOC controllers are used for two 70W motors and a dedicated DSP is used to control the major 400W BLDC.

All the three motors (provided by customer) have built-in hall sensors. I don't need them for torque and speed control loops. But, I think they could be used for position control without adding additional encoders. So, I route the hall sensor outputs to GPIO pins of CM4. These hall sensors will generate interrupts to Pi CPU. The frequency could be up to 40KHz. In order to deal with this high interrupt rate, I design a Linux kernel module to handle it. The position control loop is also implemented in kernel module instead of user space program.

In summary, it is possible to implement time-critical motor control by CM4. You just have to do it in kernel module.


How to Launch Multiple KiCad Instances in macOS?

KiCad 6.0 is going to be released within couple of months. Now, I am using KiCad 5.99 on real projects. The nightly build versions have shown the new KiCad is as useful as commercial schematic/layout packages.

But due to the limitation of macOS, it is normally unable to launch two or more instances of an application. This is very very inconvenient since I usually have to copy/modify schematic between projects. So, I have to overcome this limitation so that KiCad can be a productive tool for me.

In fact, there is a solution by using AppleScript, though it is not perfect.

At first, we can create a new Application by using AppleScript. Then, we can change its icon with the original KiCad's icon so that we know the new application is used to launch KiCad.


Create an AppleScript

Open the Script Editor program on your Mac. After Script Editor launched, click on "New Document" button then enter the following line:

do shell script "open -n /Applications/KiCad/KiCad.app"

Then press Cmd + K to compile the script.

Press Cmd + S to save the file. Give it a name "LaunchKiCad" and set its location "/Applications" so that it will be saved as "/Applications/LaunchKiCad.app", and most importantly, select Application as the File Format.

Now I can double-click on the icon of LaunchKiCad.app, it will create a new instance of KiCad. Then I can repeat the action again to open another instance of KiCad.

Change the Icon

Once the script is in Application form, we can change its icon.

Icons on macOS are in the ICNS file format. We can copy the original KiCad.app > Contents > Resources > KiCad.icns and rename it as "applet.icns".

Two steps to do it:

  1. Right-click the application and select Show Package Contents from the context menu. In the opened folder, go to Contents>Resources. We will find a file named “applet.icns”. Replace it with the one copied from KiCad.app.

  2. Next, right-click the application and select Get Info. On the information window, the first section shows the application's icon. Drag and drop the the above applet.icns file on to the icon in this window. Now, the application will use this icon in Finder.


CC3x20 Embedded Programming in macOS

To use TI SimpleLink Wi-Fi CC3x20, we have to program the serial flash. Tere are two ways to go:

  1. Use UniFlash to generate the image and program directly.
  2. Use Embedded Programming.
UniFlash requires certain hardware to be used, for example, CC3220 launchpad. For devices such as CC3120, there isn't JTAG interface. So, UniFlash has to go through UART interface. But only the UART interface with JTAG debugger or the UART on CC31XXEMUBOOST are supported.

For mass production consideration, it is better to go through the Embedded Programming method. TI has provided a Python script. But it can only run under Windows. For users like me, we use macOS and don't like to switch to Windows just for programming the image. So, I have to figure a way to use Embedded Programming under macOS.

For CC3x20 to enter boot-loader mode, the CC3x20.RX pin must be held in low and then pull RESET pin low/high. My idea is to pull RESET pin by the host RTS pin and pull host TX pin by using the tcsendbreak() API.

I have designed a C++ program by using POXIS termios APIs. But the termios API tcsendbreak() can't generate required timing for "break signal". Btw, the RTS timing is seriously affected by tcsendbreak() call. Anyway, I just can't get a proper timing.

Then, I give-up termios APIs. I think I can control RTS and break signal precisely by using FTDI D2XX driver. That's it. It just works!

I would like to share the code here in case you also need it.

bool CC3X20EP::enterBootloaderMode()
printf("%s()\n", __FUNCTION__);

// Flush the rx line (CC3x20 write line)
FT_Purge(fd, FT_PURGE_RX);

// Send a break signal(sending continuous spacing values, no start or stop bits) on the CC3x20 UART RX line.
// The CC3x20 device must sense this break signal during power up.
// The "duration" argument is ignored in macOS.
// tcsendbreak() will send break signal for 250ms ~ 500ms, actual measured 400ms.

// Power on the device (or reset it if it is already up and running).
// We can connect RTS pin to CC3x20.nHIB/nRESET and then pull RTS low/high.
// The minimum pulse time is 10ms for nHIB and 200ms for nRESET.
// See CC3120 datasheet.

// There seems garbages after nHIB is released
FT_Purge(fd, FT_PURGE_RX);

// CC3x20 sends an acknowledgment indication as described in Ack (0x00, 0xCC).
// CC3x20 should send ACK back in less than 300ms.
bool isAck = false;
uint16_t ack;
if (rx((uint8_t*)&ack, 2, 500) > 0) {
if (ack == ACK) {
isAck = true;
} else {
printf("%s() error! ack not valid\n", __FUNCTION__);
} else {
printf("%s() timedout to get ack\n", __FUNCTION__);

// On receiving the acknowledgment indication from the CC3x20 device,
// the main processor stops sending the break signal and flushes the UART lines.

if (!isAck) {
return false;

// At this point, CC3x20 is ready to receive any command.
// The main processor has 5 seconds to send any command. Failing to do so before
// the time-out expires results in the CC3x20 device initializing normally
// (aborting bootloader mode).
FT_Purge(fd, FT_PURGE_RX);

// 6. The main processor sends the Get Storage List command.
// The CC3x20 device responds with an Ack followed by a 1-byte storage list bitmap.
if (!getStorageList()) {
return false;
printf("%s() storage list bitmap: 0x%02X\n", __FUNCTION__, storageBitmap);

return true;