资讯详情

AVR单片机的端口位操作方法解析:位域bit field(和STM32位带bit-band不同,位带是利用位域的功能做成的)...

AVR单片机端口位操作方法分析

来源:未知作者:工程师周亮2018年11月22日 16:07?3708次阅读

正在上传…重新上传取消0

一、常规方法

AVR单片机的各种教种教材或编程应用参考资料,介绍的端口位操作方法无非是宏定义、整体和常量相或相和相,以实现单位状态的变化。

PORT&=-(1为:PORTB&=oxfd;作用是PB1清零,其余不变。类似之处在于:

PORTBl=(1《4);PB4位置,其余位置不变。

PORTB=(k《4);PB4翻转,其余不变。

上述方法,无论是常数值参与还是移位操作,使用总是太麻烦,不够直观,具体的常数值也需要手动计算,容易出错。移位操作生成的目标代码太大,占用系统内存或大量使用flash执行效率低的空间。

从实际应用效果来看,宏定义比较好。还喜欢使用宏定义。C编译器在编译之前会事先进行宏替换,所以,如果宏定义讲究一些技巧,则代码执行效率将得到较大提升,编程时,操作也得心应手。

建议使用以下宏定义常规参考手册或资料:

AVR单片机的端口位操作方法解析

上图最后几行操作可参考以下内容,因为水印看不清楚

#define Set_Bit(val, bitn) (val |=(1<<(bitn))) #define Clr_Bit(val, bitn) (val&=~(1<<(bitn))) #define Get_Bit(val, bitn) (val &(1<<(bitn)) ) 三个用于设置一个,清除一个,取一个值. 使用方法为.Set_Bit(PORTA,3); Clr_Bit(PORTB,2); Get_Bit(val,5);

根据第一张图片添加一个位取反的宏定义

#define Cpl_Bit(val, bitn) (val ^=(1<<(bitn))) 2021.10.15

宏定义后,可以构成一个头文件,然后添加到新的工程文件中(编译器使用)ICC的ICC6.31A),但之前需要包含相应单片机的头文件,如:MCU为AT-MEGA48,先包括iom48V.h之后,可以在程序中使用此自制头文件:

该方法在使用时仍采用移位操作,只是为了方便程序操作,只在小规模程序中使用。

下面,我们将充分利用C语言本身强大的位置运算能力和指针,构建一个ICC6.31A平台下的定义头文件,希望能给大家一些启示。

首先明确几个基本概念:位置、地址绑定、和VolaTIle限定关键字。

在ICC6.31A有一个安装目录in-clude文件夹中有大量的编译器开发人员为我们开发了各种各样的文件夹AVR以下是单片机预订头文件。iom48v.h头文件。

注:每个端口都有PIN、DDR、PORT三八位寄存器。

总结如下:

VolaTIle -单词用于规定C编译器不允许优化其有限的变量。

结合这个宏定义iom48v.h根据第一个文件,前面有这个文件对每个端口寄存器的定义。在上述宏语句中,ox25被迫转化为指针常量,事实上,上述宏定义的意义如下:PORTB地址被强制定义ox25上定义了无符号字符变量PORTB,被迫绑定ox25地址。其他句子等等。

有了这个概念,我们再来看看。C定义语言中的位置。特殊结构可以在标准C语言中定义,定义结构中的单位结构中的单位。基本构成如下:

自定义操作头文件中,下面给出的位域定义等头文件定义了一个位域BYTE_BIT。

自定义avt_bit.h头文件(节选,以ATMEGA以48为例,定义B口)

每个端口都可以按照同定义每个端口。

结合头文件的相关定义,结合位置的概念,重新绑定其特定地址。然后将每个寄存器从一个字节逐步分成可控的8个位置。在上述文件中,仅表示B端口。另外,Atmega48的端口不完全,只有B、C、D没有A口,D口8位,C口为6位.B口为7位,如果外部晶体,PB6和PB7不能用作另一个端口。因此,端口非常不完整,但为了保持位置的完整性和一致性,便于理解,它将被使用B、C、D三口均作8bit对待。在实际操作中,注意不要操作实际不存在的位置。当然,如果你感兴趣,你可以改变文件的相关定义来改进它。

为了验证这个头文件,我很快编写了一个测试程序Beep.e,具体如下:

在电路拓扑中,在ATMEGA48的PBO脚,即(14)脚外接三极管(b极),蜂鸣器由三极管控制。程序很简单。为了更直观,在PORTB发光二极管连接到口中其他未使用的引脚上。此时,在测试过程中,将端口初始化函数语句改为:

