cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Tracealyzer Integration & Debugging Handy Hint #6

Garnett.Robert
Senior III

Hi,

I have created a project with Percepio's Tracealyzer integrated into a STM32G431 Nucleo project. I use this board a lot for my smaller projects, it is cheap, small, fast 170MHz, has an excellent pinout and the G432 processor really kicks bottom. The project is for a humane electrically activated animal trap. 

I used the project with a Time OF Flight Sensor, a VL53L4CD which has a 1 MHz I2C slave interface. To get it going I used the VL53L4CD_ULD driver software provided by STM. This driver is a blocking driver that can use the VL53L4CD interrupt pin or data ready polling.  I have posted about this in an earlier post.

This project has the following debugging tools:

  1. Printfdma - non intrusive printf debugging funtion over VCP
  2. Tracealyzer Integration
  3. Run-time counters for FreeRTOS aware debugging
  4. Stack reporting  for FreeRTOS aware debugging
  5. FreeRTOS aware Hardfault handler with printf using Rozenberg printf code. (re-entrent and fast)

Other tools you can use for debugging and performance optimisation are the:

  • SWV Profiling tool
  • SWV Exception Log
  • SWV Timeline Graph
  • SWV Data Trace

The project memory is configured for DTCM at 0x10000000 with the following allocation to this memory:

  • FreeRTOS .bss modules
  • Stack and small heap
  • Various process variables, HAL Handles and FreeRTOS handles

Doing this provides a large amount of RAM for the Tracealyzer ring buffer and also optimises for speed. Have a look at the Linker script and Build Analyser for a better understanding of memory allocation.

The project can be configured to use the I2C bus in different ways depending on settings in the I2c.h header.

As posted, the CPU is running at 8 Mhz, the I2C bus at 25 kHz (Max at CPU 8 MHz) and the VL53L4CDdriver is set for DMA (deferred interrupt processing) with no VL53L4CD interrupt pin usage.  The sample rate for the VL53L4CD ranging is 30 ms. (33 Hz)

The Run Time counters and stack usage is shown below:

 

Snag_d1a23ca.png

I added the Tracealyzer plugin to my STM32CubeIDE to ease the use of this tool. Go to:

https://percepio.com/tracealyzer/gettingstarted/stm32/ 

If you like command line stuff you can export the ring buffer to a file in the debug console.  I find this clunky so I prefer the plugin.

 

This explains the in and outs of using Tracealyzer with STM32CubeIDE and ST-Link V3.

 

Here is a trace of my PixiPuss program

 

Snag_d1f1318.png

Works very well. Of course the one I'm using is the free version which is limited to snapshot recording with the Trace Recorder ring buffer. I an getting a quote for the full version, but somehow I think the price will be far too high for a retired hacker like me.

 

Here are outputs from the SWV functions you can use:

 

Snag_d2e6b13.png

Snag_d2ea8b8.png

 

Snag_d2f3f2b.png

Snag_d2ef8cc.png

Using one or more of all of these tools can make debugging like fox hunting, and they enhance "the thrill of the chase."

To get PixiPuss going extract the zip to "c:\Eng". As STM32CubeIDE insists on using absolute directories for includes, it won't work if you extract it somewhere else. I haven't a clue what to do in Linux if that's what you use.

Enjoy 

1 REPLY 1
Garnett.Robert
Senior III

Additional.

 

I added ISR registration to the project so that names are given to the ISR routines, not just absolute addresses.

 

I did this in files traceRecorderConfig.c and traceRecorderConfig.h.

 

/*
******************************************************************************
* Project	   	 : PixiePussAlarmV2_FW
* File Name    : traceRecorderConfig.c
* File Path    : C:\Eng\PixiePussAlarmV2\PixiePussAlarmV2_FW\Application\Utilities\TraceRecorderConfig\Src\traceRecorderCOnfig.c
* File Wrk Spc : /PixiePussAlarmV2_FW/Application/Utilities/TraceRecorderConfig/Src/traceRecorderCOnfig.c
* Created On   : Jun 12, 2025  8:56:08 AM
* Description  :
* Author	     : R J Garnett - rjg
******************************************************************************
*/

#pragma GCC optimize ("Os") /* rjg todo */
//#pragma GCC optimize ("O0") /* rjg todo */

#include "setCCM.h"

#define SET_DTCM_THIS_MODULE_ON 1
#define SET_ITCM_THIS_MODULE_ON 0

#if((SET_DTCM_ON == 1) || (SET_DTCM_THIS_MODULE_ON == 1))
#define USE_DTCM __attribute__((section (".DTCM_MISC")))
#else
#define USE_DTCM
#endif


