printf() using ST-Link debug interface (SWD, ITM View)

Intention

ARM provides the possibility to use a printf() like a serial output, using the SWD interface (ITM port 0). This example describes the usage using a Nucleo-64 board, ST-Link v2.1 and the STM32CubeIDE.

Note: The Cortex-M0 doesn’t have the required hardware in the mcu. So it is not possible on it.

switch printf() to the debug interface

Often you find the describtion to overwrite _write(). That’s correct. In case of the STM32CubeIDE generated code it is also possible to overwrite the more low level __io_putchar().

/* USER CODE BEGIN 4 */
int __io_putchar(int ch) {
    ITM_SendChar(ch);
    return ch;
}
/* USER CODE END 4 */

Debug Settings

You have to enable the Serial Wire Viewer (SWV). The most relevant setting is the Core clock. You have to configure the correct clock frequency, that you initialize within your application.

STM32CubeIDE Debug Configuration
STM32CubeIDE Debug Configuration SWV

Prepare the Debug view

  • Open the SWV ITM Console
open SWV ITM Console
open SWV ITM Console

printf() uses the SWO connector and the trace port 0.

  • Add ITM port 0
ITM port 0
ITM port 0
  • Enable ITM port 0
ITM port 0
ITM port 0
  • Start trace to view the printf() output
Start Trace
Start Trace

That’s all.

output
printf output

Alternative ST-Link Utility

ST-Link_Utility config
ST-Link_Utility config

 

Using UART3 as virtual COM on Nucleo-64 / STM32L476

Intention

By default the UART2 is connected to the virtual COM of the ST-Link. The same UART is possible to connect to the Arduino connector.

So I want to connect the UART3 to the virtual COM connector and use the UART2 for the Arduino connector (CN9 Pin 1 and 2),

The description is for an Nucleo-64 STM32L476, but should be compatible to some more Nucleo-64 boards, too.

Hardware changes

Switch UART2 to the Arduino connector

  1. open solder bridge SB13 and SB14
  2. close solder bridge SB62 and SB63
Nucleo-64 Uart2 to Arduino
Nucleo-64 solder bridges

Nucleo-64 evaluation board

Switch UART3 to the VCOM of the ST-Link

  1.  connect ST-Link connector TX to PC5 (Uart3 RX, CN10 pin 6)
  2. connect ST-Link connector RX to PC4 (Uart3 TX, CN10 pin 34)
Nucleo-64 UART3 to ST-Link
Wires to connect the UART3 to the ST-Link

Software changes

  • initialize UART3  (e.g. 115200 Baud, 8N1) with PC4 and PC5
  • in case you want to use printf
int __io_putchar(int ch) {
    HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
    //ITM_SendChar(ch);
    return ch;
}
  • alternatively overwrite _write()

 

 

can4osx – CAN USB device driver for Mac

When it comes to embedded development, the Mac becomes more and more popular. Because of that many semiconductor nowadays provide native Mac support for there Integrated Development Environments. All the Major ones have tools for developing and debugging on Mac machines. But when it comes to embedded development on CAN based system most of us encounter the problem that there is no native support for most of  the CAN to USB interfaces. Almost none the CAN interface manufactures provide a proper device driver for Mac.

Because of that a open source project, called can4osx, startet. It’s supposed as a generic CAN interface driver for CAN-USB interfaces of different vendors.

What is can4osx?

can4osx was born as a daughter project of can4linux, a project with the aim to bring CAN interfaces to Linux since several years. can4osx is a CAN-USB device driver, completely running in the User-Space of OSX. It uses OKitLib and IOUSBLib, so it doesn’t have to run in the kernel. The driver can be directly compiled into applications, or run as a library.

How to use it

can4osx comes with an API, which is very close to the API of Kvasers CANLIB. A little example how to use it in a simple application:

