J-Link+Ozone调试教程

一、调试前的准备

        使用J-Link将PC和单片机连接

        以STM32入门教程中的“读写内部FLASH”实验为例,使用Keil打开该工程

        按照下图所示的步骤进行操作,如果使用J-Link仿真器,则在第三步选择ST-Link Debugger

        按照下图所示的步骤进行操作,输出选项卡需要勾选调试信息

        按照下图所示的步骤进行操作,代码生成的优化选项中,优化等级需要选为0,否则Keil在编译程序的过程中可能会过度优化,导致编译得到的二进制机器码和C语言代码不是等价的,在调试过程中会引起代码执行顺序错乱

        完成以上步骤后,编译程序,然后将其下载到单片机中

二、Ozone调试工程快速创建

1、新建工程

(1)打开Ozone,在菜单栏选择“File”→“New”→“Project”,新建一个工程。(如果是初次使用,那么打开Ozone后会自动弹出向导,引导用户新建工程)

(2)在Device选择MCU型号(本教程使用STM32F103C8T6),并在Peripherals导入对应的.svd文件(芯片厂商提供,可生成外设寄存器树,直接在上位机查看和修改外设寄存器)。

(3)主机接口选择USB;目标接口视情况选择,对于ARM芯片,通过查看数据手册关于调试的章节,可以确认是否支持SWD,支持即可选择,而非ARM芯片仅支持JTAG;目标接口速率可以选择2MHz,若硬件稳定,则可以提速。

(4)选择编译输出的.elf文件,如果是使用Keil编译,则选择编译输出的.axf文件(.axf文件是ARM扩展版.elf文件),路径尽量不包含中文和全角字符。

(5)创建完成后,在菜单栏选择“File”→“Save Project as...”,即可保存调试工程。

2、三种调试启动模式

(1)Download & Reset Program:擦除Flash,将固件烧录到Flash中,然后复位MCU,释放复位后程序从头全速运行。

(2)Attach to Running Program:连接后程序保持运行,可观察设备实时的动态变量,不会破坏RAM堆栈、寄存器。

(3)Attach & Halt Program:连接后停止正在运行的程序,直接冻结现场,立刻捕获当前寄存器、堆栈、内存快照,不会破坏RAM堆栈、寄存器。

三、Ozone基础调试功能

1、断点系统与变量访问

(1)使用Ozone调试,Flash区不限制硬件断点数量(Cortex-M只有4~8个硬件断点,Keil调试受限于硬件数量),因为Ozone自动在Flash写入BKPT断点指令,缓存原始指令,断点取消后自动恢复代码。

(2)源码断点(程序断点):

①在代码浏览区的左侧有一些灰色的区域,鼠标左键点击,即可在区域对应的代码打上源码断点,当单片机运行到这些代码的时候,会触发停止运行的条件;如果需要取消源码断点,鼠标左键点击它对应的红色圆圈,即可取消断点。

②鼠标右键点击源码断点对应的红色圆圈,选择“Edit Breakpoint”,在弹出的窗口中可以设置断点触发的前置条件。

[1]Condition填入条件表达式,如cnt>100。如果选择“Trigger when true”,那么断点命中时,需要满足条件表达式才会真正停机;如果选择“Trigger on change”,那么断点命中时,表达式数值发生过变化,才会真正停机。

[2]Skpi填入断点命中次数,假如设置为499,那么断点命中500次才会真正停机。

(3)数据断点(访问断点):

①数据断点,它是针对变量(或者说内存地址)而言的,如果一个变量被设置了数据断点,那么当程序对该变量进行访问时,会触发停止条件。

②数据断点的设置方式:

[1]在菜单栏选择“View”→“Watched Data”,打开数据监测窗口。

[2]直接编辑表达式,输入变量名或变量地址,即可监测变量的值,编辑Value属性还可以直接在线修改变量的值,但是需要注意,对于非静态局部变量,仅程序运行到该函数栈帧内时有效,函数退出后栈内存失效。

[3]鼠标右键上一步添加的变量,选择“Set Data Breakpoint”,即可为该变量添加数据断点。

[4]数据断点有三个参数,设置完成后,点击“OK”即可,能看到变量被打上黄色圆圈标记。

Expression:数据断点的表达式,也就是变量名或变量地址

Access Condition:数据断点的触发条件,可选择仅读访问触发(Write)、仅写访问触发(Read)、读写访问均可触发(Read/Write)

Access Size:数据断点对应的变量字节长度,可选择1/2/4 Bytes或自动识别(Automatic)

(4)向量捕获断点:

①如果设置了向量捕获断点,那么程序一旦进入异常处理函数,将会立刻停机,自动弹出异常窗口,直接显示故障原因,不用手动进HardFault函数挖栈。

②向量捕获断点的设置方式:

[1]在菜单栏选择“View”→“Break & Tracepoints”,打开断点管理窗口(断点管理窗口会管理所有类型的断点,可以在其中配置断点是否使能)。

[2]鼠标右键窗口的空白区域,选择“Vector Catch”。

