J-Link+Ozone调试教程

📅 2026/7/18 13:27:39
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、三种调试启动模式1Download Reset Program擦除Flash将固件烧录到Flash中然后复位MCU释放复位后程序从头全速运行。2Attach to Running Program连接后程序保持运行可观察设备实时的动态变量不会破坏RAM堆栈、寄存器。3Attach Halt Program连接后停止正在运行的程序直接冻结现场立刻捕获当前寄存器、堆栈、内存快照不会破坏RAM堆栈、寄存器。三、Ozone基础调试功能1、断点系统与变量访问1使用Ozone调试Flash区不限制硬件断点数量Cortex-M只有4~8个硬件断点Keil调试受限于硬件数量因为Ozone自动在Flash写入BKPT断点指令缓存原始指令断点取消后自动恢复代码。2源码断点程序断点①在代码浏览区的左侧有一些灰色的区域鼠标左键点击即可在区域对应的代码打上源码断点当单片机运行到这些代码的时候会触发停止运行的条件如果需要取消源码断点鼠标左键点击它对应的红色圆圈即可取消断点。②鼠标右键点击源码断点对应的红色圆圈选择“Edit Breakpoint”在弹出的窗口中可以设置断点触发的前置条件。[1]Condition填入条件表达式如cnt100。如果选择“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/WriteAccess Size数据断点对应的变量字节长度可选择1/2/4 Bytes或自动识别Automatic4向量捕获断点①如果设置了向量捕获断点那么程序一旦进入异常处理函数将会立刻停机自动弹出异常窗口直接显示故障原因不用手动进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、其它调试窗口1Call Stack①打开方式在菜单栏选择“View”→“Call Stack”。②函数调用栈可显示函数的调用链路在出现Hardfault时还可直接定位bug函数。2RegistersPeripherals①打开方式在菜单栏选择“View”→“Registers”。②寄存器窗口显示了单片机的所有寄存器存储情况如果导入了相应的.svd文件还可直接查看外设寄存器的存储情况。3Memory①打开方式在菜单栏选择“View”→“Memory”。②Memory支持整片内存的查看对于可写区域可以直接在线修改。4Disassembly①打开方式在菜单栏选择“View”→“Disassembly”。②Disassembly窗口展示汇编语言与C语言混排展示对应关系支持单步汇编级调试可定位异常指令。5Source Files①打开方式在菜单栏选择“View”→“Source Files”。②Source Files窗口展示程序中的代码文件不一定包含工程的所有文件在编译过程中未使用的代码文件不会显示双击对应的代码文件可在代码浏览窗口打开它按下CtrlF可以查找想要搜索的字段在菜单栏也提供了若干Find工具搜索变量和函数。4、程序运行控制键1Halt program execution控制单片机停止运行程序代码浏览页面会跳转到停止点。2Resumes program execution控制单片机继续运行程序直到触发停止运行的条件或手动停止运行。3Reset Break At Symbol控制单片机复位运行到程序入口符号main函数/Reset_Handler后立刻暂停CPU。4Reset Halt控制单片机复位在复位向量地址处停机。5Reset Run控制单片机复位复位后全速运行程序直到触发停止运行的条件或手动停止运行。6Steps into the current subroutine控制单片机从当前停止点对应的C代码开始运行程序并停止在下一条C代码也即只运行一条C代码如果遇到函数调用会转入函数中停在函数开头。7Steps over the current subroutine控制单片机从当前停止点对应的C代码开始运行程序并停止在下一条C代码也即只运行一条C代码如果遇到函数调用会一步跳过不会转入函数即使在执行函数的时候触发了断点。8Steps out the current subroutine控制单片机从当前停止点对应的C代码开始运行程序直到从当前运行的函数中转出或触发停止运行的条件单片机停止运行程序。5、RTT日志打印1SEGGER_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 13Ozone加载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系统时序追踪1SystemView是SEGGER配套J-Link的免费非商业用途RTOS全时序追踪工具可从SEGGER官网获取底层依靠RTT传输内核事件实时抓取MCU运行全过程生成可视化时间轴图谱专门解决嵌入式实时系统卡顿、调度异常、功耗高、中断风暴等疑难问题兼容FreeRTOS、RT-Thread、uC/OS等主流RTOS。2SystemView工作原理简介①目标系统中的应用程序调用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的使用可参考另外的教程此处不过多赘述。