打印
[AVR单片机]

AVR I/O口操作一点小经验

[复制链接]
4034|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
笑苍天|  楼主 | 2008-4-14 17:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    现象:用mega64的定时中断控制某个IO口产生一个固定频率的脉冲方波,但偶尔出现一个误差较大的波形。此IO口为PORTB7。
    解决:首先怀疑是否C程序中其他地方操作了此IO口,经查没有;然后怀疑C程序中是否有对整个PORTB口进行操作的语句,经查没有;最后,在反汇编级代码中查找,发现有如下语句:out 0x18, r25;可见C程序经编译后有对整个PORTB口进行操作,这样当中断在前述语句前发生时,若r25=0x00,而中断中把PORTB7置1的话,中断结束后执行out 0x18, r25;时便表现为波形不正确,输出0不正确依理例推。

源程序级解析:
原程序:
/* BYTE STRUCTION -----------------------------------------------------------*/
typedef union
{
    unsigned int byte;
    struct
    {
        unsigned b0  :1;
        unsigned b1  :1;
        unsigned b2  :1;
        unsigned b3  :1;
        unsigned b4  :1;
        unsigned b5  :1;
        unsigned b6  :1;
        unsigned b7  :1;
    }bit;
}BYTE_STRUCT;
/*-----------------------------------------------------------------------*/
#define _MY_SFR_IO8(io_addr)        _MY_MMIO_BYTE((io_addr) + 0x20)
#define _MY_SFR_MEM8(mem_addr)      _MY_MMIO_BYTE(mem_addr)
#define _MY_MMIO_BYTE(mem_addr)     (*(volatile BYTE_STRUCT *)(mem_addr))
/*-----------------------------------------------------------------------*/
#define  PIN_B                      _MY_SFR_IO8(0x16) 
#define  DDR_B                      _MY_SFR_IO8(0x17)
#define  PORT_B                     _MY_SFR_IO8(0x18)  
    
#define  ioWatchdog         PORT_B.bit.b5   /* 清看门狗信号io引脚 */
#define  ioPulse            PORT_B.bit.b7   /* 无功脉冲输出信号io引脚 */
/*-----------------------------------------------------------------------*/

#define    INPUT              0             /* 根据芯片定义 */
#define    OUTPUT             1             /* 根据芯片定义 */
/*-----------------------------------------------------------------------*/

#define    CLRWDT           {                                   \
                                asm( "WDR" );                   \
                                ioWatchdogDir = OUTPUT;         \
                                ioWatchdog = ~ioWatchdog;       \
                            } /* clr watchdog */
/*-----------------------------------------------------------------------*/
主函数中调用CLRWDT部分的反汇编如下(编译器是WinAVR-20070525):
139:          CLRWDT;
+000047F9:   95A8        WDR                      Watchdog reset
+000047FA:   9ABD        SBI     0x17,5           Set bit in I/O register
+000047FB:   B388        IN      R24,0x18         In from I/O location
+000047FC:   9582        SWAP    R24              Swap nibbles
+000047FD:   9586        LSR     R24              Logical shift right
+000047FE:   7087        ANDI    R24,0x07         Logical AND with immediate
+000047FF:   9580        COM     R24              One's complement
+00004800:   7081        ANDI    R24,0x01         Logical AND with immediate
+00004801:   9582        SWAP    R24              Swap nibbles
+00004802:   0F88        LSL     R24              Logical Shift Left
+00004803:   7E80        ANDI    R24,0xE0         Logical AND with immediate
+00004804:   B398        IN      R25,0x18         In from I/O location
+00004805:   7D9F        ANDI    R25,0xDF         Logical AND with immediate
+00004806:   2B98        OR      R25,R24          Logical OR
+00004807:   BB98        OUT     0x18,R25         Out to I/O location

修改后程序:(仅修改CLRWDT的宏定义)
/*------------------------------------------------------------------------------ 
xct gai 2008-3-28 
用ioWatchdog = ~ioWatchdog翻转的话编译器编译出来的语句可能不用sbi和cbi,而是对整个
端口PORTB操作,当刚好操作端口时发生中断,可能发生端口输出错误
-----------------------------------*/
#define    CLRWDT       {                                   \
                            asm( "WDR" );                   \
                            ioWatchdogDir = OUTPUT;         \
                            if( ioWatchdog ) ioWatchdog = 0;\
                            else             ioWatchdog = 1;\
                        } /* clr watchdog */
主函数中调用CLRWDT部分的反汇编如下(编译器是WinAVR-20070525):
139:          CLRWDT;
+000047D9:   95A8        WDR                      Watchdog reset
+000047DA:   9ABD        SBI     0x17,5           Set bit in I/O register
+000047DB:   9BC5        SBIS    0x18,5           Skip if bit in I/O register set
+000047DC:   C002        RJMP    PC+0x0003        Relative jump
+000047DD:   98C5        CBI     0x18,5           Clear bit in I/O register
+000047DE:   C001        RJMP    PC+0x0002        Relative jump
+000047DF:   9AC5        SBI     0x18,5           Set bit in I/O register

    经修改后不但程序按预期执行,而且代码更加精简。至此问题得到代码级的、原理级的、完全的解释。
    由此可见我们编程时对所用单片机的汇编指令和编译器的熟悉是多么重要;而对程序的测试及反汇编代码的通读又是多么必要。

相关帖子

沙发
ZRL700424| | 2008-4-15 14:13 | 只看该作者

那何必还用C呢?直接用汇编不就行了?

使用特权

评论回复
板凳
sztwp| | 2008-4-15 14:34 | 只看该作者

是啊 可见熟悉汇编是很重要的

pcbw.cn

使用特权