DDRB=oxff;PORTB=ox00;(或PORTB=oxff;视二极管连接法而定),用蜂鸣器观察二极管PBO位置是否可以单独移动。当然,使用此定义头文件可以实现单片机任何一口的位置操作。

二、扩展应用

基于上述原理,结合C语言取地址运算符&,实现一个通用定义头文件并不难,适合所有人AVR单片机,但代码比较复杂。你不妨自己试试。

注:以上文件为参考ICC6.31A编译器的include在使用文件夹之前,请先包含系统提供的内容MCU将自定义的头文件复制到所建工程中,然后自由使用。

就目前的使用情况而言,使用情况AT-MEGA48/16/128的MCU因此,即使定义了三个相应的位置操作头文件,也可以。一般文件往往体积大,编制麻烦,可读性差,不适合普通爱好者。

AVR单片机端口位操作方法分析 - 控制/MCU - 电子爱好者网AVR分析单片机的端口位操作方法——宏定义后,可以构成一个头文件,然后添加到新的工程文件中(编译器)ICC的ICC6.31A),但之前需要包含相应单片机的头文件,如:MCU为AT-MEGA48,先包括iom48V.h之后,可以在程序中使用此自制头文件:http://www.elecfans.com/emb/danpianji/20181122820061.html

AVR的两种位操作的比较(位域方式和移位宏方式)测试环境如下:硬件:AT90S2313软件:    WiinAVR gcc3.3   -Os级优化(最小size)。说明:    由于AVR不支持位操作,所以必须通过软件来实现。下面对我所知道的两种方法进行一个简单的比较。    1、位域方式。先定义一个位域,            typedef struct _bit_struct             {                 unsigned char bit0 : 1 ;                 unsigned char bit1 : 1 ;                 unsigned char bit2 : 1 ;                 unsigned char bit3 : 1 ;                 unsigned char bit4 : 1 ;                 unsigned char bit5 : 1 ;                 unsigned char bit7 : 1 ;                 unsigned char bit6 : 1 ;             }bit_field;         再用一个宏    ,来指向要操作的位。              #define LED             GET_BITFIELD(PORTB).bit0              #define BUTTON      GET_BITFIELD(PINB).bit7         使用时只需要直接赋值即可:如LED =     0 ,LED = 1,  或者直接判断 LED==0    ,    LED ==1.         这种方法类似C51中的位操作。直接。    2、位移宏方式。主要有三个.                 #define Set_Bit(val, bitn)    (val |=(1<<(bitn)))                #define Clr_Bit(val, bitn)     (val&=~(1<<(bitn)))                #define Get_Bit(val, bitn)    (val &(1<<(bitn)) )         三个分别用来设置某一位,清除某一位,取某一位的值.           使用方法为.Set_Bit(PORTA,3);   Clr_Bit(PORTB,2);   Get_Bit(val,5);

根据最开始的第一张图片再加一个位取反的宏定义  #define Cpl_Bit(val, bitn)    (val ^=(1<<(bitn)))

