• keil程序在外部RAM中调试的问题总结(个人的一点经验总结)


    keil程序在内部RAM调试的基本步骤网上已经有非常多了,我就不再赘述,大家能够在网上搜到非常多。


    可是有些时候内部RAM并不够用,这就须要将程序装入外部RAM中调试,而在这个过程中可能会出现各种各样的问题,在这里我将会把我遇到过的一些问题和须要注意的地方总结一下,希望可以对大家有所帮助。


    有错误的地方也希望大神们可以指教,提前表示感谢···


    转载请注明出处:waitig's blog


    先介绍下我项目使用的硬件,芯片是LPC1788,外部RAM是MT48LC4M32B2,大小为16M(128Mb X32 SDRAM),之前用的是MT48LC2M32B2,大小为8M,后来发现东西太多,8M不够用了,所以换成了16M的。

    在换RAM时也遇到过一些问题,这个在下文中会有介绍。


    • 在外部RAM中调试程序,程序中一定不能有操作外部RAM的代码!


    在外部RAM中调试,程序中不要有操作外部RAM的代码,初始化也不要有,包含对RAM相关引脚的操作。

    RAM的初始化和引脚的初始化要放到jlink的下载配置文件里,主要是对LPC的寄存器进行相关配置,不要忘了RAM中的读取算法配置。

    把我的配置文件贴出来供大家參考一下。

    FUNC void PinSel(int p1, int n1, int f1)
    {
        _WDWORD(0x4002C000 + (p1 * 32 + n1) * 4, 0x8 | f1);
    }
    
    FUNC void InitSDRAM(void)
    {
        int i;
        
        PinSel(2,16,1);
        PinSel(2,17,1);
        PinSel(2,18,1);
        PinSel(2,20,1);
        PinSel(2,24,1);
        PinSel(2,28,1);
        PinSel(2,29,1);
        PinSel(2,30,1);
        PinSel(2,31,1);
    
        for(i = 0; i < 32; i++)
            PinSel(3,i,1);
            
        for(i = 0; i < 21; i++)
            PinSel(4,i,1);
    
        PinSel(4,24,1);
        PinSel(4,25,1);
        PinSel(4,30,1);
        PinSel(4,31,1);
        
        /* PCONP |= 1 << 11 */
        _WDWORD(0x400FC0C4, 0x04288FDE);    // Power On EMC
    
        /* EMCCONTROL |= 1 */
        _WDWORD(0x2009C000, 0x00000001);    // Enable EMC
    
        /* EMCDLYCTL */
        _WDWORD(0x400FC1DC, 0x00081818);    // Config data read delay
    
        /* EMCCONFIG */
        _WDWORD(0x2009C008, 0x00000000);    // Little endian mode
    
        /* DYNAMICCONTROL */
        _WDWORD(0x2009C020, 0x00000003);    // Set normal self refresh mode, normal power mode
                                            // CE always HI
                                            // Enable clock out
                                            // Clock do not stop during idle
    
        /* DYNAMICREFRESH */
        _WDWORD(0x2009C024, 0x0000001F);    // refresh timing 
    
        /* DYNAMICREADCONFIG */
        _WDWORD(0x2009C028, 0x00000001);    // read timing 
    
        /* DYNAMICRP */
        _WDWORD(0x2009C030, 0x00000002);    // tRP
    
        /* DYNAMICRAS */
        _WDWORD(0x2009C034, 0x00000003);    // tRAS
        
        /* DYNAMICSREX */
        _WDWORD(0x2009C038, 0x00000005);    // tSREX
    
        /* DYNAMICAPR */
        _WDWORD(0x2009C03C, 0x00000001);    // tAPR
    
        /* DYNAMICDAL */
        _WDWORD(0x2009C040, 0x00000005);    // tDAL
    
        /* DYNAMICWR */
        _WDWORD(0x2009C044, 0x00000003);    // tWR
    
        /* DYNAMICRC */
        _WDWORD(0x2009C048, 0x00000004);    // tRC
    
        /* DYNAMICRFC */
        _WDWORD(0x2009C04C, 0x00000004);    // tRFC
    
        /* DYNAMICXSR */
        _WDWORD(0x2009C050, 0x00000005);    // tXSR
    
        /* DYNAMICRRD */
        _WDWORD(0x2009C054, 0x00000001);    // tRRD
    
        /* DYNAMICMRD */
        _WDWORD(0x2009C058, 0x00000003);    // tMRD
    
        /* DYNAMICCASRAS0 */
        _WDWORD(0x2009C104, 0x00000303);    // RAS/CAS Latency
    
        /* DYNAMICCONFIG0 */
        _WDWORD(0x2009C100, 0x00004500);    // Config device type as SDRAM
                                            // Config address mapping
    
        _sleep_(100);                       // Wait 100 ms
    
        /* DYNAMICCONTROL */
        _WDWORD(0x2009C020, 0x00000183);    // nop command
    
        _sleep_(100);                       // Wait 100 ms
    
        /* DYNAMICCONTROL */
        _WDWORD(0x2009C020, 0x00000103);    // pre-charge command
        
    //    /* DYNAMICREFRESH */
    //    _WDWORD(0x2009C024, 0x00000002);    // refresh timing 
    
        _sleep_(100);                       // Wait 100 ms
    
        /* DYNAMICREFRESH */
        _WDWORD(0x2009C024, 0x0000001F);    // refresh timing 
        
        /* DYNAMICCONTROL */
        _WDWORD(0x2009C020, 0x00000083);    // mode command
        _RDWORD(0xA0000000 | (0x32 << (2 + 2 + 8)));
        
        _sleep_(100);                       // Wait 100 ms
    
        /* DYNAMICCONTROL */
        _WDWORD(0x2009C020, 0x00000003);    // noamal command
    
        /* DYNAMICCONFIG0 */
        _WDWORD(0x2009C100, 0x00084500);    // enable buffer
        
        _sleep_(100);                       // Wait 100 ms
    }
    

    以上是外部RAM的初始化部分,不要忘了当中的RAM本身的寄存器,在本例中的地址是0xA0000000,寄存器中的各个位数的作用例如以下图:


    (截图自MT48LC4M32B2的datasheet)

    当中数据要配置正确,RAM才干正确工作。接下来是对内存保护单元(MPU)的配置,两者结合,就是Jlink对RAM的初始化和配置。


    RAM_Debug.ini 的文件例如以下所看到的:

    INCLUDE MT48LC4M32LFB5.ini
    
    InitSDRAM();                                // Initialize memory
    
    LOAD ..SDRAM_objuc1788.axf INCREMENTAL    // Download program
    
    /* RNR */
    _WDWORD(0xE000ED98, 0x00000000);            // Use No.0 MPU
    
    /* RBAR */
    _WDWORD(0xE000ED9C, 0xA0000000);            // Set MPU base addr
    
    /* RASR */
    _WDWORD(0xE000EDA0, 0x03000031);            // Set MPU size and permission
    
    /* SHCSR */
    // _WDWORD(0xE000ED24, 0x00000100);            // Enable memory managemeng fault
    
    /* MPU_CONTROL */
    _WDWORD(0xE000ED94, 0x00000005);            // Enable MPU
    
    /* VTOR */
    _WDWORD(0xE000ED08, 0x10000000);            // Set vector table offset
    
    SP = _RDWORD(0x10000000);                   // Set stack pointer
    PC = _RDWORD(0x10000004);                   // Set program counter
    

    这样就能像在FLASH中调试一样来在RAM中调试了。

    可是,程序中不要出现不论什么操作RAM的代码!一定记住!


    • 程序装入RAM中起始地址出错


    所谓起始地址,就是指程序刚装入RAM中,还没有执行的那个pc地址。

    正常情况下,这个地址是指向芯片启动代码的systemiInit()这个函数所在的地址的,可是有些时候这个地址会出错,跑到一个不知道什么地址的地方去。


    例如以下图所看到的:



    我分析出现这个现象的原因是在jlink对RAM的配置方面有问题,也就是对RAM没有配置成功,导致程序没有成功存入RAM中,或者是储存成功,可是读取失败,或者储存读取都有问题。


    解决方式就是查看内存中的数据,看是否正确。或者对设备又一次上电,对jlink复位后又一次调试。


    这个问题在我这里偶尔会出现,但又一次调试又会消失,所以到眼下我也没弄清楚这个问题出现的原因。


    • 程序跑入HardFault_Handler

    HardFault_Handler是指指令错误中断,通常是由于程序代码翻译成的汇编代码中出现了错误的指令。

    解决问题的方法通常是找出错误指令的地址,假设地址是在当前分配的代码段的最開始位置(比方我在分散载入里面将这段代码放在0xA0001000-0XA0002000之间,然后出错指令出如今0xA0001010这个位置);

    或者在上个代码段的结束位置(比方我将这段代码放在0xA0001000位置处,上段代码在0xA0000900结束,而出错指令在0xA0000920这个位置);

    普通情况下是内存不够用导致的,并且出错的语句通常是向全局变量赋值的语句。由于内存不够用,可是jlink在向RAM中写数据的时候并不知道内存不够,导致后来溢出的数据折返回代码段起始地址,将曾经的内容覆盖所导致的。(内存覆盖)


    解决问题的方法就是换一个大的RAM。


    另一种可能,就是在程序中有操作RAM的代码,这也会导致RAM中内容被改动,出现错误的指令。


    • 程序中途跑飞


    上一条中的问题也有可能导致程序中途跑飞,此外另一种可能就是指针问题。

    指针没有正确初始化,使用了未初始化的指针,或者是指针没有正确回收,导致出现野指针,是最常见的,也是最easy导致程序跑飞的原因。出现故障应最先考虑此因素。


    排除了以上的因素后,能够依据那些常规方法,比方查看LR寄存器的值,找到出现故障的语句等方法来查找详细原因。


    • 更换新外部RAM时要注意的问题


    更换新RAM之前先看RAM的手冊和芯片的手冊,看清楚使用的芯片支持不支持新的RAM,我就由于没看清楚手冊导致买了芯片不支持的RAM,既费钱又费力。。。。

    芯片user manul上一般都有一个表,表中就是它所支持的全部RAM的类型。例如以下图:



    先查清楚再买芯片,血淋淋的教训。。。


    不同的RAM读写规则有些会有所不同,配置也不尽同样,所以在更换新RAM时要细致读懂RAM的datasheet,对其清楚掌握。

    基本上须要配置的最主要參数有下面几个:页大小,外部总线地址映射(行,列,bank),空间大小,位数,读写的算法(在RAM自己的寄存器中配置);

    外部总线地址映射要与芯片相应,然后通过上表来确定配置寄存器的值。(程序在RAM中调试要改动jlink的ini文件)



    • 结语:临时就想到了这些,以上的问题都是我在实际项目中碰到的问题,和一些经验介绍。因为我自己也学艺不精,能力有限,所以难免有错误的地方,希望路过的大神能够帮忙指正。同一时候声明,仅供參考。


    转载请注明出处:waitig's blog


  • 相关阅读:
    《Think in Java》(十四)类型信息
    《Think in Java》(十三)字符串
    《Think in Java》(十二)通过异常处理错误
    《Think in Java》(十七)容器深入研究
    《Think in Java》(十一)持有对象
    集合框架概览
    Gulp 自动化构建过程
    自动化打包 CSS
    更新 Node 稳定版本命令
    mac 命令行打开vscode
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4233191.html
Copyright © 2020-2023  润新知