【问题标题】:Address Error ISR地址错误 ISR
【发布时间】:2014-07-08 13:51:27
【问题描述】:

我正在尝试在 dsPIC30f3011 微控制器上运行和调试 C 程序。当我在 MPLAB 中运行我的代码时,代码总是倾向于在这个 ISR 处停止,并且我完全没有任何变量的输出,我的代码甚至没有执行。我认为这似乎是某种“陷阱”程序,用于捕捉简单的错误(即振荡器故障等)。我使用的是 MPLabIDE v8.5,MPLab ICD3 处于调试模式。值得一提的是,MPLAB 显示我已连接到目标(dsPIC)和 ICD3。有人可以告诉我为什么会出现这个问题吗?

这是 ISR:

void _ISR __attribute__((no_auto_psv))_AddressError(void)
{

        INTCON1bits.ADDRERR = 0;
        while(1);
}

这是我的代码,首先是初始化,然后是 PID 使用,然后是 DSP 函数, 然后是派生语法/算法的实际DSP头文件。在我定义 DutyCycle 的地方也存在某种问题。

//////////////////////////初始化///////////// ///////////////////////////////////p>

  #include "dsp.h"    //see bottom of program


    tPID SPR4535_PID;                   // Declare a PID Data Structure named, SPR4535_PID, initialize the PID object       
    /* The SPR4535_PID data structure contains a pointer to derived coefficients in X-space and */
    /* pointer to controller state (history) samples in Y-space. So declare variables for the */
    /* derived coefficients and the controller history samples */
    fractional abcCoefficient[3] __attribute__ ((space(xmemory)));          //  ABC Coefficients loaded from X memory
    fractional controlHistory[3] __attribute__ ((space(ymemory)));          //  Control History loaded from Y memory
    /* The abcCoefficients referenced by the SPR4535_PID data structure */
    /* are derived from the gain coefficients, Kp, Ki and Kd */
    /* So, declare Kp, Ki and Kd in an array */
    fractional kCoeffs[] = {0,0,0};     
//////////////////////////////////PID variable use///////////////////////////////

void ControlSpeed(void)
{
    LimitSlew();
    PID_CHANGE_SPEED(SpeedCMD);
    if (timer3avg > 0)  
        ActualSpeed = SPEEDMULT/timer3avg;
    else
        ActualSpeed = 0;
    max=2*(PTPER+1);
    DutyCycle=Fract2Float(PID_COMPUTE(ActualSpeed))*max;
    // Just make sure the speed that will be written to the PDC1 register is not greater than the PTPER register
    if(DutyCycle>max)
        DutyCycle=max;
    else if (DutyCycle<0)
        DutyCycle=0;
}


//////////////////////////////////PID functions//////////////////////////////////

        void INIT_PID(int DESIRED_SPEED)
    {
        SPR4535_PID.abcCoefficients = &abcCoefficient[0];    //Set up pointer to derived coefficients 
        SPR4535_PID.controlHistory = &controlHistory[0];     //Set up pointer to controller history samples 

        PIDInit(&SPR4535_PID);                               //Clear the controller history and the controller output 

        kCoeffs[0] = KP;                                  // Sets the K[0] coefficient to the KP 
        kCoeffs[1] = KI;                                  // Sets the K[1] coefficient to the KI
        kCoeffs[2] = KD;                                  // Sets the K[2] coefficient to the Kd
        PIDCoeffCalc(&kCoeffs[0], &SPR4535_PID);             //Derive the a,b, & c coefficients from the Kp, Ki & Kd 

        SPR4535_PID.controlReference = DESIRED_SPEED;        //Set the Reference Input for your controller
    }

    int PID_COMPUTE(int MEASURED_OUTPUT)
    {
        SPR4535_PID.measuredOutput = MEASURED_OUTPUT;             // Records the measured output
        PID(&SPR4535_PID);  
        return SPR4535_PID.controlOutput;                                      // Computes the control output
    }

    void PID_CHANGE_SPEED (int NEW_SPEED)
    {
        SPR4535_PID.controlReference = NEW_SPEED;                   // Changes the control reference to change the desired speed
    }