[3]将所有错误类型全部勾选。

(5)任务断点:

①如果程序使用了FreeRTOS/μC-OS,则可以设置断点仅在执行指定任务时可以触发,用于排查多任务干扰bug。

②任务断点的设置方式:

[1]按照源码断点的方式在对应代码处设置断点。

[2]鼠标右键断点,选择“Edit Breakpoint...”,打开断点编辑窗口。

[3]如果程序使用了FreeRTOS并且有任务定义,则可在Task选项的下拉框选择相应的任务,这样,只有当前执行任务匹配所选任务时,断点才会触发。

2、实时变量波形绘图

(1)在菜单栏选择“View”→“Data Sampling”,打开数据采样窗口。

(2)切换Data Sampling窗口的选项卡为Setup,将想要观察动态变化的变量从Watched Data窗口拖到Data Sampling窗口,或者手动添加表达式(全局/静态变量名)。

(3)切换Data Sampling窗口的选项卡为Samples,可以看到每隔一定的采样时间(采样频率Sampling Freq可配置),Ozone都会对变量值进行采样,生成若干数据点,鼠标右键窗口,选择“Export...”,即可导出.csv格式文件,然后使用其它工具(如MATLAB)绘图。

3、其它调试窗口

(1)Call Stack:

①打开方式:在菜单栏选择“View”→“Call Stack”。

②函数调用栈可显示函数的调用链路,在出现Hardfault时,还可直接定位bug函数。

(2)Registers(Peripherals):

①打开方式:在菜单栏选择“View”→“Registers”。

②寄存器窗口显示了单片机的所有寄存器存储情况,如果导入了相应的.svd文件,还可直接查看外设寄存器的存储情况。

(3)Memory:

①打开方式:在菜单栏选择“View”→“Memory”。

②Memory支持整片内存的查看,对于可写区域可以直接在线修改。

(4)Disassembly:

①打开方式:在菜单栏选择“View”→“Disassembly”。

②Disassembly窗口展示汇编语言(与C语言混排,展示对应关系),支持单步汇编级调试,可定位异常指令。

(5)Source Files:

①打开方式:在菜单栏选择“View”→“Source Files”。

②Source Files窗口展示程序中的代码文件(不一定包含工程的所有文件,在编译过程中未使用的代码文件不会显示),双击对应的代码文件可在代码浏览窗口打开它,按下Ctrl+F可以查找想要搜索的字段,在菜单栏也提供了若干Find工具搜索变量和函数。

4、程序运行控制键

(1)Halt program execution:控制单片机停止运行程序,代码浏览页面会跳转到停止点。

(2)Resumes program execution:控制单片机继续运行程序,直到触发停止运行的条件,或手动停止运行。

(3)Reset & Break At Symbol:控制单片机复位,运行到程序入口符号(main函数/Reset_Handler)后立刻暂停CPU。

(4)Reset & Halt:控制单片机复位,在复位向量地址处停机。

(5)Reset & Run:控制单片机复位,复位后全速运行程序,直到触发停止运行的条件,或手动停止运行。

(6)Steps into the current subroutine:控制单片机从当前停止点对应的C代码开始运行程序,并停止在下一条C代码,也即只运行一条C代码,如果遇到函数调用,会转入函数中,停在函数开头。

(7)Steps over the current subroutine:控制单片机从当前停止点对应的C代码开始运行程序,并停止在下一条C代码,也即只运行一条C代码,如果遇到函数调用,会一步跳过,不会转入函数(即使在执行函数的时候触发了断点)。

(8)Steps out the current subroutine:控制单片机从当前停止点对应的C代码开始运行程序,直到从当前运行的函数中转出,或触发停止运行的条件,单片机停止运行程序。

5、RTT日志打印

(1)SEGGER_RTT不占用USART硬件串口、不占用定时器,仅靠J-Link调试通道传输,可实现零开销实时打印日志。

(2)单片机工程的工作:

①在J-Link完整安装包中找到RTT源码(如果没有则从SEGGER官网下载即可,截至2026年依然可行),取出如下文件,添加到Keil工程中。(如果后续编译报错,可选择移除SEGGER_RTT_ASM_ARMv7M.s)

②包含头文件SEGGER_RTT.h,并在使用前调用SEGGER_RTT_Init函数,初始化RTT模块(一般在初始化其它模块时一同对其进行初始化)。

SEGGER_RTT_Init();

③RTT支持多通道分离日志,一共16个通道(编号0~15),调用SEGGER_RTT_printf函数可打印日志,函数的使用方式和printf类似,只是多了一个函数参数——通道编号。

SEGGER_RTT_printf(<number>, "<打印的日志字符串及格式控制>", <输出表列>);

④举例:修改main.c,在主函数中打印日志。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"
#include "SEGGER_RTT.h"