void simpleSend(void)
{
CanHandle hdl;
int channel = 0;
int bitrate = canBITRATE_125K;
canInitializeLibrary();

    hdl = canOpenChannel(channel, canOPEN_EXCLUSIVE | canOPEN_REQUIRE_EXTENDED);
    canBusOff(hdl);
    canSetBusParams(hdl, bitrate, 10, 5, 1, 1, 0);
    canBusOn(hdl);
    canWrite(hdl, 0x123, "can4osx", 8, 0);
    canBusOff(hdl);
}

Supported interfaces

At the moment only a few interfaces are officially supported. The reason for that is that only a few real hardware interface could be verified. Verified ones are:

  • Kvaser Leaf Light V.2
  • IXXAT USB-to-CAN FD Automotive
  • Kvaser Leaf Pro V.2 (unstable)

But since can4osx is open source every can implement or verify other interfaces.

Where is can4osx used

Besides in some little CAN hobby projects, can4osx is used for example in the  Mac Toolchain version of the different CAN Tools of the company emotas embedded communication GmbH.

 

Using emotas CANopen stack with Python

Implementation of CANopen devices with commercial CANopen Stacks, like the emotas CANopen Stack, is easy in fast. At least in C and C++. But when it comes to other languages some kind of wrappers are needed, because most of the stacks come the programing language ANSI C. This short article is supposed to show a easy way how to get a ANSI C CANopen Stack into the Python environment.

Creating the C Library

The first thing we need is a C Library of the Stack, so on Windows one would create a .dll, in Linux a .so, and on Mac a .dylib. Especially under Windows you have to be careful which export version of our public API you are using. There are two way, using __stdcall or __declspec(dllexport). Depending on that you have to choose the right way of creating callbacks for the stack. We will see an example later. In this article we are using a Windows dll, and we used __stdcall for the export of the API.

Using Python ctypes to call C functions

Since version 2.5 of Python there is a standard library included in Python, which enables us to use C functions in Python.

First we have to import the package with:

from ctypes import *

After we imported the library we can now load the dll/so/dylib with:

canopen = ctypes.WinDLL("canopen.dll")

or

canopen = ctypes.CDLL("canopen.so")

The result of this call is that we created an object canon which has every public function of the dll as a member method. So now you can call functions of the stack like this:

canopen.coLibdrvHardwareInit()

or

canopen.coLibdrvCanInit(250)

So for normal C functions it is pretty straight forward with using the ctyes library of python.

Using Python ctyes for C callback functions

The emtas CANopen stack comes with the opportunity to register C callback function in form of function pointer for several events. In case one of these events occurs the corresponding application callback function is called. Since we are now in the Python environment we have to enable the C library to call Python functions. But there is a pretty easy and straight-forward solution in ctypes, too.

As an example how to use callbacks lets assume the following: The CANopen Stack uses for the implementation of a CANopen LED a with the following C layout:

typedef void (* CO_EVENT_LED_T)(BOOL_T);

So a function which takes one parameter of type BOOL, and it returns void. So in Python first we create a corresponding function type.

CO_EVENT_LED_T = WINFUNCTYPE(None, c_bool)

We use WINFUNCTYPE instead of CFUNCTYPE because of the type of export we choose for the C-DLL.

No we can create a callback-object with a real Python function:

ledCallback = CO_EVENT_LED_T(ledGreenInd)

Where ledGreenInd is the name of the real Python function.

After we created the callback, we can actually pass it to the metas CANopen Stack with calling the Register function of the stack.

canopen.coLibEventRegister_LED_GREEN(ledCallback)

From this point on, every time the stack wants to turn the CANopen LED on or off, it will call the Python Application function ledGreenInd with a bool parameter.

Important notice

Make sure that your C library is compiled in the same format (32/64 bit) as your Python interpreter.

More complex example

In the following there is a more complex example of a slave example with the emtas CANopen stack in Python:

from ctypes import *
import ctypes

canopen = ctypes.WinDLL('canopen.dll')

