gha20028 发表于 2011-6-14 17:03:46

IAR FOR NEC 定时器间隔不能小于86.4us的分析-------IAR代码优化问题

一.
近日在用0537D调试定时器H0的定时间隔功能,间隔时间设为16.25us,在定时器中断处理函数中将P10口的状态反转,实现在P10口输出方波,方波脉宽为16.25us。通过示波器观察P10口波形,发现波形脉宽并不是需要的16.25us,而是86.4us(大概),而且不管将CMP00的值该为多少都是86.4us的脉宽
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648595NOATCK.JPG
示波器波形 (原文件名:11.JPG)
只有把定时时间设置大于86.4us才能正常输出,比如设置为100us,示波器能观察到100us脉宽的波形。
IAR程序如下:

#include "io78f0537_64.h"
#include "intrinsics.h"

#pragma location = "OPTBYTE"
__root const unsigned char option_bytes[]={0x7F,0x01,0x00,0x00,0x03}; /*设置选项字节*/

#define PM00    PM0_bit.no0
#define PM01    PM0_bit.no1
#define PM10    PM1_bit.no0
#define IRTXD   P1_bit.no0
/*时钟初始化*/
void Clock_inital(void)
{
OSCSEL=0;
MSTOP=1;
RSTOP=0;
MCM=0;
XTSTART=0;
PCC = 0;
LSRSTOP=1;
EXCLK=1;
EXCLKS=0;
OSCSELS=1;
WDTE = 0XAC;

}
/*定时器H0初始化*/
void TMH0_ini(void)
{
        TMHMD0 = 0x00;/* 8M*/
        CMP00 = 130;/* 13016.25us 38-40k*/
        TMMKH0 = 0;/*开中断*/
       
}

void main(void)
{
    __disable_interrupt();
    Clock_inital();
    PM10 = 0;/*-----P10口输出*/
    TMH0_ini();
    TMHE0 = 1;/*------开始定时器H0*/   
    IMS = 0xcc;
    IXS = 0x00;
    __enable_interrupt();

    while(1)
    {
      WDTE = 0XAC;
    }
   
}
/*-------定时器H0中断处理函数*/
#pragma vector = INTTMH0_vect
__interrupt void MD_INTTMH0(void)
{
        TMIFH0 = 0;
        IRTXD = ~IRTXD;
        WDTE = 0XAC;

}
但是把上面的程序移到PM+里面,定时为16.25us,在示波器里也能正常观察到16.25us脉宽的波形。
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648600FXLJ2U.JPG
(原文件名:22.JPG)
PM+程序如下:
#pragma sfr
#pragma DI
#pragma EI
#pragma NOP
#pragma HALT
#pragma STOP


#define   IRTXD   P1.0

#pragma interrupt INTTMH0 MD_INTTMH0

void Clock_inital(void)
{

OSCSEL=0;
MSTOP=1;
RSTOP=0;
MCM=0;
XTSTART=0;
PCC = 0;
LSRSTOP=1;



EXCLK=1;


EXCLKS=0;
OSCSELS=1;


WDTE = 0XAC;

}


void TMH0_ini(void)
{
        TMHMD0 = 0x00;/* 8M*/
        CMP00 = 130;/* 13016.25us*/
        TMMKH0 = 0;

}



main()
{
    DI();
    Clock_inital();
   
    PM1.0 = 0;
   
    IMS = 0xcc;
    IXS = 0x00;

    TMH0_ini();
   
   
    TMHE0 = 1;
   
   EI();

   
   
    while(1)
    {
      WDTE = 0XAC;
    }
}


__interrupt void MD_INTTMH0(void)
{
        TMIFH0 = 0;
        IRTXD = ~IRTXD;
        WDTE = 0XAC;

}
Option.asm 文件
OPT CSEG AT 0080H
OPTION: DB 7FH                   ; Enables watchdog timer operation (illegal access detection operation),
                                                ; Window open period of watchdog timer: 50%,
                                                ; Overflow time of watchdog timer: 210/fRL,
                                                ; Internal low-speed oscillator can be stopped by software.
      DB 01H                         ; 2.7 V POC mode
      DB 00H                         ; Reserved area
      DB 00H                         ; Reserved area
      DB 03H                        ; On-chip debug operation enabled

END



分析:既然程序都是一样的,配置字节也是一样,但是输出波形却不一样,那么应该就是IAR的设置问题。通过查阅资料和咨询高手,推断是IAR效率的问题,既然是效率的问题,那么就有可能跟IAR编译优化有关。打开项目的IAR的优化设置选项如图1:
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648602L3B8ZE.JPG
图1 (原文件名:图1.JPG)

项目默认的优化(optimizations)Level项选择的是Low,将low该为Medium,图2
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648604H6RZB2.JPG
图2 (原文件名:图2.JPG)

