打印
[ZLG-ARM]

R14被莫名修改,导致函数不能正确返回,出现死循环

[复制链接]
2022|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xlander|  楼主 | 2007-9-19 13:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
先说一下环境:
lpc2220,模板是zlg网站上的最新版本。


概述:
我自己写了一个串口驱动,现在UART0自收自发,UART1自收自发都正常,UART0,UART1“接力”的时候会出现问题。
所谓的接力是指同时启用Uart0,Uart1,Uart0将自己收到的数据通过Uart1发送出去,同时,Uart1将自己收到的数据通过Uart0发送出去。


现象:
在我调试的时候发现,两个串口会正常工作一段时间,然后R14不知道什么样的原因被莫名其妙的修改,在下文所述的UART1_EnableTX(),以及于此类似的UART0_EnableTX(),函数返回时R14存储的值指向自身函数的入口,出现死循环的情况。


问题:
不知道各位有没有遇到过类似的情况?如果有,是什么样的原因造成的?
另外,有一个概念我一直没弄明白,假设当前程序正在Uart1的中断服务程序(低优先)中,然后这个时候产生了Uart0中断(高优先),程序会跳转到Uart0的中断服务程序中么?


供参考的资料:
void UART1_DisableTX()
{
    U1IER = U1IER & (~0x02);
}

void UART1_EnableTX()
{
    U1IER = U1IER | 0x02;
}