评论回复
地板
笑苍天|  楼主 | 2008-4-15 22:56 | 只看该作者

回ZRL700424

你可以看到我用的是WinAVR-20070525,程序也是C的,汇编是我理解AVR单片机和除bug时的工具。有时在C的层面解释不了的问题,就要到反汇编代码中去找。

使用特权

评论回复
5
song529110| | 2008-4-21 20:53 | 只看该作者

谢谢指教

谢谢指教

使用特权

评论回复
6
农民讲习所| | 2008-4-21 22:46 | 只看该作者

ioWatchdog = ~ioWatchdog -》ioWatchdog = !ioWatchdog看看

使用特权

评论回复
7
笑苍天|  楼主 | 2008-4-22 00:03 | 只看该作者

谢农民讲习所提示

ioWatchdog = ~ioWatchdog 改为ioWatchdog = !ioWatchdog后,编译后反汇编如下,可见效果是一样的(编译器都未使用SBI和CBI及SBIS等位操作指令)。

179:                          CLRWDT;
+000048BA:   95A8        WDR                      Watchdog reset
+000048BB:   9ABD        SBI     0x17,5           Set bit in I/O register
+000048BC:   B388        IN      R24,0x18         In from I/O location
+000048BD:   9582        SWAP    R24              Swap nibbles
+000048BE:   9586        LSR     R24              Logical shift right
+000048BF:   7087        ANDI    R24,0x07         Logical AND with immediate
+000048C0:   9580        COM     R24              One's complement
+000048C1:   7081        ANDI    R24,0x01         Logical AND with immediate
+000048C2:   9582        SWAP    R24              Swap nibbles
+000048C3:   0F88        LSL     R24              Logical Shift Left
+000048C4:   7E80        ANDI    R24,0xE0         Logical AND with immediate
+000048C5:   B398        IN      R25,0x18         In from I/O location
+000048C6:   7D9F        ANDI    R25,0xDF         Logical AND with immediate
+000048C7:   2B98        OR      R25,R24          Logical OR
+000048C8:   BB98        OUT     0x18,R25         Out to I/O location

使用特权

评论回复
8
Lxueqiang| | 2008-4-23 17:02 | 只看该作者

很好的东西!

收藏之!

使用特权

评论回复
9
ayb_ice| | 2008-4-23 20:11 | 只看该作者

这只能说明编译器差

使用特权

评论回复
10
宇宙飞船| | 2008-4-24 11:44 | 只看该作者

9 楼这个草包!不懂装懂!

楼主要在IO口输出方波,可以用异或的方法,任何的C编译器编译出来的汇编都少于或等于4条指令!
对于清看门狗,可以用或操作,目前流行的MCU都有或即数,编译出来肯定小于等于3条指令!

使用特权

评论回复
11
zhousd| | 2008-4-24 15:48 | 只看该作者

好象只有9楼的---》IAR编译器才是一流的哟!

只可惜IAR生不逢时,碰着GCC粉丝们,被打得一鼻子灰。

使用特权

评论回复
12
zuoyou| | 2008-4-25 10:19 | 只看该作者

re

10楼的修样也太...!
或许为#define  ioWatchdog         (PORT_B.bit.b5)   /* 清看门狗信号io引脚 */
9楼的也有点对

使用特权

评论回复
13
宇宙飞船| | 2008-4-26 13:55 | 只看该作者

WINAVR-GCC是16万的下使用记录,井底蛙们请睁大眼睛看看!!

WinAVR                                          Downloads 
Latest     20071221 Notes (2007-12-20 21:46)
      WinAVR-20071221-install.exe  Mirror     88219     i386     .exe 
      20070525 Notes (2007-05-25 12:31)
      WinAVR-20070525-install.exe  Mirror     146522     i386     .exe 
      20070122 Notes (2007-01-22 15:53)
      WinAVR-20070122-install.exe  Mirror     96773     i386     .exe 
      20060421 Notes (2006-04-21 13:44)
      WinAVR-20060421-install.exe  Mirror     163773     i386     .exe 
      20060125 Notes (2006-01-25 12:31)
      WinAVR-20060125-install.exe  Mirror     58689     i386     .exe 

使用特权

评论回复
14
taoest| | 2008-4-28 21:44 | 只看该作者

瞎折腾

明明写的是C,却要在汇编上搞研究。

使用特权

评论回复
15
ATmega32L| | 2008-4-29 08:31 | 只看该作者

看IAR:

看IAR:


#ifdef __IAR_SYSTEMS_ICC__
#define GET_IO_BITFIELD(addr)         (*(( __io volatile   bitfield  *)(addr))) 
#define __GET_IO_BIT(addr,b)        GET_IO_BITFIELD(addr).bit##b 
#define GET_IO_BIT(addr,b) __GET_IO_BIT(addr,b) 

#else 
#define GET_IO_BITFIELD(addr) GET_BITFIELD(addr) 
#define GET_IO_BIT(addr,b) GET_BIT(addr,b)
#endif


#define TEST_IO   GET_IO_BIT(&PORTB,PB0) 

看编译出来的汇编代码: 
  TEST_IO =1; 
        SBI     0x05, 0x00 


   TEST_IO =0; 
        CBI     0x05, 0x00 


 TEST_IO =!TEST_IO; 
        IN      R16, 0x05 
        SBRS    R16, 0 
        SBI     0x05, 0x00 
        SBRC    R16, 0 
        CBI     0x05, 0x00 

 TEST_IO =~TEST_IO; 
        IN      R16, 0x05 
        SBRS    R16, 0 
        SBI     0x05, 0x00 
        SBRC    R16, 0 
        CBI     0x05, 0x00 

使用特权

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

本版积分规则

8

主题

106

帖子

0

粉丝