重新编译后运行
在示波器里观察到想要的波形,脉宽16.25us
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648605C2QBV2.JPG
(原文件名:22.JPG)
将优化选择为high也能得到正确结果。但是选择为None和low是一样的。
通过上面的实验初步得出结论:IAR优化选项会影响定时器执行效率。但是具体的影响在什么地方却不知道!!!!????./emotion/em003.gif

将上面的结论用到项目里定时器模拟38K红外发送程序里面又出现了新问题:
程序如下:
#include "io78f0537_64.h"
#include "intrinsics.h"
#include "nec78f0xb.h"

#define U1200CR5092   /* UART0 = 1200CR50=92*/
#define U1200CR51208   /* UART0 = 1200CR51=208*/

#define U2400CR5046    /* UART0 = 2400CR50=46*/
#define U2400CR5185   /* UART0 = 2400CR51=104*/

#pragma location = "OPTBYTE"
__root const unsigned char option_bytes[]={0x60,0x01,0x00,0x00,0x03}; //--------设置选项字节





unsigned char count = 0;

unsigned char IRSNEDFLAG;
unsigned char IRREVOKSET;
unsigned char IRHEAD1SET;
unsigned char IRLENTHSET;

typedef structM2A_uart_data
{
    unsigned charHead1;
    unsigned charLenth;
    unsigned charHead2;
    unsigned charAfn;
    unsigned charData_u;
} _M2A_uart_data;

_M2A_uart_data IRDATA;


void nopn(void)
{
        unsigned int i;
        for(i=0;i<40000;i++)
        {
               
        }
}

//时钟初始化
void Clock_inital(void)
{
EXCLK=1;
OSCSEL=0;
XTSTART=0;
EXCLKS=0;
OSCSELS=1;
PCC = 0;
LSRSTOP=1;
RSTOP=0;
XSEL=0;
MCM0=0;
MSTOP=1;
WDTE = 0XAC;

}

//串口0初始化
void UART0_inital(void)
{
/*    |------|------|------|------|------|------|------|------|*
       |7   |6   |5   |4   |3   |   2|1   |0   |
BRGC0 |------|------|------|------|------|------|------|------|
       |TPS01 |TPS00 |   0|MDL04 |MDL03 |MDL02 |MDL01 |MDL00 |
       |------|------|------|------|------|------|------|------|*/   

BRGC0 = 0x09;/* fxclk0=TM50k=9*/


/*    |------|------|------|------|------|------|------|------|*
       |7   |6   |5   |4   |3   |   2|1   |0   |
ASIM0 |------|------|------|------|------|------|------|------|
       |POWER0|TXE0|RXE0|PS01|PS00| CL0|SL0   |1   |
       |------|------|------|------|------|------|------|------|*/   

ASIM0 = 0x05;

POWER0 = 1;
TXE0 = 0;
RXE0 = 1;


PM10 = 0;
IRTXD = 1;

PM11 = 1;
PU11 = 0;

IRRXD = 1;

SRMK0=0;
STMK0=1;

WDTE = 0XAC;

}

//定时器50初始化为UART0的时钟
void TM50_ini(void)
{
    P17 = 0;
    PM17 = 0;
/*   |------|------|------|------|------|------|------|------|*
       |7   |6   |5   |4   |3   |   2|1   |0   |
TCL50 |------|------|------|------|------|------|------|------|
       |   0|0   |0   |   0|   0|TCL502|TCL501|TCL500|
       |------|------|------|------|------|------|------|------|*/   
    TCL50 = 0x04;/*f=8M/2^2=2M */
   
                  
    CR50 = U2400CR50;/* (1/2M) * 80 = 40usUART0=2400 CR50=46UART0=1200 CR50=92*/
/*   |------|------|------|------|------|------|------|------|*
       |7   |6   |5   |4   |3   |   2|1   |0   |
TMC50 |------|------|------|------|------|------|------|------|
       |TCE50 |TMC506|0   |0   | LVS50|LVR50 |TMC501|TOE50 |
       |------|------|------|------|------|------|------|------|*/      
    TMC50 = 0x8A;
   
   
    TMMK50 = 1;
   
    WDTE = 0XAC;
}
//定时器51初始化为红外波特率控制定时器
void TM51_ini(void)
{
        TCL51 = 0x05;/*f= 8M/64 = 0.25M*/
        CR51 = U2400CR51;   /* (1/0.25M) *208 = 833us   1s/1200=833us,1/2400 = 416us CR51 = 104 */
        TMC51 = 0x0A;
    TMMK51 = 0;
    TMPR51 = 0;
       
}
//定时器H0初始化为38K载波输出
void TMH0_ini(void)
{
        TMHMD0 = 0x00;/* 8M*/
        CMP00 = 130;/* 130   38-40k*/
        TMMKH0 = 0;
        TMPRH0 = 1;
}

