目的
某天看到安富莱家的自己开发的调试器可以实现输出中文调试信息到自己编写的Jlink RTT上位机上,觉得无比神奇,略好奇到底是如何做的,找了很久,发现其上位机的对应的实现好像也没有完全开源出来。
后来经过很多搜索确认,知道了几个事实:
- Jlink自己自带的上位机J-Link RTT Viewer 不支持中文调试信息显示
- Jlink自己自带的命令行终端J-Link RTT Client是可以显示中文的,它的原理应该类似一个客户端连接到服务器,socket那种搞法的
- Jlink本身其实有提供一个叫SDK的东西,也就是直接控制Jlink的的库,但是很遗憾,这个是收费的,贼贵!!!!
- 某个国内的大佬使用pylink(python实现的控制Jlink的一个库)实现了一个类似与J-Link RTT Viewer 那样的东西,虽然比较简陋,但是我感觉已经很不错了,链接-》RTT-T
那么有没有可能用C#来控制Jlink呢?
C#控制Jlink的可能性
答案是,有!!!
解决思路在于,C#调用JLinkARM.dll里面的函数
这里转载下我认为很有意义的发现
我和一个老哥讨论的一些成果
感谢这个老哥xtq0009和我探讨了下Jlink DLL的问题,他也在做类似的东西~
我总结下我和他探讨的成果~
- 他认为这个RTT的API可能不是那样用的,RTT的功能是通过芯片上的一篇内存区域来做的的,这个可以参考SEGGER JLINK的WIKI
老哥说他感觉可能只要找到这个图中Up和Down描述描述符的位置,然后通过现在已知可以用的接口(读写寄存器的接口)可能就可以实现RTT了,之前我没有朝这个方向去找,去IDA反编译找API了~ 但是好像直接去读对应的RTT结构体的内存地址,会有问题,它提示是保护的,读不了!! - 目前找这个描述符的位置似乎只能是通过编译固件出来之后的map文件来找,我原来其实也很好奇Jlink的RTT终端下面有一个自动获取地址的输入窗,我一直纳闷怎么用,是不是也和这个地址有关系,或者说这个地址是通过内核,芯片来决定的
连接上芯片的LOG,感觉对照数据手册,是有关联的,跟调试组件有关系,具体什么关系,具体Jlink RTT它用了啥组件,暂时不知道
我的研究
结合以上的几个发现,加上我自己的测试,我把我对接JLinkARM.dll的C#接口分享出来,不保证都能用,我只是测试了几个常用的功能,RTT功能还有些问题,不稳定,有时候可以连上,有时候不能,但是连接Jlink,读取CPU ID,读取Jlink固件版本啥的似乎可以。
稳定性问题
不稳定的原因有可能找到了,原来RTT Read的接口,我没有指定传入缓存区的大小,指定了之后,基本上打开运行就是稳定的了
但是数据有时候还是会重复读取一次,暂时没有找到原因,以下是运行截图,TEST那句话是我从板子输出的
中文RTT输出
这个真实牛逼到我了
我找到让Jlink RTT输出中文的方法了
需要这么干:
首先芯片的代码编码格式选择UTF-8编码
注意是你输出RTT的代码的那个编码,因为RTT组件,它本身就是UTF-8编码的~~
然后RTT_Read接口
charset写auto
这么写
/// <summary>
/// 从RTT回读数据
/// </summary>
/// <param name="terminal"></param>
/// <param name="rxBuffer"></param>
/// <param name="len"></param>
[DllImport("JLinkARM.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern void JLINK_RTTERMINAL_Read(int terminal, [Out(), MarshalAs(UnmanagedType.LPArray)] byte[] rxBuffer, UInt32 size);
然后C#调用这里使用UTF8编码读取RTT发送来的字符
public static string JLINKARM_ReadRTT_String()
{
try
{
byte[] aa = new byte[1000];
JLINK_RTTERMINAL_Read(0, aa,1000);
ASCIIEncoding kk = new ASCIIEncoding();
//使用UTF-8编码
UTF8Encoding uu = new UTF8Encoding();
string ss = uu.GetString(aa);
if(ss.Length>1)
{
Console.Write(ss);
}
return ss;
}
catch(Exception ex)
{
}
return String.Empty;
}
然后见证奇迹的一刻~
其实我非常想写埃斯B Jlink,真是够~~
RTT输入输出缓冲区的默认值
注意SEGGER_RTT_Conf.h里面上下行数据缓冲区有默认值,
默认值 上行1K,下行64字节,printf的空间是64字节
如果你需要收发比较长的数据请修改三个宏.
使用的.net 4.8框架
JLinkHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*引入C/C++生成的dll时需要添加*/
using System.Runtime.InteropServices;
using System.Threading;
namespace Jlink_demo
{
internal class JLinkHandler
{
public static void JLINKARM_Sleep(int ms)
{
Thread.Sleep(ms);
}
/// <summary>
/// 打开JLINK设备
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_Open();
/// <summary>
/// 关闭JLINK设备
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_Close();
/// <summary>
/// 连接设备
/// </summary>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_Connect();
/// <summary>
/// 开启RTT
/// </summary>
/// <param name="terminal"></param>
/// <param name="size"></param>
[DllImport("JLinkARM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINK_RTTERMINAL_Control(UInt32 CMD, UInt32 size);
/// <summary>
/// 从RTT回读数据
/// </summary>
/// <param name="terminal"></param>
/// <param name="rxBuffer"></param>
/// <param name="len"></param>
[DllImport("JLinkARM.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern void JLINK_RTTERMINAL_Read(int terminal, [Out(), MarshalAs(UnmanagedType.LPArray)] byte[] rxBuffer, UInt32 size);
static string gbk_ansi(string str)
{
string keyword;
byte[] buffer = Encoding.GetEncoding("GB2312").GetBytes(str);
keyword = Encoding.UTF8.GetString(buffer);
return keyword;
}
public static string JLINKARM_ReadRTT_String()
{
try
{
byte[] aa = new byte[1000];
JLINK_RTTERMINAL_Read(0, aa,1000);
ASCIIEncoding kk = new ASCIIEncoding();
//使用UTF-8编码
UTF8Encoding uu = new UTF8Encoding();
string ss = uu.GetString(aa);
if(ss.Length>1)
{
Console.Write(ss);
}
return ss;
}
catch(Exception ex)
{
}
return String.Empty;
}
/// <summary>
/// 写数据到RTT
/// </summary>
/// <param name="terminal"></param>
/// <param name="txBuffer"></param>
/// <param name="len"></param>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINK_RTTERMINAL_Write(int terminal, [In(), MarshalAs(UnmanagedType.LPArray)] byte[] txBuffer, UInt32 size);
/// <summary>
/// 系统复位
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_Reset();
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_GoAllowSim();
/// <summary>
/// 执行程序
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_Go();
/// <summary>
/// 中断程序执行
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool JLINKARM_Halt();
/// <summary>
/// 单步执行
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool JLINKARM_Step();
/// <summary>
/// 清除错误信息
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ClrError();
/// <summary>
/// 设置JLINK接口速度
/// </summary>
/// <param name="speed"></param>
/// <remarks>0为自动调整</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_SetSpeed(int speed);
/// <summary>
/// 设置JTAG为最高速度
/// </summary>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_SetMaxSpeed()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt16 JLINKARM_GetSpeed()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_GetVoltage()
;
/// <summary>
/// 当前MCU是否处于停止状态
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool JLINKARM_IsHalted()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool JLINKARM_IsConnected()
;
/// <summary>
/// JLINK是否已经可以操作了
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool JLINKARM_IsOpen()
;
/// <summary>
/// 取消程序断点
/// </summary>
/// <param name="index">断点序号</param>
/// <remarks>配合JLINKARM_SetBP()使用</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ClrBP(UInt32 index)
;
/// <summary>
/// 设置程序断点
/// </summary>
/// <param name="index">断点序号</param>
/// <param name="addr">目标地址</param>
/// <remarks>建议使用JLINKARM_SetBPEx()替代</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_SetBP(UInt32 index, UInt32 addr)
;
/// <summary>
/// 设置程序断点
/// </summary>
/// <param name="addr">目标地址</param>
/// <param name="mode">断点类型</param>
/// <returns>Handle,提供给JLINKARM_ClrBPEx()使用</returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern int JLINKARM_SetBPEx(UInt32 addr, BP_MODE mode)
;
/// <summary>
/// 取消程序断点
/// </summary>
/// <param name="handle"></param>
/// <remarks>配合JLINKARM_SetBPEx()使用</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ClrBPEx(int handle)
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int JLINKARM_SetWP(UInt32 addr, UInt32 addrmark, UInt32 dat, UInt32 datmark, byte ctrl, byte ctrlmark)
;
/// <summary>
/// 取消数据断点
/// </summary>
/// <param name="handle"></param>
/// <remarks>配合JLINKARM_SetWP()使用</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ClrWP(int handle)
;
/// <summary>
/// 设置寄存器
/// </summary>
/// <param name="index"></param>
/// <param name="dat"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteReg(ARM_REG index, UInt32 dat)
;
/// <summary>
/// 读取寄存器
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_ReadReg(ARM_REG index)
;
/// <summary>
/// 写入一段数据
/// </summary>
/// <param name="addr"></param>
/// <param name="size"></param>
/// <param name="buf"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteMem(UInt32 addr, UInt32 size, byte[] buf)
;
/// <summary>
/// 读取一段数据
/// </summary>
/// <param name="addr"></param>
/// <param name="size"></param>
/// <param name="buf"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ReadMem(UInt32 addr, UInt32 size, [Out(), MarshalAs(UnmanagedType.LPArray)] byte[] buf)
;
/// <summary>
/// 从调试通道获取一串数据
/// </summary>
/// <param name="buf"></param>
/// <param name="size">需要获取的数据长度</param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ReadDCCFast([Out(), MarshalAs(UnmanagedType.LPArray)] UInt32[] buf, UInt32 size)
;
/// <summary>
/// 从调试通道获取一串数据
/// </summary>
/// <param name="buf"></param>
/// <param name="size">希望获取的数据长度</param>
/// <param name="timeout"></param>
/// <returns>实际获取的数据长度</returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_ReadDCC([Out(), MarshalAs(UnmanagedType.LPArray)] UInt32[] buf, UInt32 size, int timeout)
;
/// <summary>
/// 向调试通道写入一串数据
/// </summary>
/// <param name="buf"></param>
/// <param name="size">需要写入的数据长度</param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteDCCFast(UInt32[] buf, UInt32 size)
;
/// <summary>
/// 向调试通道写入一串数据
/// </summary>
/// <param name="buf"></param>
/// <param name="size">希望写入的数据长度</param>
/// <param name="timeout"></param>
/// <returns>实际写入的数据长度</returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_WriteDCC(UInt32[] buf, UInt32 size, int timeout)
;
/// <summary>
/// 获取JLINK的DLL版本号
/// </summary>
/// <returns></returns>
/// <remarks>使用10进制数表示</remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_GetDLLVersion()
;
/// <summary>
/// 执行命令
/// </summary>
/// <param name="oBuffer"></param>
/// <param name="a"></param>
/// <param name="b"></param>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_ExecCommand([In(), MarshalAs(UnmanagedType.LPArray)] byte[] oBuffer,int a,int b);
/// <summary>
/// 选择接口,0是JTAG 1是SWD
/// </summary>
/// <param name="type"></param>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_TIF_Select(int type);
/// <summary>
/// 获取JLINK的固件版本号
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_GetHardwareVersion()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern void JLINKARM_GetFeatureString([Out(), MarshalAs(UnmanagedType.LPArray)] byte[] oBuffer)
;
[DllImport("JLinkARM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern void JLINKARM_GetOEMString([Out(), MarshalAs(UnmanagedType.LPArray)] byte[] oBuffer)
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_SetLogFile([In(), MarshalAs(UnmanagedType.LPArray)] byte[] oBuffer)
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern StringBuilder JLINKARM_GetCompileDateTime()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_GetSN()
;
/// <summary>
/// 获取当前MCU的ID号
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 JLINKARM_GetId()
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern void JLINKARM_ReadMemU32(UInt32 addr, UInt32 leng, ref UInt32 buf, ref byte status)
;
/// <summary>
/// 写入32位的数据
/// </summary>
/// <param name="addr"></param>
/// <param name="dat"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteU32(UInt32 addr, UInt32 dat)
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern void JLINKARM_ReadMemU16(UInt32 addr, UInt32 leng, ref UInt16 buf, ref byte status)
;
/// <summary>
/// 写入16位的数据
/// </summary>
/// <param name="addr"></param>
/// <param name="dat"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteU16(UInt32 addr, UInt16 dat)
;
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern void JLINKARM_ReadMemU8(UInt32 addr, UInt32 leng, ref byte buf, ref byte status)
;
/// <summary>
/// 写入8位的数据
/// </summary>
/// <param name="addr"></param>
/// <param name="dat"></param>
/// <remarks></remarks>
[DllImport("JLinkARM.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern void JLINKARM_WriteU8(UInt32 addr, byte dat)
;
/// <summary>
/// 读取32位的数据
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
/// <remarks></remarks>
public UInt32 JLINKARM_ReadU32(UInt32 addr)
{
UInt32 dat = 0;
byte stu = 0;
JLINKARM_ReadMemU32(addr, 1, ref dat, ref stu);
return dat;
}
/// <summary>
/// 读取16位的数据
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
/// <remarks></remarks>
public UInt16 JLINKARM_ReadU16(UInt32 addr)
{
UInt16 dat = 0;
byte stu = 0;
JLINKARM_ReadMemU16(addr, 1, ref dat, ref stu);
return dat;
}
/// <summary>
/// 读取8位的数据
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
/// <remarks></remarks>
public byte JLINKARM_ReadU8(UInt32 addr)
{
byte dat = 0;
byte stu = 0;
JLINKARM_ReadMemU8(addr, 1, ref dat, ref stu);
return dat;
}
/// <summary>
/// 设置数据断点
/// </summary>
/// <param name="addr">目标地址</param>
/// <param name="addrmark">地址屏蔽位</param>
/// <param name="dat">目标数据</param>
/// <param name="datmark">数据屏蔽位</param>
/// <param name="mode">触发模式</param>
/// <returns>Handle,提供给JLINKARM_ClrWP()函数使用</returns>
/// <remarks>当前数值除了屏蔽位以外的数据位,与目标数据除了屏蔽位以外的数据位,一致即可产生触发</remarks>
public int JLINKARM_SetWP(UInt32 addr, UInt32 addrmark, UInt32 dat, UInt32 datmark, WP_MODE mode)
{
switch (mode)
{
case WP_MODE.READ_WRITE:
return JLINKARM_SetWP(addr, addrmark, dat, datmark, 0x8, 0xf7);
case WP_MODE.READ:
return JLINKARM_SetWP(addr, addrmark, dat, datmark, 0x8, 0xf6);
case WP_MODE.WRITE:
return JLINKARM_SetWP(addr, addrmark, dat, datmark, 0x9, 0xf6);
default:
{
return 0xff;
}
}
}
public string JLINKARM_StringFeature()
{
byte[] aa = new byte[1000];
JLINKARM_GetFeatureString(aa);
ASCIIEncoding kk = new ASCIIEncoding();
string ss = kk.GetString(aa);
return ss;
}
public string JLINKARM_StringOEM()
{
byte[] aa = new byte[1000];
JLINKARM_GetOEMString(aa);
ASCIIEncoding kk = new ASCIIEncoding();
string ss = kk.GetString(aa);
return ss;
}
public void JLINKARM_Setup(string cmdstr)
{
//RmAnnotate rm = new RmAnnotate();
//rm.MarkDosMode = false;
//rm.MarkStartSpaceToTab = 0;
//cmdstr = rm.Convert(cmdstr);
cmdstr = cmdstr.Replace("\n", "").Replace(" ", "").Replace("\t", "").Replace("\r", "");
string[] cmd = cmdstr.Split(';');
for (int i = 0; i <= cmd.Length - 1; i++)
{
_setupDoCmd(cmd[i]);
}
}
private void _setupDoCmd(string cmdstr)
{
string cmd = cmdstr.ToLower();
cmd = cmd.Replace("(", ",");
cmd = cmd.Replace(")", "");
cmd = cmd.TrimEnd(',');
string[] arg = cmd.Split(',');
UInt32 val1;
UInt32 val2;
if (arg.Length == 3)
{
cmd = arg[0];
val1 = _setupGetVal(arg[1]);
val2 = _setupGetVal(arg[2]);
}
else if (arg.Length == 2)
{
cmd = arg[0];
val1 = _setupGetVal(arg[1]);
val2 = 0;
}
else if (arg.Length == 1)
{
cmd = arg[0];
val1 = 0;
val2 = 0;
}
else
{
cmd = "";
val1 = 0;
val2 = 0;
}
if (cmd != "")
{
Console.WriteLine("Do CMD: " + cmdstr);
switch (cmd)
{
case "setjtagspeed":
JLINKARM_SetSpeed((int)val1);
break;
case "delay":
JLINKARM_Sleep((int)val1);
break;
case "disablemmu":
Console.WriteLine("...........................CMD not Supported");
break;
case "go":
JLINKARM_Go(); break;
case "halt":
JLINKARM_Halt(); break;
case "reset":
JLINKARM_Reset();
if (val1 > 0)
{
JLINKARM_Sleep((int)val1);
}
break;
case "resetbp0":
Console.WriteLine("...........................CMD not Supported"); break;
case "resetadi":
Console.WriteLine("...........................CMD not Supported"); break;
case "read8":
JLINKARM_ReadU8(val1); break;
case "read16":
JLINKARM_ReadU16(val1); break;
case "read32":
JLINKARM_ReadU32(val1); break;
case "verify8":
do
{
byte aa = JLINKARM_ReadU8(val1);
if (aa == ((byte)val2 & 0xff))
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "verify16":
do
{
ushort aa = JLINKARM_ReadU16(val1);
if (aa == ((ushort)val2 & 0xffff))
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "verify32":
do
{
uint aa = JLINKARM_ReadU32(val1);
if (aa == val2)
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "write8":
JLINKARM_WriteU8(val1, (byte)val2);
break;
case "write16":
JLINKARM_WriteU16(val1, (ushort)val2);
break;
case "write32":
JLINKARM_WriteU32(val1, val2);
break;
case "writeverify8":
do
{
JLINKARM_WriteU8(val1, (byte)val2);
byte aa = JLINKARM_ReadU8(val1);
if (aa == ((byte)val2 & 0xff))
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "writeverify16":
do
{
JLINKARM_WriteU16(val1, (ushort)val2);
ushort aa = JLINKARM_ReadU16(val1);
if (aa == ((ushort)val2 & 0xffff))
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "writeverify32":
do
{
JLINKARM_WriteU32(val1, val2);
uint aa = JLINKARM_ReadU32(val1);
if (aa == val2)
{
break; // TODO: might not be correct. Was : Exit Do
}
JLINKARM_Sleep(1);
} while (true);
break;
case "writeregister":
JLINKARM_WriteReg((ARM_REG)val1, val2);
break;
case "writejtag_ir":
Console.WriteLine("...........................CMD not Supported");
break;
case "writejtag_dr":
Console.WriteLine("...........................CMD not Supported");
break;
default:
Console.WriteLine("...........................Unkonwned CMD");
break;
}
}
}
private UInt32 _setupGetVal(string str)
{
UInt32 dd;
if (str.StartsWith("0x") && str.Length >= 3)
{
dd = Convert.ToUInt32(str.Substring(2), 16);
}
else
{
dd = Convert.ToUInt32(str);
}
return dd;
}
/// <summary>
/// ARM内部寄存器
/// </summary>
/// <remarks></remarks>
public enum ARM_REG : UInt32
{
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
CPSR,
R15,
R8_USR,
R9_USR,
R10_USR,
R11_USR,
R12_USR,
R13_USR,
R14_USR,
SPSR_FIQ,
R8_FIQ,
R9_FIQ,
R10_FIQ,
R11_FIQ,
R12_FIQ,
R13_FIQ,
R14_FIQ,
SPSR_SVC,
R13_SVC,
R14_SVC,
SPSR_ABT,
R13_ABT,
R14_ABT,
SPSR_IRQ,
R13_IRQ,
R14_IRQ,
SPSR_UND,
R13_UND,
R14_UND,
SPSR_SYS,
R13_SYS,
R14_SYS,
PC = 9
}
/// <summary>
/// 程序断点模式
/// </summary>
/// <remarks></remarks>
public enum BP_MODE : UInt32
{
ARM = 1,
THUMB = 2,
HARD_ARM = 0xffffff01u,
HARD_THUMB = 0xffffff02u,
SOFT_ARM = 0xf1u,
SOFT_THUMB = 0xf2u
}
/// <summary>
/// 数据断点模式
/// </summary>
/// <remarks></remarks>
public enum WP_MODE : UInt32
{
READ_WRITE,
READ,
WRITE
}
}
}
Program.cs
using Jlink_demo;
using System;
using System.Text;
namespace MyApp // Note: actual namespace depends on the project name.
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Jlink操作演示");
JLinkHandler.JLINKARM_SetLogFile(System.Text.Encoding.UTF8.GetBytes("this_log.txt"));
JLinkHandler.JLINKARM_Open();
var ver = JLinkHandler.JLINKARM_GetDLLVersion();
Console.WriteLine("DLL 版本: "+ver);
Console.WriteLine("SN:" + JLinkHandler.JLINKARM_GetSN());
Console.WriteLine("硬件版本: " + JLinkHandler.JLINKARM_GetHardwareVersion());
JLinkHandler.JLINKARM_ExecCommand(System.Text.Encoding.UTF8.GetBytes("device = STM32H743VI"), 0, 0);
JLinkHandler.JLINKARM_TIF_Select(1);
JLinkHandler.JLINKARM_SetSpeed(4000);
Console.WriteLine("CPU ID: "+JLinkHandler.JLINKARM_GetId());
//JLinkHandler.JLINKARM_Connect();
//JLinkHandler.JLINKARM_Halt();
//RTT control函数传入的控制参数
const UInt32 JLINKARM_RTTERMINAL_CMD_START = 0;
const UInt32 JLINKARM_RTTERMINAL_CMD_STOP = 1;
const UInt32 JLINKARM_RTTERMINAL_CMD_GETDESC = 2;
const UInt32 JLINKARM_RTTERMINAL_CMD_GETNUMBUF = 3;
const UInt32 JLINKARM_RTTERMINAL_CMD_GETSTAT = 4;
//JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_STOP, 0);
//JLinkHandler.JLINKARM_Sleep(10);
//JLINK_RTTERMINAL_Control这个函数的传入参数不太确定
JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_START, 0);
JLinkHandler.JLINKARM_Sleep(10);
//不确认能否正常使用的接口
//var des = JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_GETDESC, 0);
//Console.WriteLine($"查找到的描述符:{des:X8}");
//var num_buf = JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_GETNUMBUF, 0);
//Console.WriteLine($"缓冲区数量:{num_buf:D4}");
//var stat = JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_GETSTAT, 0);
//Console.WriteLine($"状态:{stat:X8}");
//尝试读取RTT,但是不是每一次都可以成功读取到硬件的RTT调试信息
int num = 500;
while (num-- >0)
{
Console.Write(JLinkHandler.JLINKARM_ReadRTT_String());//读取RTT接口也是,参数还不确定,有时候可以正常工作,有时候不能
JLinkHandler.JLINKARM_Sleep(100);
}
Console.WriteLine("结束");
JLinkHandler.JLINK_RTTERMINAL_Control(JLINKARM_RTTERMINAL_CMD_STOP, 0);
JLinkHandler.JLINKARM_Close();
}
}
}
最终成果
我基于上面这些发现写了一个属于我自己的RTT工具,我叫它RTT_ViewerPlusProMax
链接
自用的,可能有些许不完善的,但是简单用应该是可以了…
基本功能如下:
支持的功能大概就这些
使用时的一些截图~
定时发送: