上一章节我们已经了解了uVision2 IDE平台的大体布局和一些功能(包括下载程序、进入仿真、单步执行、停止运行等等),在这一章节中我们就来详细介绍uVision2提供的这些功能对应的AGDI接口函数。
AGDI是Keil提供的一组API,里面包含了对上面说到的下载程序和仿真调试的所有接口函数,它是用来驱动在线调试器的。
AGDI接口独立于处理器的体系结构,所以能很方便的实现驱动的开发并且带来很小的开销。
我们通过该接口编写自己的目标驱动dll来连接调试器和Keil IDE。
在工程中的AGDI文件中,所有以AG_开头的函数都需要在目标驱动dll中被定义,不过也可以是一个空函数,他们用于实现了调试器的各项功能。
接口函数 | 功能描述 |
AG_Init() | AGDI的初始化函数 |
AG_MemAtt() | 内存访问函数 |
AG_BpInfo() | 断点设置/重置/使能/失能/清除 |
AG_BreakFunc() | 断点访问函数 |
AG_GoStep() | 运行/停止命令 |
AG_Serial() | 读/写窗口函数 |
AG_MemAcc() | 访问被调试的单片机的内存函数 |
AG_RegAcc() | 读/写单个寄存器的函数 |
AG_AllReg() | 读/写所有寄存器的函数 |
AG_HistFunc() | 追踪历史 *** 作的函数 |
AG_Init() :AGDI的初始化,不管是下载程序还是进入仿真,Keil IDE一定会调用AG_Init这个函数很多次来初始化一些指针和参数,AG_Init的部分代码如下:
/*----------- AGDI Basic Functions -----------------*/
/*AGDI-Init Function*/
U32 _EXPO_ AG_Init (U16 nCode, void *vp) {
U32 nE;
nE = 0;
switch (nCode & 0xFF00) {
case AG_INITFEATURES: // Initialize & start the target
PlayDead = 0; // clear some variables...
//---We don't have special features here, so all variables are cleared.
//---uVision2 will query these features lateron.
//---Note: the 'supp' structure is defined in 'AGDI.H'
supp.MemAccR = 0; // memory-access while running
supp.RegAccR = 0; // register-access while running
supp.hTrace = 0; // trace support
supp.hCover = 0; // code coverage support
supp.hPaLyze = 0; // Performance-Analyzer support
supp.hMemMap = 0; // Memory-Map support
supp.ResetR = 0; // Reset while running support
//---Note: if InitTarget() fails, then set nE=1, else nE=0.
// if 1 is returned, then uVision will cancel using this driver
FlashProg = 0;
InitMem(); // init Cache memory
bootflag = 1;
nE = InitTarget(); // Initialize your target...
bootflag = 0;
if (nE == 0) { // initialization was Ok.
InitRegs(); // define register layout for RegWindow
}
if (nE == 0) { // everything is ok so far
pCbFunc (AG_CB_EXECCMD, "U $\n"); // force disassemble @curPC
}
NumRecs = 0; // clear 'number of trace records'
break;
case AG_INITITEM: // init item
switch (nCode & 0x00FF) {
case AG_INITMENU: // init extension menu
*((DYMENU **) vp) = (DYMENU *) Menu;
break;
case AG_INITEXTDLGUPD: // init modeless extesion dlg update function
*((UC8 **) vp) = (UC8 *) DlgUpdate;
break;
case AG_INITMHANDLEP: // setup ptr to HWND of active modeless dlg
pHwnd = (HWND *) vp;
break;
case AG_INITPHANDLEP: // pointer to parent handle (MainFrame)
hMfrm = (HWND) vp;
break;
case AG_INITINSTHANDLE: // pointer to Agdi-instance handle
hInst = (HMODULE) vp;
break;
case AG_INITBPHEAD: // pointer to head of Bp-List
pBhead = (AG_BP **) vp;
break;
case AG_INITCURPC: // pointer to program counter
pCURPC = (UL32 *) vp;
break;
case AG_INITDOEVENTS: // DoEvents function pointer
//--- this is no longer relevant due to uVision's threading.
break;
case AG_INITUSRMSG: // Registered Message for SendMessage
Uv2Msg = (DWORD) vp; // (Serial-Window, TextOut messages)
break;
case AG_INITCALLBACK: // pointer to callback function
pCbFunc = (pCBF) vp; // call-back function of s166
break;
case AG_INITFLASHLOAD: // Prepare for Flash Download
nE = InitProgrammer ();
if (nE) break;
nE = InitDevFlash (); // Initialize Flash, Erase
break;
case AG_STARTFLASHLOAD: // S8051 says 'Ready for Flash DownLoad'
nE = FlashLoad(); // Flash-Download, write, verify
break;
}
break;
case AG_GETFEATURE: // uVision2 want's details about features...
switch (nCode & 0x00FF) {
case AG_F_MEMACCR: nE = supp.MemAccR; break;
case AG_F_REGACCR: nE = supp.RegAccR; break;
case AG_F_TRACE: nE = supp.hTrace; break;
case AG_F_COVERAGE: nE = supp.hCover; break;
case AG_F_PALYZE: nE = supp.hPaLyze; break;
case AG_F_MEMMAP: nE = supp.hMemMap; break;
case AG_F_RESETR: nE = supp.ResetR; break;
}
break;
case AG_EXECITEM: // execute various commands
switch (nCode & 0x00FF) {
case AG_UNINIT: // Clean up target system settings
ExitProgrammer (); // Stop Programmer
PlayDead = 1; // mark target as disconnected.
CloseAllDlg(); // close all open dialogs
StopTarget(); // shutdown target & communication
WriteMonParms (pdbg->TargArgs); // update argument string
break;
case AG_RESET: // perform a reset on the target system
if (PlayDead || hMfrm == NULL) break; // target is disconnected
ResetTarget(); // reset the target
pCbFunc (AG_CB_EXECCMD, "U $\n"); // dasm $
NumRecs = 0; // clear 'number of trace records'
break;
}
break;
}
if (PlayDead) { // driver is disconnected
StopTarget(); // shut down driver
PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0); // unload this driver
}
return (nE);
}
根据小编在开发的过程中发现Keil在下载程序和进入仿真这两个过程调用AG_Init时参数nCode的值和顺序均不同,两者的调用顺序分别为:
在下载的过程中,AGDI会通过上面这些调用顺序分别调用函数 “InitProgramme()”、“InitDevFlash()”、“FlashLoad()” 这三个函数需要我们来实现的,主要是对目标单片机中的主控芯片进行程序的烧录的初始化和少些过程。
在进入仿真的过程中,AGDI会通过上面这些调用顺序,调用 ResetTarget() 函数,这个函数和我们后面点击程序Reset时候调用的函数接口是同一个,所以在 ResetTarget() 函数里面要做好区分这两者的标志。
AG_BpInfo() : 这个接口是做断点的设置/重置/使能/失能/清除的 *** 作。
当我们在要调试的工程中设置断点或者清除断点的时候均会调用到这个接口,并且nCode的参数会根据我们的 *** 作进行赋值。
U32 _EXPO_ AG_BpInfo (U16 nCode, void *vp) {
U32 nA, nAdr;
U16 *pB;
AG_BP *pK;
if (PlayDead) return (0);
switch (nCode) {
case AG_BPDISALL: // disable all Exec-Bp's
case AG_BPKILLALL: // kill all Exec-Bp's
break;
default: // need attribute pointer anyway, set it up here.
nAdr = ((GADR *) vp)->Adr; // the referred address
pB = MGetAttr (nAdr); // get attribute location for 'nAdr'
if (!pB) return (0x80000000); // failed: unmapped address
break;
}
nA = 0;
switch (nCode) { // Function code
case AG_BPQUERY: // Query Break
case AG_BPEXQUERY: // Query for 'executed' attribute
nA = (UL32) (*pB & __BPQX); // Attributes are similar to uVision
break;
case AG_BPENABLE: // enable breakpoint
if (*pB & AG_ATR_BPDIS) { // is Bp disabled ?
*pB = (*pB & ~AG_ATR_BPDIS) | AG_ATR_BREAK; // enable it
}
break;
case AG_BPDISABLE: // disable breakpoint
if (*pB & AG_ATR_BREAK) { // is Bp enabled ?
*pB = (*pB & ~AG_ATR_BREAK) | AG_ATR_BPDIS; // disable it
break;
}
break;
case AG_BPKILL: // kill breakpoint
*pB = *pB & ~(AG_ATR_BREAK | AG_ATR_BPDIS);
break;
case AG_BPSET: // Set breakpoint
*pB = (*pB & ~AG_ATR_BPDIS) | AG_ATR_BREAK;
break;
case AG_BPDISALL: // disable all Bp's
pK = *pBhead; // head of breakpoint list
for ( ; pK ; pK = pK->next ) {
if (pK->type != AG_ABREAK || !pK->enabled) {
continue;
}
++nA; // count number of changed Bp's
pB = MGetAttr (pK->Adr); // get attribute location
if (pB && (*pB & AG_ATR_BREAK)) { // is Bp enabled ?
*pB = (*pB & ~AG_ATR_BREAK) | AG_ATR_BPDIS; // disable it
}
}
break;
case AG_BPKILLALL: // kill all Bp's
pK = *pBhead; // head of breakpoint list
for ( ; pK ; pK = pK->next ) {
if (pK->type != AG_ABREAK) continue;
++nA; // count number of changed Bp's
pB = MGetAttr (pK->Adr); // get attribute location
if (pB) {
*pB = *pB & ~(AG_ATR_BREAK | AG_ATR_BPDIS);
}
}
break;
}
if (PlayDead == 1) {
StopTarget();
PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
}
return (nA);
}
AG_GoStep() : 这个接口主要是实现单步执行、多步执行、运行断点、跳出函数、单行运行、运行到光标处、停止调试等 *** 作。
例如当我们点击单步执行的时候,这个接口中的nCode参数就会设置为AG_NSTEP,并且nSteps参数会被设置为当前指令的条数(也就是当前C语言语句编译成汇编语言的指令条数)。
/*
* Go/Step/Stop commands
*/
_EXPO_ U32 AG_GoStep (U16 nCode, U32 nSteps, GADR *pA) {
U32 nE;
if (PlayDead) return (0); // driver is disconnected.
nE = 0; // clear error code
switch (nCode) {
case AG_STOPRUN: // Stop Go/Step.
StopRun = 1;
nE = StopExec(); // nE: 1=Stopped, 0=executing
if (nE == 1) iRun = 0; // stopped.
if (!iRun) {
Invalidate(); // registers, caches, etc.
}
NumRecs = 0; // clear 'number of trace records'
break;
case AG_NSTEP: // execute 'nSteps' instruction steps
iRun = 1; // 'executing'
for ( ; nSteps != 0 ; --nSteps ) {
if (StopRun) break; // Stop-Button was pressed
if (Step() != 0x01) break; // 0x01 means 'Ok.'
}
StopRun = 0; // clear Stop-Button flag.
Invalidate(); // registers, caches, etc.
iRun = 0; // clear 'executing' flag
NumRecs = 0; // clear 'number of trace records'
break;
case AG_GOTILADR: // run til 'pA->Adr' or some Bp,
iRun = 1; // whichever comes first
GoUntil (pA->Adr);
StopRun = 0;
Invalidate(); // registers, caches, etc.
iRun = 0; // clear 'executing' flag
NumRecs = 0; // clear 'number of trace records'
break;
case AG_GOFORBRK: // run forever or till some Bp reached.
iRun = 1;
GoUntil (0xFFFFFFFF); // means 'go forever'
StopRun = 0;
Invalidate(); // registers, caches, etc.
iRun = 0; // clear 'executing' flag
NumRecs = 0; // clear 'number of trace records'
break;
}
if (PlayDead) { // target not connected
StopTarget(); // shut it down
PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0); // terminate ourselves
}
else {
GetRegs(); // get target registers
*pCURPC = REG51.nPC; // let uVision2 know about PC...
}
return (nE);
}
AG_MemAcc() : 这个接口实现了我们在调试的过程中读取/修改某个变量的值,或者读取整个数据存储区的数据。
从代码中可以看到,在读取或者写入的函数参数都是三个,分别是nA、pB、nMany,其中nA指的是要读取/写入的变量的地址,pB指的是要存放读取变量的空间(或者修改该变量的新的数值),nMany指的该变量数值的字节数。
例如,当我们要读取代码中变量 i 的值,可以将鼠标移动到变量 i 的位置,这时候其实就已经触发到这个接口了,这个接口会将变量 i 的地址nA和这个变量的字节数nMany(假如变量 i 定义为UNIT32的话,就是四个字节,这时候nMany就等于4),然后将读取后的数值放置到pB管道中,Keil会读取这管道显示到界面中。
/*
* Access target memory
*/
U32 _EXPO_ AG_MemAcc (U16 nCode, UC8 *pB, GADR *pA, UL32 nMany) {
U16 nErr;
UL32 nA;
if (iRun && !supp.MemAccR) { // currently running, can't access.
return (AG_NOACCESS); // error: can't access register
}
if (PlayDead) { // driver disconnected.
return (AG_NOACCESS);
}
nErr = 0;
if (hMfrm == NULL) return (nErr);
nA = pA->Adr;
switch (nCode) {
case AG_READ: // need 'read' permission
pA->ErrAdr = ReadMem (nA, pB, nMany);
if (pA->ErrAdr) nErr = AG_RDFAILED;
break;
case AG_WRITE: // need 'write' permission
pA->ErrAdr = WriteMem (nA, pB, nMany);
if (pA->ErrAdr) nErr = AG_WRFAILED;
break;
// used for Program download
case AG_WROPC: // need 'Read/Execute' permissions
nA |= (amCODE << 24);
pA->ErrAdr = WriteMem (nA, pB, nMany);
if (pA->ErrAdr) nErr = AG_WRFAILED;
break;
// used for disassembly etc.
case AG_RDOPC: // need 'Read/Execute' permissions
nA |= (amCODE << 24);
pA->ErrAdr = ReadMem (nA, pB, nMany);
if (pA->ErrAdr) nErr = AG_RDFAILED;
break;
default:
nErr = AG_INVALOP; // invalid Operation
break;
}
if (PlayDead == 1) { // disconnected...
StopTarget(); // shut down driver
PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
}
return (nErr);
}
AG_RegAcc() : 这个接口是读取单片机芯片的内置寄存器的数值,包括R0~R7、psw等等。
/*
* Read/Write a single Register
*/
U32 _EXPO_ AG_RegAcc (U16 nCode, U32 nReg, GVAL *pV) {
U16 nErr;
if (iRun && !supp.RegAccR) { // currently running, can't access.
return (AG_NOACCESS); // error: can't access register
}
if (PlayDead) return (AG_NOACCESS); // driver is disconnected.
nErr = 0;
switch (nCode) { // which Opcode
case AG_READ: // read register
GetRegs(); // make sure REG51 is up to date
switch (nReg) {
case nnR0: case nnR1: case nnR2: case nnR3:
case nnR4: case nnR5: case nnR6: case nnR7:
pV->uc = REG51.Rn [nReg & 0x07];
break;
case nrA:
pV->uc = REG51.acc;
break;
case nrDPTR:
pV->u16 = (((WORD16) REG51.dph) << 8) | (WORD16) REG51.dpl;
break;
case mPC:
pV->u32 = REG51.nPC; // Note: requires 'amCODE << 24' selector.
break;
}
break;
case AG_WRITE:
switch (nReg) {
case nnR0: case nnR1: case nnR2: case nnR3:
case nnR4: case nnR5: case nnR6: case nnR7:
REG51.Rn [nReg & 0x07] = pV->uc;
x: SetRegs (®51);
break;
case nrA:
REG51.acc = pV->uc;
goto x;
case nrDPTR:
REG51.dpl = (BYTE) (pV->u16 & 0xFF);
REG51.dph = (BYTE) ((pV->u16 >> 8) & 0xFF);
goto x;
case mPC:
curPC = pV->u32;
WritePC (curPC); // to target
break;
}
break;
default:
nErr = AG_INVALOP; // invalid Operation
break;
}
if (PlayDead == 1) {
StopTarget();
PostMessage (hMfrm, Uv2Msg, MSG_UV2_TERMINATE, 0);
}
return (nErr);
}
上面讲的就是AGDI常用的接口啦。
OK啦,这一章就介绍到这里了~~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)