Freemaster 是恩智浦免费为用户提供的,一种在 PC 电脑上对 MCU 程序中数据可视化的工具。
使用 Freemaster 工具不需要在目标工程中引用同 Freemaster 相关的源代码,只需要将需要实时显示的变量创建为全局变量即可,即在 MCU 的内存中分配一个固定的地址。之后,Freemaster 将通过 SWD 通信接口读内存中的值,并实时显示到 PC 机的界面上。
通过 SWD 接口访问内存是 ARM 调试的基本方式,同常用的支持在线调试的调试器工作方式相同。
以下使用一个操作实例,来说明 Freemaster 的使用方法。
硬件平台
本例使用恩智浦官方在中国市场推出的 LPC54114-Lite 开发板作为目标设备。如图 1 所示。
图 1 LPC54114-Lite 开发板
LPC54114-Lite 开发板以 LPC54114 为主控核心,板载集成了开源的 CMSIS-DAP 调试器,仅用一根 USB 数据线,就可以实现供电、调试、串口通信的功能, 适合随身携带和展示。
Freemaster 支持多种连接 MCU 的通信协议,如图 2 所示,其中包括了常用的 JLink 和 CMSIS-DSP。比较惊喜的是,Freemaster 竟然还支持 OSBDM 通信协议,这就意味着一些基于 JM60 板载调试器的 Kinetis 开发板也能用起来了,较新的 Kinetis 开发板使用基于 K20 主控的板载调试器,可以自由变身为 CMSIS-DAP、JLink 或 OpenSDA(使用 OSBDM 通信协议)。
图 2 Freemaster 支持多种同 MCU 的通信协议
LPC54114-Lite 开发板板载基于 LPC11U35 的调试器使用了 CMSIS-DAP 的固件,本文中将使用 CMSIS-DAP 作为样例介绍 Freemaster 的用法,使用其它通信协议与 CMSIS-DAP 类似。
创建 MCU 样例工程
当使用调试接口作为 Freemaster 与 MCU 的通信接口有个极为明显的好处,就是不需要在用户程序中写任何关于 Freemaster 代码,这就是所谓的“非侵入性”调试。
用户程序只要将需要 Freemaster 进行图形化的数据安排到全局变量里,让编译过程能够为这些数据分配固定地址的内存。最终 Freemaster 会通过调试接口,直接访问 MCU 的内存,从而得到可显示的数据。
以下使用 NXP MCUXpresso SDK 中提供的 lpc_adc_burst 工程作为示例程序的基础,对这个工程进行简化和改造,实现让 ADC0 硬件对通道 0(温度传感器)和通道 3(板载电位器)连续采样。采样结果被保存在全局变量数组 gAdcSensingValue[]中,再通过 Freemaster 显示到虚拟示波器界面上。
这里节选 main.c 文件中的关键代码如下:
<font size="3" face="微软雅黑">#include "fsl_common.h"
#include "board.h"
#include "clock_config.h"
#include "pin_mux.h"
#include "fsl_clock.h"
#include "fsl_power.h"
#include "fsl_adc.h"
/*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t gAdcSensingValue[2];
/*******************************************************************************
* Prototypes
******************************************************************************/
static void ADC_Configuration(void);
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Main function
*/
int main(void)
{
uint8_t ch;
BOARD_InitBootClocks();
BOARD_InitBootPins();
BOARD_InitDebugConsole();
printf("HelloWorld.rn");
ADC_Configuration();
ADC_DoSoftwareTriggerConvSeqA(ADC0); /* software start the conversion. */
while (1)
{
ch = getchar();
putchar(ch);
}
}</font>
复制代码
<font size="3" face="微软雅黑">void ADC_Configuration(void)
{
adc_config_t adcConvConfigStruct;
adc_conv_seq_config_t adcSeqConfigStruct;
/* Enable power. */
POWER_DisablePD(kPDRUNCFG_PD_ADC0); /* Power on the ADC converter. */
POWER_DisablePD(kPDRUNCFG_PD_VD7_ENA); /* Power on the analog power supply. */
POWER_DisablePD(kPDRUNCFG_PD_VREFP_SW); /* Power on the reference voltage source. */
POWER_DisablePD(kPDRUNCFG_PD_TEMPS); /* Power on the temperature sensor. */
/* Enable clock. */
CLOCK_EnableClock(kCLOCK_Adc0);
if (!ADC_DoSelfCalibration(ADC0))
{
printf("ADC_DoSelfCalibration() failed.rn");
while (1);
}
/* Configure the converter. */
adcConvConfigStruct.clockMode = kADC_ClockAsynchronousMode;
adcConvConfigStruct.clockDividerNumber = 1u;
adcConvConfigStruct.resolution = kADC_Resolution12bit;
adcConvConfigStruct.enableBypassCalibration = false;
adcConvConfigStruct.sampleTimeNumber = 7u;
ADC_Init(ADC0, &adcConvConfigStruct);
/* enable the temperature sensor connected to channel 0. */
ADC_EnableTemperatureSensor(ADC0, true);
/* Configure the sequence. */
adcSeqConfigStruct.channelMask = (1u << 0u) | (1u << 3u) ; /* channel 0 and channel 3. */
adcSeqConfigStruct.triggerMask = 0u; /* no hardware trigger. */
adcSeqConfigStruct.triggerPolarity = kADC_TriggerPolarityPositiveEdge;
adcSeqConfigStruct.enableSyncBypass = false;
adcSeqConfigStruct.enableSingleStep = false;
adcSeqConfigStruct.interruptMode = kADC_InterruptForEachSequence; /* interrupt at the end of the sequence. */
ADC_SetConvSeqAConfig(ADC0, &adcSeqConfigStruct);
ADC_EnableConvSeqA(ADC0, true);
/* Enable interrupts. */
ADC_EnableInterrupts(ADC0, ADC_INTEN_SEQA_INTEN_MASK);
NVIC_EnableIRQ(ADC0_SEQA_IRQn);</font>
复制代码
<font size="3" face="微软雅黑">void ADC0_SEQA_IRQHandler(void)
{
adc_result_info_t adcResultStruct;
uint32_t flags = ADC_GetStatusFlags(ADC0);
if (kADC_ConvSeqAInterruptFlag == (kADC_ConvSeqAInterruptFlag & flags))
{
ADC_GetChannelConversionResult(ADC0, 0u, &adcResultStruct);
gAdcSensingValue[0] = adcResultStruct.result;
ADC_GetChannelConversionResult(ADC0, 3u, &adcResultStruct);
gAdcSensingValue[1] = adcResultStruct.result;
}
ADC_ClearStatusFlags(ADC0, flags);
ADC_DoSoftwareTriggerConvSeqA(ADC0);
/*
* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
* exception return operation might vector to incorrect interrupt.
*/
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}</font>
复制代码
编译生成"lpc_adc_burst.out"文件,如图 3 所示。然后下载并运行。
图 3 在 Keil 中设置生成映像文件格式
注意,如果是以调试方式下载程序,切记要确保下载后再退出调试模式,然后通过开发板上的复位按键硬件复位。此时 Keil 要让出对调试器的占用,在接下来的操作中要把调试通信总线交给 Freemaster。
创建并配置 Freemaster 工程
Freemaster 软件在 NXP 官网的产品主页是:
https://www.nxp.com/support/developer-resources/software-development-tools/freemaster-run-time-debugging-tool:FREEMASTER
创建 Freemaster 新工程
下载、安装软件后启动 Freemaster 软件,默认创建了一个新工程。
右键选中左侧树形目录中的工程名,选中“属性”,在弹出对话框中编辑工程名,本例中改为“lpc54114-lite”,如图 4 所示。
图 4 创建 Freemaster 新工程
此时一定要先保存工程,让工程文件有个确定的文件地址,以便于后续关联其它文件时可以使用相对路径。
配置同 MCU 的通信协议并导入调试程序文件
配置新的 Freemaster 工程:
使用 CMSIS-DAP 通信协议,通过 LPC54114-Lite 板载的 CMSIS-DSP 调试器同主控芯片 LPC54114 通信。
导入"lpc_adc_burst.out"文件,Freemaster 会自动分析出变量名对应的内存地址。
操作见图 5 所示
图 5 配置同 MCU 的通信协议并导入调试程序文件
这里面有两个要点:
1. 指定调试程序的映像文件时最好用相对路径,否则整个文件夹被复制到别的电脑上后会识别不出来原有电脑的路径。
2. 为了确保变量的地址映射被成功识别出来,可以单击“View”查看解析出来的符号表,如图 6 所示。
图 6 Freemaster 从映像文件中解析出的符号表
生成 Freemaster 变量表
Freemaster 工程需要在内部保存一个 Freemaster 变量的清单,为后续步骤提供操作对象。
Freemaster 变量是对目标芯片上地址的封装,同时在 Freemaster 内部电脑的内存中,建立了一个定期刷新的数据缓存,并自动更新缓存中变量的值。后续示波器显示的变量,是直接从这个缓存中读取的。
创建变量表的操作如图 7 所示。
图 7 生成 Freemaster 变量表
注意,只有在变量表中创建的变量才能被后续创建的虚拟示波器识别出来。
创建虚拟示波器页面并设定显示通道
右键选中工程名,在弹出菜单中选中“New Scope...”,创建新的示波器页面。
在配置新示波器页面中,为新示波器页面命名并指定该示波器页面的刷新周期,在“Setup”标签页中指定显示通道,为指定通道选择变量,并可为指定通道命名。此处在一个示波器页面中支持最多 8 个通道,并可分组显示。
操作界面如图 8 所示。
图 8 创建新的示波器页面并设定显示通道
用户可以在一个 Freemaster 工程下面创建多个示波器页面。另外 Freemaster 还允许创建其它可视化数据的子模块,用户可以通过 JavaScript 和 Html 语言编写网页添加到其中。
启动 Freemaster 工程
此时全部配置工作就已经做好了,确保 MCU 端程序正在运行,并且电脑上没有其它程序占用同 MCU 连接的调试总线,就可以启动 Freemaster 开始采集和显示数据了。
点击 Freemaster 工程窗口工具栏中的“Start/Stop Communication(Ctrl+K)”图标,之后就能看到示波器页面上有曲线出来了。如图 9 所示。
图 9 启动 Freemaster 工程
图中可以看到:
一条比较平稳的红色曲线,它显示的是变量 gAdcSensingValue[0]的值,也就是芯片内部温度传感器的采样值。
一条变化剧烈的绿色曲线,它显示的是变量 gAdcSensingValue[1]的值,也就是从板载电位器上取得带采样值。而此时,我正在用螺丝刀旋转它以改变采样值。
总结
本文基于 NXP 官方的 LPC54114-Lite 开发板,简单介绍了数据可视化工具 Freemaster 软件的用法。
Freemaster 可以使用常用的 CMSIS-DAP 调试器作为通信媒介,使用通用的 SWD 接口通信,无需在应用程序中进行专门的通信协议移植工作,Freemaster 软件不需要“侵入”目标程序,只要将待检测变量创建为全局变量即可。
使用 Freemaster 可以快速实现对 MCU 的数据可视化,方便调试。
另外,Freemaster 具有非常丰富的功能和强大的可扩展性,例如,可以自动记录数据并导出到多种常用的数据文件格式,可以支持 JavaScript 和 Html 语言编程的网页,定制显示页面。这些功能读者在基于本文入门 Freemaster 软件后继续发掘。