//红外数据模拟发送函数
void Irsend(unsigned char sdata)
{
    unsigned char senddata,i;
   
   
    senddata = sdata;
   
    IRSNEDFLAG = 0;
    /*IRTXD = ~IRTXD;*/
   
    TCE51 = 1;
    TMHE0 = 1;
    do{}while( 0 == IRSNEDFLAG);

    for(i=0;i<8;i++)
    {
      if(senddata-(senddata/2)*2)
      {
            IRSNEDFLAG = 0;
            IRTXD = 0;
            TCE51 = 1;
            do{}while( 0 == IRSNEDFLAG);
      }
      else
      {
            IRSNEDFLAG = 0;
            
            IRTXD = ~IRTXD;
            TMHE0 = 1;
            TCE51 = 1;
            do{}while( 0 == IRSNEDFLAG);   
      }
      senddata = senddata >> 1;
    }
   
    IRSNEDFLAG = 0;
    IRTXD = 0;
    TCE51 = 1;
    do{}while( 0 == IRSNEDFLAG);
    IRSNEDFLAG = 0;
   
}

void main(void)
{
    DI();
    Clock_inital();
    UART0_inital();
    IMS = 0xcc;
    IXS = 0x00;
    TM50_ini();
    TM51_ini();
    TMH0_ini();

   
    EI();
   
    Irsend(0X12);
    nopn();
   
    Irsend(0X34);
    nopn();
   
    Irsend(0X56);
    nopn();
   
    Irsend(0X78);
    nopn();
   
    Irsend(0X9A);
    nopn();
   
    Irsend(0XBC);
    nopn();
   
   
   
    while(1)
    {
            Irsend(0X78);
            /*moni_uart(0x68);*/
            /*TXS0 = 0X56;*/
            /*nopn();*/
    }
   
}


//-----定时器51中断处理,间隔时间为波特率时间
#pragma vector = INTTM51_vect
__interrupt void MD_INTTM51(void)
{
        TMIF51 = 0;
        TMHE0 = 0;
        TCE51 = 0;
        IRSNEDFLAG = 1;
        WDTE = 0XAC;

       
}
//----定时器H0中断处理函数,间隔时间为(1/38k)/2
#pragma vector = INTTMH0_vect
__interrupt void MD_INTTMH0(void)
{
        TMIFH0 = 0;
        IRTXD = ~IRTXD;
        WDTE = 0XAC;

}

#pragma vector = INTSR0_vect
__interrupt void JS_INTSR0(void)
{
        unsigned char rdata0;
    unsigned char cwzt;
   
    cwzt = ASIS0;
    if(0x00 != cwzt)
    {
            return;
    }
    rdata0 = RXB0;
    SRIF6 = 0;
    IRDATA.Data_u = rdata0;
    count ++;/*overtime control*/
    if(count >= 200)
    {
            count = 0;
    }
   
}
当把工程的优化设置为图3所示的medium时
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648606RSSSU0.JPG
图3 (原文件名:图3.JPG)

用示波器观察P10口却没有观察到波形,查看汇编程序
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648607NUVM0C.JPG
图4 (原文件名:图4.JPG)
发现优化后的程序在do()while()语句处成了死循环,所以后面的程序都执行不了。
怎么办,让这个函数不优化,查看#pragma optimize的用法:
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648608MJRV3E.JPG
(原文件名:tt7.JPG)
描述:使用pragma optimize 指令可以减小优化等级或者关掉一些优化选项,这个指令只影响紧跟在其后的函数。这个指令的参数只能为比项目优化等级低的参数,比如项目优化选择为low,那么就不能用#pragma optimize = medium来定义函数,如果定义了会被编译器忽略。

所以我在Irsend函数前面加上
#pragma optimize = none
让这个函数不优化:

//红外数据模拟发送函数
//#pragma optimize = none
void Irsend(unsigned char sdata)
然后编译运行,在P10口观察到了发送的调制好的红外信号。



关于避免程序优化后不能执行还有其他方法:
1.        volatile 的使用
比如有事用for循环写延时函数
for(i=0i<100;i++);
上面的语句可以用来延时,但是如果选择(high)高优化,那么上面的语句就会被优化掉
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648609IY9DOK.JPG
(原文件名:图6.JPG)
整个delay函数被优化成了一条RET语句。
当使用volatile 定义后
http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648610JCPU1R.JPG
(原文件名:图7.JPG)
Delay函数并没有被优化。

关于其他避免程序被优化掉的方法希望大家能够给点意见!!!./emotion/em003.gif

sophie0403 发表于 2011-6-15 14:34:20

LZ真仔细,赞~~~

iepavb 发表于 2013-6-17 21:43:21

{:smile:}
LZ真仔细,赞~~~

1125526801 发表于 2015-7-22 22:34:01

学习了,谢谢分享
页: [1]
查看完整版本: IAR FOR NEC 定时器间隔不能小于86.4us的分析-------IAR代码优化问题