2021.10.15    cpl取反指令的全称是三个字母首字母简写Converse Position Logical,    3、测试程序.            说明,假设PORTB.7接按纽,PORTB.0 接LED            测试程序完成如下操作。                   当BUTTON == 0时 ,LED输出1 否则输出0,                   这样的目的是即测试了输入,又测试了输出1和输出0,相对全面一点。  C代码如下.                     // testled.c     测试AVR的位操作.                 // 这是gcc;如是其它编译器,请修改。                #include <avr/io.h>                 // 定义一个寄存器(Register)或端口(Port)的八个位                typedef struct _bit_struct                {                     unsigned char bit0 : 1 ;                     unsigned char bit1 : 1 ;                     unsigned char bit2 : 1 ;                     unsigned char bit3 : 1 ;                     unsigned char bit4 : 1 ;                     unsigned char bit5 : 1 ;                     unsigned char bit7 : 1 ;                     unsigned char bit6 : 1 ;                 }bit_field;                  //定义一个宏,用来得到每一位的值                #define GET_BITFIELD(addr) (*((volatile  bit_field *) (addr)))                //定义每一个位                 #define LED             GET_BITFIELD(PORTB).bit0                 #define BUTTON      GET_BITFIELD(PINB).bit7                #define Set_Bit(val, bitn)    (val |=(1<<(bitn)))                 #define Clr_Bit(val, bitn)     (val&=~(1<<(bitn)))                 #define Get_Bit(val, bitn)    (val &(1<<(bitn)) )                 int main( void )                 {                     DDRB = 0x41;   //配置PB0为输出,PB7为输入                    if ( BUTTON==0 )     LED = 1; else LED = 0;                     //if(!Get_Bit(PINB,7) )  Set_Bit(PORTB,0);    else Clr_Bit(PORTB,0);//宏定义后面做语句也要加分号2021.10.16                    while(1);                 }                 //     ----------------------        end         -----------------------------     4、测试过程。       a.先使用位域方式。       主程序中使用 if ( BUTTON==0 )     LED = 1; else LED = 0;        结果如下:                     int main( void )                     {                       4a:    cf ed           ldi    r28, 0xDF    ; 223                       4c:    d0 e0           ldi    r29, 0x00    ; 0                       4e:    de bf           out    0x3e, r29    ; 62                       50:    cd bf           out    0x3d, r28    ; 61                         DDRB = 0x41;      //配置PB0为输出,PB7为输入                      52:    81 e4           ldi    r24, 0x41    ; 65                      54:    87 bb           out    0x17, r24    ; 23                        if ( BUTTON==0 )     LED = 1; else LED = 0;                       56:    86 b3           in    r24, 0x16    ; 22                       58:    e8 2f           mov    r30, r24                       5a:    ff 27           eor    r31, r31                       5c:    80 81           ld    r24, Z                       5e:    86 fd           sbrc    r24, 6                       60:    07 c0           rjmp    .+14         ; 0x70                       62:    88 b3           in    r24, 0x18    ; 24                       64:    e8 2f           mov    r30, r24                       66:    ff 27           eor    r31, r31                       68:    80 81           ld    r24, Z                       6a:    81 60           ori    r24, 0x01    ; 1                       6c:    80 83           st    Z, r24                       6e:    06 c0           rjmp    .+12         ; 0x7c                       70:    88 b3           in    r24, 0x18    ; 24                       72:    e8 2f           mov    r30, r24                       74:    ff 27           eor    r31, r31                       76:    80 81           ld    r24, Z                       78:    8e 7f           andi    r24, 0xFE    ; 254                       7a:    80 83           st    Z, r24                         while(1);                       7c:    ff cf           rjmp    .-2          ; 0x7c          main函数共52Bytes.其中,从lst文件看得出:main函数的初始化用了4条指令,8Bytes. 最后一句while(1);用了1条指令2Bytes.( for循环和do-while也是)          DDRB=0x41用了2条指令4Bytes. 计算一下:52-8-4-2=38Bytes,即if ( BUTTON==0 )     LED = 1; else LED = 0; 这句用了19条指令38Bytes. (居然运用了3个寄存器白r24,r30,r31,和一个Z,代码真是苦涩,,我看不懂,准备以后作代码加密用

.  )        b.使用移位宏方式。       将 if ( BUTTON==0 )     LED = 1; else LED = 0;  换为等效的     if(!Get_Bit(PINB,7) )  Set_Bit(PORTB,0);    else Clr_Bit(PORTB,0);        结果,main函数仅24Bytes.其它代码一样,略去. 所以,上面这句代码仅用了24-14=10Bytes ,5条指令。生成的代码如下:               56:    b7 99           sbic    0x16, 7    ; 22               58:    02 c0           rjmp    .+4          ; 0x5e               5a:    c0 9a           sbi    0x18, 0    ; 24               5c:    01 c0           rjmp    .+2          ; 0x60               5e:    c0 98           cbi    0x18, 0    ; 24     5. 菜论:鱼和熊掌。      由于AVR可以对I/O脚进行sbic,sbi,cbi,这样的位操作,所以使用I/O脚操作时,移位宏可以产生高效的代码。       例如,要实现上面的几个简单的指令,为了实现LED=1这样的类似C51的sbit的效果,我必须多付出(38-10=28Bytes)的代价。     6......         对于I/O脚,可以产生这样高效的代码,是因为有sbi和cbi这样的指令(但是这两指令仅限于地址在00-1F之间的地址),那么对于一般的变量,又如何呢?. 

AVR的两种位操作及比较 - 单片机/MCU论坛 - 电子技术论坛 - 广受欢迎的专业电子论坛!

标签: 2bf5a二极管2bf5a

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台