uint8_t KeyNum;					//定义用于接收按键键码的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();					//OLED初始化
	Key_Init();					//按键初始化
	Store_Init();					//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失

	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");

	while (1)
	{
		KeyNum = Key_GetNum();		//获取按键键码

		if (KeyNum == 1)		//按键1按下
		{
			Store_Data[1] ++;	//变换测试数据
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();		//将Store_Data的数据备份保存到闪存,实现掉电不丢失
			SEGGER_RTT_printf(0, "Key value = %d\r\n", KeyNum);
		}

		if (KeyNum == 2)		//按键2按下
		{
			Store_Clear();		//将Store_Data的数据全部清0
			SEGGER_RTT_printf(0, "Key value = %d\r\n", KeyNum);
		}

		OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位
		OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据
		OLED_ShowHexNum(3, 6, Store_Data[2], 4);
		OLED_ShowHexNum(4, 1, Store_Data[3], 4);
		OLED_ShowHexNum(4, 6, Store_Data[4], 4);
	}
}

(3)在Ozone菜单栏选择“View”→“Terminal”,即可打开RTT终端窗口,窗口中能够显示日志信息。

四、RTOS系统级调试

1、RTOS内核感知调试

(1)本节使用FreeRTOS教程中计数型信号量实验的工程进行演示,同样地,该工程也需要做调试前的准备(回看第一节)。

(2)在FreeRTOSConfig.h中添加如下宏定义(注意不要重复定义,如果原本已有定义,则修改已有定义),然后编译工程。

#define configUSE_TRACE_FACILITY 1 
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 
#define configRECORD_STACK_HIGH_WATER_MARK 1

(3)Ozone加载RTOS解析插件:

①按照之前的步骤新建工程。

②如果弹出如下警告窗口,点击左下角“Apply selected fix-ups”,修复完成后点“Reload Project”重新加载工程。

③在Console窗口输入命令“Project.SetOSPlugin ("C:\Program Files\SEGGER\Ozone\Plugins\OS\FreeRTOSPlugin_CM4.js");”。(导入的.js文件取决于单片机的架构,本例使用的是Cotex-M4架构,所以导入FreeRTOSPlugin_CM4.js)

④在Ozone菜单栏选择“View”→“FreeRTOS”,即可打开任务视图窗口。

(4)开始调试,当程序停止时,任务视图窗口才会更新显示。

2、SystemView系统时序追踪

(1)SystemView是SEGGER配套J-Link的免费(非商业用途)RTOS全时序追踪工具(可从SEGGER官网获取),底层依靠RTT传输内核事件,实时抓取MCU运行全过程,生成可视化时间轴图谱,专门解决嵌入式实时系统卡顿、调度异常、功耗高、中断风暴等疑难问题,兼容FreeRTOS、RT-Thread、uC/OS等主流RTOS。

(2)SystemView工作原理简介:

①目标系统中的应用程序调用SystemView APls,对需要记录的信息进行采集,例如任务切换、中断进出、数据采样等。

②SystemView模块调用RTT APIs,将数据写入位于目标系统RAM里的RTT bufer。

③在不影响目标程序运行的情况下,J-Link通过调试接口读取RTT buffer的内容。

④SystemView主机软件通过J-Link DLL实时获取RTT数据,并进行图形化显示。

(3)使用SystemView前,需要将RTT代码和SystemView代码整合在嵌入式应用程序中:

①RTT源码整合在上一节中有说明,本节不再赘述。

②在SystemView完整安装包中找到SystemView源码(如果没有则从SEGGER官网下载即可,截至2026年依然可行),取出如下文件,添加到Keil工程中。

③在FreeRTOSConfig.h中添加如下宏定义(注意不要重复定义,如果原本已有定义,则修改已有定义),并包含SEGGER_SYSVIEW_FreeRTOS.h文件。

#include "SEGGER_SYSVIEW_FreeRTOS.h"

#define configUSE_TRACE_FACILITY        	    1
#define configRECORD_STACK_HIGH_WATER_MARK 	    1
#define configGENERATE_RUN_TIME_STATS   		0
#define configUSE_STATS_FORMATTING_FUNCTIONS 	1

④在main.c文件中包含SEGGER_SYSVIEW.h文件,并初始化SystemView。

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "FreeRTOS.h"
#include "task.h"
#include "FreeRTOS_experiment.h"
#include "Key.h"
#include "LED.h"
#include "SEGGER_SYSVIEW.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	Key_Init();         	//Key初始化
	LED_Init();         	//LED初始化

	/* SystemView 核心初始化 */
	SEGGER_SYSVIEW_Conf();

	/* 开始录制 */
	SEGGER_SYSVIEW_Start();

	FreeRTOS_Test();
	while (1)
	{

	}
}

⑤如果编译报了RAM空间不足的错误,则尝试减小分配给FreeRTOS的堆,修改以下宏定义。

#define configTOTAL_HEAP_SIZE    ( ( size_t ) ( 10 * 1024 ) )

(4)只要目标芯片的SWD/JTAG带宽足够,SEGGER J-Link支持多应用同时访问同一调试器,首先使用Ozone运行程序,然后单独打开SystemView连接同一个J-Link,即可进行录制。关于SystemView的使用,可参考另外的教程,此处不过多赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值