/////////////////////////////////////dsp.h/////////////////////////////////////////////////

    typedef struct {
            fractional* abcCoefficients;    /* Pointer to A, B & C coefficients located in X-space */
                                            /* These coefficients are derived from */
                                            /* the PID gain values - Kp, Ki and Kd */
            fractional* controlHistory;     /* Pointer to 3 delay-line samples located in Y-space */
                                            /* with the first sample being the most recent */
            fractional controlOutput;       /* PID Controller Output  */
            fractional measuredOutput;      /* Measured Output sample */
            fractional controlReference;    /* Reference Input sample */
    } tPID;

    /*...........................................................................*/

    extern void PIDCoeffCalc(               /* Derive A, B and C coefficients using PID gain values-Kp, Ki & Kd*/
            fractional* kCoeffs,            /* pointer to array containing Kp, Ki & Kd in sequence */
            tPID* controller                /* pointer to PID data structure */
    );

    /*...........................................................................*/

    extern void PIDInit (                   /* Clear the PID state variables and output sample*/
            tPID* controller               /* pointer to PID data structure */
    );


    /*...........................................................................*/

    extern fractional* PID (                /* PID Controller Function */
            tPID* controller               /* Pointer to PID controller data structure */
    );

【问题讨论】:

  • 那么while(1); 声明肯定会挂你的芯片。我不熟悉芯片,但我想这是由于未对齐的访问(即尝试在奇数字节地址读取 16 位数量)。
  • 你在什么模式下操作?
  • @Treesrule14 如果你的意思是“模式”,就像我通过 MPLAB 驱动我的设备和 MCU 一样,那么我在“调试”中运行。
  • 通常当中断发生时,返回地址被压入堆栈。如果您可以检查堆栈以找到返回地址,那么您可以找到发生地址异常时正在执行的代码。
  • 我们可以看看你的代码吗?通常,在库代码中查找错误也不是特别有帮助。

标签: c microcontroller pic memory-address mplab


【解决方案1】:

dsPIC 陷阱没有免费提供太多信息,因此我倾向于在 ISR 中添加一点汇编语言的前言。 (请注意,堆栈错误陷阱有点复杂,因为它在堆栈已经乱序时使用 RCALL 和 RETURN 指令。)

/**
 * \file trap.s
 * \brief Used to provide a little more information during development.
 *
 * The trapPreprologue function is called on entry to each of the routines
 * defined in traps.c.  It looks up the stack to find the value of the IP
 * when the trap occurred and stores it in the _errAddress memory location.
 */

.global __errAddress
.global __intCon1

.global _trapPreprologue

.section .bss

__errAddress:   .space 4
__intCon1:      .space 2

.section .text

_trapPreprologue:
; Disable maskable interrupts and save primary regs to shadow regs
    bclr    INTCON2, #15            ;global interrupt disable
    push.s                          ;Switch to shadow registers

; Retrieve the ISR return address from the stack into w0:w1
    sub     w15, #4, w2             ;set W2 to the ISR.PC (SP = ToS-4)
    mov     [--w2], w0              ;get the ISR return address LSW (ToS-6) in w0
    bclr    w0, #0x0                ;mask out SFA bit (w0<0>)
    mov     [--w2], w1              ;get the ISR return address MSW (ToS-8) in w1
    bclr    w1, #0x7                ;mask out IPL<3> bit (w1<7>)
    ze      w1, w1                  ;mask out SR<7:0> bits (w1<15..8>)

; Save it
    mov     #__errAddress, w2       ;Move address of __errAddress into w2
    mov.d   w0, [w2]                ;save the ISR return address to __errAddress

; Copy the content of the INTCON1 SFR into memory
    mov     #__intCon1, w2          ;Move address of __intCon1 into w2
    mov     INTCON1, WREG           ;Read the trap flags into w0 (WREG)
    mov     w0, [w2]                ;save the trap flags to __intCon1

; Return to the 'C' handler
    pop.s                           ;Switch back to primary registers
    return

然后,我将所有陷阱 ISR 保存在单个 traps.c 文件中,该文件使用 traps.s 中的前序。请注意,您的微控制器的实际陷阱可能会有所不同 - 请查看数据表以了解实现了哪些。

/**
 * \file traps.c
 * \brief Micro-controller exception interrupt vectors.
 */

#include <stdint.h>

#include "traps.h"      // Internal interface to the micro trap handling.

/* Access to immediate call stack.  Implementation in trap.s */

extern volatile unsigned long _errAddress;
extern volatile unsigned int _intCon1;

extern void trapPreprologue(void);

/* Trap information, set by the traps that use them. */

static unsigned int _intCon2;
static unsigned int _intCon3;
static unsigned int _intCon4;

/* Protected functions exposed by traps.h */

void trapsInitialise(void)
{
    _errAddress = 0;
    _intCon1 = 0;
    _intCon2 = 0;
    _intCon3 = 0;
    _intCon4 = 0;
}