#if((SET_ITCM_ON == 1) || (SET_ITCM_THIS_MODULE_ON == 1))
#define USE_ITCM __attribute__((section(".ITCM_SECTION")))
#else
#define USE_ITCM
#endif
#include "trcRecorder.h"
#include "osPriorities.h"
#include "stdbool.h"

extern void  I2C2_EV_IRQHandler(void);
extern void  I2C2_ER_IRQHandler(void);
extern void  DMA1_Channel1_IRQHandler(void);
extern void  DMA1_Channel2_IRQHandler(void);
extern void  DMA1_Channel3_IRQHandler(void);
extern void  DMA2_Channel1_IRQHandler(void);
extern void  USART2_IRQHandler(void);
extern void  EXTI0_IRQHandler(void);

TraceHandleBaseType_t USE_DTCM i2c2_ev_irqhandler;
TraceHandleBaseType_t USE_DTCM i2c2_er_irqhandler;
TraceHandleBaseType_t USE_DTCM dma1_channel1_irqhandler;
TraceHandleBaseType_t USE_DTCM dma1_channel2_irqhandler;
TraceHandleBaseType_t USE_DTCM dma1_channel3_irqhandler;
TraceHandleBaseType_t USE_DTCM dma2_channel1_irqhandler;
TraceHandleBaseType_t USE_DTCM usart2_irqhandler;
TraceHandleBaseType_t USE_DTCM exti0_irqhandler;

traceResult InitISRNames(void)
{
	traceResult result;

	/* These are in DTCM so we need to initialise them here */
	i2c2_ev_irqhandler 				= I2C2_EV_IRQHandler;
	i2c2_er_irqhandler 				= I2C2_ER_IRQHandler;
	dma1_channel1_irqhandler 	= DMA1_Channel1_IRQHandler;
	dma1_channel2_irqhandler 	= DMA1_Channel2_IRQHandler;
	dma1_channel3_irqhandler 	= DMA1_Channel3_IRQHandler;
	dma2_channel1_irqhandler 	= DMA2_Channel1_IRQHandler;
	usart2_irqhandler 				= USART2_IRQHandler;
	exti0_irqhandler 					= EXTI0_IRQHandler;



	result = xTraceISRRegister("I2C2_EV", 				IRP_I2C_BUS_ALL_IRQS, 	&i2c2_ev_irqhandler);
	result |= xTraceISRRegister("I2C2_ER", 				IRP_I2C_BUS_ALL_IRQS, 	&i2c2_er_irqhandler);
	result |= xTraceISRRegister("I2C2_Ch1Rx", 		IRP_I2C_BUS_ALL_IRQS, 	&dma1_channel1_irqhandler);
	result |= xTraceISRRegister("I2C2_Ch2Tx", 		IRP_I2C_BUS_ALL_IRQS, 	&dma1_channel2_irqhandler);
	result |= xTraceISRRegister("ADC1_DMA", 			IRP_ADC1_DMA_IRQN, 			&dma1_channel3_irqhandler);
	result |= xTraceISRRegister("PDMA_Ch2_1_Tx", 	IRP_PRINTDMA_UART_ALL, 	&dma2_channel1_irqhandler);
	result |= xTraceISRRegister("PDMA_UART", 			IRP_PRINTDMA_UART_ALL, 	&usart2_irqhandler);
	result |= xTraceISRRegister("I2C_EXT_IRQ", 		IRP_I2C_EXT_IRQ_PIN, 		&exti0_irqhandler);
	return result;
}

 

In addition you must signal the use of the ISR by adding: xTraceISRBegin(handle) and  xTraceISREnd(xIsTaskSwitchRequired) .  I added a two handy macros in main.h so I can turn tracealyzer on and off usinf a #define TRACEALYZER_ON = 1

You just need to add the macros to the ISR routine:

 

/****************************************************************************
 * @brief  Callback function I2C Event Interrupt request.
 * @retval None
 ****************************************************************************/
void I2C2_EV_IRQHandler(void)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  TRACE_ISR_BEGIN(i2c2_ev_irqhandler); // Start trace for ISR

  HAL_I2C_EV_IRQHandler(&hi2c2);

  if(hi2c2.ErrorCode	 != HAL_I2C_ERROR_NONE)
  {
		I2CTaskData.taskState = I2C_TASK_HAL_ERROR; 		// Set task state to HAL error
		I2CTaskData.taskErrorCode = I2C_TASK_HAL_ERROR; // Set task error code to HAL error
  }
	else
	{
			/* TODO */
	}
  vTaskNotifyGiveFromISR(I2CTaskHnd, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

  TRACE_ISR_END(0); // End trace for ISR
}

It's a bit tedious if you have a lot of interrupts to track, but it provides useful insights into how the code runs.

 

Snag_175149e0.png

Enjoy