###########################
### this is the main function
###########################
def main():

	canopen.coLibdrvHardwareInit()

	if canopen.coLibdrvCanInit(250) != 0:
		exit(1)

	if canopen.coLibdrvTimerSetup(1000) != 0:
		exit(2)

	CO_EVENT_STORE_T = WINFUNCTYPE(c_int, c_ubyte)
	callback1 = CO_EVENT_STORE_T(loadInd)
	retVal = canopen.coLibCanOpenStackInit(callback1)
	if retVal != 0:
		exit(3)

	CO_EVENT_NMT_T = WINFUNCTYPE(c_int, c_bool, c_int)
	callback2 = CO_EVENT_NMT_T(nmtInd)
	if canopen.coLibEventRegister_NMT(callback2) != 0:
		exit(3)

	CO_EVENT_ERRCTRL_T = WINFUNCTYPE(None, c_ubyte, c_int, c_int)
	callback3 = CO_EVENT_ERRCTRL_T(hbState)
	if canopen.coLibEventRegister_ERRCTRL(callback3) != 0:
		exit(4)

	CO_EVENT_SDO_SERVER_T = WINFUNCTYPE(c_int, c_bool, c_ubyte, c_ushort, c_ubyte)
	callback4 = CO_EVENT_SDO_SERVER_T(sdoServerReadInd)
	if canopen.coLibEventRegister_SDO_SERVER_READ(callback4) != 0:
		exit(5)


	CO_EVENT_SDO_SERVER_T = WINFUNCTYPE(c_int, c_bool, c_ubyte, c_ushort, c_ubyte)
	callback6 = CO_EVENT_SDO_SERVER_T(sdoServerWriteInd)
	if canopen.coLibEventRegister_SDO_SERVER_WRITE(callback6) != 0:
		exit(7)

	CO_EVENT_PDO_T = WINFUNCTYPE(None, c_ushort)
	callback7 = CO_EVENT_PDO_T(pdoInd)
	if canopen.coLibEventRegister_PDO(callback7) != 0:
		exit(8)

	CO_EVENT_PDO_T = WINFUNCTYPE(None, c_ushort)
	callback8 = CO_EVENT_PDO_T(pdoRecEvent)
	if canopen.coLibEventRegister_PDO_REC_EVENT(callback8) != 0:
		exit(9)

	CO_EVENT_CAN_STATE_T = WINFUNCTYPE(None, c_int)
	callback11 = CO_EVENT_CAN_STATE_T(canInd)
	if canopen.coLibEventRegister_CAN_STATE(callback11) != 0:
		exit(12)

	CO_EVENT_COMM_T = WINFUNCTYPE(None, c_int)
	callback12 = CO_EVENT_COMM_T(commInd)
	if canopen.coLibEventRegister_COMM_EVENT(callback12) != 0:
		exit(13)

	if canopen.coLibdrvCanEnable() != 0:
		exit(14)

	while True:
		canopen.coLibCommTask()

	return


def loadInd(sub_index):
	print "loadInd:", sub_index
	return 0


def nmtInd(execute, newState):
	print "nmtInd: New Nmt state", newState,"- execute", execute
	return 0


def pdoInd(pdoNr):
	print "pdoInd: pdo", pdoNr, "received"


def pdoRecEvent(pdoNr):
	print "pdoRecEvent: pdo", pdoNr, "time out"


def hbState(nodeId,state,nmtState):
	print "hbInd: HB Event", state, "node", nodeId, "nmtState:", nmtState


def sdoServerReadInd(execute,sdoNr,index,subIndex):
	print "sdo server read ind: exec:", execute, "sdoNr",  sdoNr, "index", index,":", subIndex
	#return 10
	return 0



	
def sdoServerWriteInd(execute,sdoNr,index,subIndex):
	print "sdo server write ind: exec:", execute, "sdoNr",  sdoNr, "index", index,":", subIndex
	#return 10
	return 0


def canInd(canState):
	print "CAN STATE:", canState
	return


def commInd(commEvent):
	print "COMM STATE:", commEvent
	return


def ledGreenInd(on):
	print "GREEN:", on


def ledRedInd(on):
	print "RED:", on


if __name__ == '__main__':
	main()