/* Trap Handling */
// The trap routines call the _trapPreprologue assembly routine in traps.s
// to obtain the value of the PC when the trap occurred and store it in
// the _errAddress variable.  They reset the interrupt source in the CPU's
// INTCON SFR and invoke the (#defined) vThrow macro to report the fault.

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _OscillatorFail(void)
{
    INTCON1bits.OSCFAIL = 0;        /* Clear the trap flag */
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _StackError(void)
{
    INTCON1bits.STKERR = 0;         /* Clear the trap flag */
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _AddressError(void)
{
    INTCON1bits.ADDRERR = 0;        /* Clear the trap flag */
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _MathError(void)
{
    INTCON1bits.MATHERR = 0;        /* Clear the trap flag */
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _DMACError(void)
{
    INTCON1bits.DMACERR = 0;        /* Clear the trap flag */
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _HardTrapError(void)
{
    _intCon4 = INTCON4;
    INTCON4 = 0;                    // Clear the hard trap register
    _intCon2 = INTCON2;
    INTCON2bits.SWTRAP = 0;         // Make sure the software hard trap bit is clear
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

void __attribute__((interrupt(preprologue("rcall _trapPreprologue")),no_auto_psv)) _SoftTrapError(void)
{
    _intCon3 = INTCON3;
    INTCON3 = 0;                    // Clear the soft trap register
    vThrow(_intCon1, _intCon2, _intCon3, _intCon4, _errAddress);
}

vThrow 宏的实现取决于您。但是,它不应该使用堆栈,因为它可能不可用(因此没有puts() 调试调用!)在开发过程中,使用带有 NOP 语句的简单无限循环是合理的,您可以在其中断点。

(在生产构建中,我的 vThrow macro 将参数记录到 RAM 的保留区域中,该区域在启动时被链接描述文件清零,并重置微控制器。在启动期间,程序检查保留区域,如果它不为零,则记录错误事件以进行诊断。)

一旦遇到陷阱,检查 _errAddress 变量的内容将为您提供 ISR 的返回地址,即紧跟在生成中断的指令之后的地址。然后,您可以检查您的 MAP 文件以查找例程,如果您真的很热衷,请检查反汇编以查找特定指令。之后,调试由您决定。

【讨论】:

    【解决方案2】:

    正如 cmets 中所建议的,while(1) 语句是您的代码被挂起的地方。但是请注意,您的代码 正在执行 - 您只是处于无限循环中。这也是您无法查看变量或当前程序计数器的原因。通常当你通过PC主机连接到一个ucontroller时,你不能在ucontroller执行时查看状态信息。一切都运行得太快,即使是慢速,也无法不断更新您的屏幕。

    要尝试找出原因,您可以在 ISR 中设置断点并重置控制器。当断点被命中时,执行将停止,您也许可以调查堆栈帧以查看在触发 ISR 之前执行的最后一行代码。但是,这并不能保证 - 根据您的特定 ucontroller 处理中断的方式,调用堆栈在正常程序执行和中断上下文之间可能不是连续的。

    如果这不起作用,请在调用 ISR 之前在您的代码中设置一个断点,然后单步执行您的代码,直到它被调用。您在 ISR 之前执行的最后一行代码将是原因。请记住,这可能需要一些时间,特别是如果违规行在循环中并且仅在一定次数的迭代后才触发 ISR。

    编辑
    发布此答案后,我注意到您对链接脚本警告的最后评论。这是一个完美的例子,说明为什么除了极少数例外,您应该像解决编译器错误一样努力解决警告。特别是如果您不了解警告的含义和原因。

    【讨论】:

      【解决方案3】:

      PID 算法涉及乘法。在 dspic 上,这是通过内置的硬件乘法器完成的。这个乘法器有一个必须指向 xmemory 空间的寄存器和另一个指向 ymemory 空间的寄存器。然后 dsp 内核将这两个相乘,结果可以在累加器中找到(其中有两个)。

      如果将 xmemory 地址范围加载到 ymemory 寄存器中,则会触发地址错误陷阱,反之亦然。您可以通过单步执行程序集中的代码来检查这一点。

      这不是触发陷阱的唯一实例。还有可能导致此问题的硅错误,请检查勘误表。

      【讨论】:

      • 好点,特别提到勘误文档。
      猜你喜欢
      • 2023-03-22
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 2019-05-05
      • 2015-07-12
      相关资源
      最近更新 更多