UART1_EnableTX的汇编代码如下:
80005a48    [0xe59f0114]   ldr      r0,0x80005b64 ; = #0xe000c000
80005a4c    [0xe5d00004]   ldrb     r0,[r0,#4]
80005a50    [0xe3800002]   orr      r0,r0,#2
80005a54    [0xe59f1108]   ldr      r1,0x80005b64 ; = #0xe000c000
80005a58    [0xe5c10004]   strb     r0,[r1,#4]
80005a5c    [0xe1a0f00e]   mov      pc,r14        <==    此处R14=0x80005a48,为函数的入口

以下为串口驱动的代码:
其中队列部分参考了zlg的队列中间件,我的改动主要使其适合于前后台系统。


#define UART1_TXD_SEL        (1 << 16)        //P0.8
#define UART1_RXD_SEL        (1 << 18)        //P0.9
void UART1_InitTxdRxd(void)    { PINSEL0 |= UART1_TXD_SEL | UART1_RXD_SEL;}

#define UART1_MODEMSEL        (1 << 20) | (1 << 22) | (1 << 24) | (1 << 26) | (1 << 28) | (1 << 30) 
void UART1_InitModem(void)    { PINSEL0 |= UART1_MODEMSEL; }

volatile uint8 acUart1RxBuff[UART1_RX_QUEUE_SIZE];
volatile uint8 acUart1TxBuff[UART1_TX_QUEUE_SIZE];
volatile struct _tag_Queue stUart1RxQueue;
volatile struct _tag_Queue stUart1TxQueue;

void /*  __irq*/ IRQ_UART1(void)
{
    uint8 nState;
    nState = (U1IIR & 0x0E) >> 1;
    switch(nState)
    {
    case 0x06://字符超时
    case 0x02://接收
        {
            QueuePushOneChar(&stUart1RxQueue, U1RBR);
            break;
        }
    case 0x01://发送
        {
            if(QueuePullOneChar(&stUart1TxQueue, &nState) == 1)
                U1THR = nState;
            break;
        }
    case 0x00://MODEM中断
        {
            nState = U1MSR;
            break;
        }
    case 0x03://接受线状态
        {
            nState = U0LSR;
            nState = U0RBR;
            break;
        }
    }
    nEnterTimes1--;
    VICVectAddr = 0x00;                  // 中断处理结束    
}

void UART1_Init(void)
{
    UART1_InitTxdRxd();
    QueueInit(acUart1RxBuff, UART1_RX_QUEUE_SIZE, &stUart1RxQueue);
    QueueInit(acUart1TxBuff, UART1_TX_QUEUE_SIZE, &stUart1TxQueue);

    VICIntEnClr = 1 << 7;                //禁能中断
}


uint32 UART1_SetMode(const UARTMODE *pMode)
{
    extern void IRQ_Def(void);
    extern void UART1_Handler(void);
    uint32 i;
    uint8 nTemp;
    if(UartEx_IsModeValid(pMode) == 0)
        return 0;
    U1LCR = 0x80;                        // DLAB位置1

    i = (Fpclk>>4) / pMode->nBaudRate;    // 设置串口波特率
    U1DLM = i >> 8;
    U1DLL = i & 0xFF;

    nTemp = 0;
    nTemp |= (pMode->nDataLen - 5);                //数据长度
    nTemp |= (pMode->nStopBitLen - 1) << 2;        //停止位
    if(pMode->nParity != 0)                        //是否校验
        nTemp |= (pMode->nParity - 1) << 4;        //校验方式
    U1LCR = nTemp;

    if(pMode->nFullSignal != 0)
        UART1_InitModem();

    U1FCR = 0x01;                       // 使能FIFO,并设置触发点为1字节
    U1IER = 0x03;                        // 接收中断,发送中断

    VICIntEnClr = 1 << 7;                //禁能中断

    VICIntSelect &= (~(1 << 7));        //设置UART1中断分配为IRQ中断
    VICVectCntl2 = 0x20 | 7;            //UART1中断
    VICVectAddr2 = (int)UART1_Handler;    //设置UART1中断服务程序地址

    VICDefVectAddr = (int)IRQ_Def;

    VICIntEnable = 1 << 7;                //使能中断
    return 1;
}
void UART1_DisableRX()
{
    U1IER = U1IER & (~0x01);
}
void UART1_EnableRX()
{
    U1IER = U1IER | 0x01;
}
void UART1_DisableTX()
{
    U1IER = U1IER & (~0x02);
}
void UART1_EnableTX()
{
    U1IER = U1IER | 0x02;
}

uint8  UART1_Send(const uint8 *pnData, uint8 nDataLen)
{
    volatile struct _tag_Queue *pstQueue = &stUart1TxQueue;
    uint8 nLen;
    uint8 temp;
    UART1_DisableTX();
    nLen = QueuePushIn(pstQueue, pnData, nDataLen);
    if ((U1LSR & 0x00000020) != 0)
    {                                               /* UART0发送保持寄存器空 */
        QueuePullOut(pstQueue, 1, &temp);             /* 发送最初入队的数据 */
        U1THR = temp;
    }
    UART1_EnableTX();
    return nLen;
}

uint8 UART1_Receive(uint8 *pnData, uint8 nDataLen)
{
    volatile struct _tag_Queue *pstQueue = &stUart1RxQueue;
    uint8 nLen = 0;
    if(QueueGetSize(pstQueue) >= nDataLen)
    {
        UART1_DisableRX();
        QueuePullOut(pstQueue, nDataLen, pnData);
        UART1_EnableRX();
        nLen = nDataLen;
    }
    return nLen;
}

相关帖子

沙发
bald| | 2007-9-19 13:32 | 只看该作者

有中断嵌套问题,中断服务程序入口处保护LR

使用特权

评论回复
板凳
xlander|  楼主 | 2007-9-19 13:41 | 只看该作者

怎么保护

bald:
对这个"中断嵌套"一点概念没有,尽管搜索了论坛,但是都没有一个系统的教程,请问有没有相关的资料,或者您能给出一些示意性的代码么?谢谢。

使用特权

评论回复
地板
xlander|  楼主 | 2007-9-19 13:49 | 只看该作者

re

我之前就已经在irq.s加入:

UART0_Handler  HANDLER IRQ_UART0
UART1_Handler  HANDLER IRQ_UART1

这样做是不是就是说已经进行了有关“中断嵌套”的保护,除此之外,还需要做什么?

使用特权

评论回复
5
bald| | 2007-9-19 14:17 | 只看该作者

参考一下子程序嵌套例程

我对C不熟悉,不过加一句汇编十分方便
在子程序中嵌套中只要进入后加一句:STR SP!,{LR}就可以了。不过中断服务程序LR还需要加上一个偏移量。
当然结束中断进程也需要相应的出栈操作

使用特权

评论回复
6
xlander|  楼主 | 2007-9-19 15:13 | 只看该作者

新的参考资料

新的参考资料,调试过程中发现的。

我调试软件用的是ADS。

程序正常的时候:
cpsr为NzcvqiFt_SYS,即系统模式,spsr为Unavailable
不管是进入还是退出UART0_EnableTX(),都无变化

程序进入“死循环”的时候:
cpsr为NzcvqIFt_IRQ,即系统模式,spsr为NzcvqiFt_SYS

但是死循环的程序UART0_EnableTX()从来没有在中断服务程序里调用过。

请高人帮我分析下。
是程序跑飞,还是怎么回事儿?或者是别的什么地方出了什么问题?比方说队列.

使用特权

评论回复
7
xlander|  楼主 | 2007-9-19 15:23 | 只看该作者

to bald

以下是zlg模板中的代码,估计能解决中断嵌套的问题。

    MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function

        EXPORT  $IRQ_Label                      ; The label for exports 输出的标号
        IMPORT  $IRQ_Exception_Function         ; The imported labels 引用的外部标号

$IRQ_Label
        SUB     LR, LR, #4                      ; Calculate the returning address 计算返回地址
        STMFD   SP!, {R0-R3, R12, LR}           ; Protects the task environments 保存任务环境
        MRS     R3, SPSR                        ; Protects the status variable 保存状态
        STMFD   SP, {R3,LR}^                    ; Protects SPSR and SP in user status, Notice: DO NOT write back.保存SPSR和用户状态的SP,注意不能回写
                                                ; If the SP is written back, it should be adjusted to its appropriate value later.如果回写的是用户的SP,所以后面要调整SP
        NOP
        SUB     SP, SP, #4*2

        MSR     CPSR_c, #(NoInt | SYS32Mode)    ; Switch to the System Mode 切换到系统模式 
       
        BL      $IRQ_Exception_Function         ; call the C interrupt handler funtion 调用c语言的中断处理程序

        MSR     CPSR_c, #(NoInt | IRQ32Mode)    ; Switch bak to IRQ mode 切换回irq模式
        LDMFD   SP, {R3,LR}^                    ; Recover SPSR and SP in user status, Notic: DO NOT write back. 恢复SPSR和用户状态的SP,注意不能回写
                                                ; If the SP is written back, it should be adjusted to its appropriate value later.如果回写的是用户的SP,所以后面要调整SP
        MSR     SPSR_cxsf, R3
        ADD     SP, SP, #4*2                    ; 

        LDMFD   SP!, {R0-R3, R12, PC}^          ;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

165

帖子

0

粉丝