AVR C语言编程的小技巧 IO口置位(2)
- 格式:pdf
- 大小:171.01 KB
- 文档页数:15
一、AVR单片机位操作
(1)置位。
要将R的第3位置1,其他位不变,可以这样做:R |= (1<<3),其中“1<<3”
的结果是“0b00001000”,R |= (1<<3)也就是R=R|0b00001000,任何数和0相或不变,任何数和1相或为1,这样达到对R的第3位置1,但不影响其他位的目的。
(2)清位。
要将R的第2位清0,其他位不变,可以这样做:R &= -(1<<2),其中“-(1<<2)”
的结果是“0b11111011”,R&=-(1<<2)也就是R=R&0b11111011,任何数和1相与不变,任何数和0相与为0,这样达到对R的第2位清0,但不影响其他位的目的。
(3)获得某一位的状态。
(R>>4) & 1,是获得R第4位的状态,“R>>4”是将R右移4位,将R的第4位移至第0位,即最后1位,再和1相与,也就是和0b00000001相与,保留R最后1位的值,以此得到第4位的状态值。
二、AVR单片机中断向量表
三、AVR单片机引脚图。
AVR C语言编程的小技巧-IO口置位给单片机IO口置位是编程用的比较多的操作,这是我在学习C语言编程中的一点小小心得,希望大家觉得有用!AVR 单片机的IO口是标准的双向端口,首先要设置IO口的状态,即:输入还是输出DDRx寄存器就是AVR单片机的端口方向寄存器,通过设置DDRx可以设置x端口的状态。
DDRx端口方向寄存器相应位设置为1则对应的x端口相应位为输出状态,DDRx端口方向寄存器相应位设置为0则对应的x端口相应位为输入状态。
例如:DDRA = 0xFF; //设置端口A所有口为输出状态,因为0xFF对应的二进制为11111111bDDRA = 0x0F //设置端口A高4位为输入状态,低4位为输出状态,因为0x0F对应的二进制为00001111bPORTx寄存器是AVR单片机的输出寄存器,端口输出状态设定好后通过设置PORTx可以使端口x的相应位输入高电平或低电平来控制外部设备。
例如:PORTA = 0xFF; //端口A所有口线输出高电平PORTA = 0x0F; //端口A高4位输出低电平,低4位输出高电平小贴士:利用位逻辑运算符对特定的端口进行设定。
PORTA = 1<<3; //端口A第4位置为高电平,其它为低电平,应为00000001左移3位后是00001000PORTA = 1<<7; //同理,第8位置高电平有时候我们期望端口某一位设置成高电平,但是其它位的高低电平要保持不变,如何做呢?C语言是很强大的,有办法!如下:PORTA |=1<<3; //实现端口A第4位置为高电平,其它位的高低电平不受影响上面的语句是简化的写法,分解一下就是:PORTA = PORTA | (1<<3); //数字1左移3位后与端口A进行按位或,结果就是端口A 第4位置为高电平,其它位的高低电平不受影响那么大家就会问了,如何实现设置某一位为低电平,其它位的高低电平不变呢?建议大家思考1分钟再看下面的内容。
对于位运算的操作是基本不涉及的,但是在但是在单片机单片机系 它实际上控制着它实际上控制着PB 口的8个端口PB0-PB7的方向,也就是说它的每一位都控制一个端口的方向,如果我们要把端口PB0-PB3设置为输出口,而把PB4-PB7 这就牵出了这就牵出了这就牵出了单片机单片机C 我们来看这个语句:我们来看这个语句:我们来看这个语句:DDRE |= (1 << PE5);个地址,这个我们暂且不去深究。
我们还是回过头来看DDRE= DDRE | (1 << PE5);这句话实现的功能,这句话实际上是将寄存器DDRE 中的内容(数据)跟二进制数0b00100000进行或操作,我们知道两个数的或操作的结果是:只要有一个是1,结果就是1.1.那么假如那么假如DDRE 中本来的值是0b10001010(0x8a),0b10001010(0x8a),它和它和0b00100000进行“或”操作以后的结果变成了0b101010100b10101010((0xaa 0xaa)。
我们可以看出,)。
我们可以看出,相或以后DDRE 中的第5位以外的各位的值都没有改变,而第5位变成了1,我们的目的就是要将第5位设为输出口(即将第5位设置为1)。
)。
现在我们来看一下从语言中有几种位运算符:现在我们来看一下从语言中有几种位运算符:移位运算符:左移移位运算符:左移<<<<<<,右移,右移,右移>> >>与运算符:与运算符:& &或运算符:或运算符:| |取反运算符:取反运算符:~ ~异或运算符:异或运算符:^ ^就这些了,总共只有就这些了,总共只有6中位运算符。
现在我们来看一下这些运算符都起什么作用;么作用;左移运算符:表达形式为x<<n,x<<n,意思是将数据意思是将数据x 向左移动n 位,在这里x 和n 都必须是无符号整形数据(所有的位运算符的操作对象都是无符号整形); 例如,例如,例如,x x 是一个unsigned char 类型的数据(即x 是一个单字节数据,共有8位),设x 的初值为0x000000010x00000001,执行,执行x<<n 的操作:的操作:n=0时,时,x<<n x<<n 表示x 左移0位,实际就是不移动,位,实际就是不移动,x x 的值不改变的值不改变n=1时,时,x<<n x<<n 表示x 左移1位,运算结果为0b00000010n=2时,时,x<<n x<<n 表示x 左移2位,运算结果为0b00000100...n=7时,时,x<<n x<<n 表示x 左移7位,运算结果为0b10000000n=8时,时,x<<n x<<n 表示x 左移8位,运算结果为0b00000000从结果来看,从结果来看,当n 在1-7之间取值时,运算的结果总是1依次向左移动一位,但是当n>=8以后,以后,x x 的值就一直是0了,这是因为x 是一个只有8位的整形变量,形变量,它的最大二进制长度是它的最大二进制长度是8位,位,当当n 超过8以后,以后,移位操作的结果已经超移位操作的结果已经超出8位的范围了,产生了溢出现象。
如何用C语言操纵AVR的IO端口(以ICCAVR为例):举例一:将PB0定义为输出,且输出为高电平DDRB=BIT(0);//定义PB0为输出PORTB|=BIT(0);// PB0 输出高电平举例二:将PB0、PB1定义为输出,且PB0输出低电平,PB1均为高电平DDRB|=BIT(0)|BIT(1);//定义PB0、PB1为输出PORTB|=BIT(0)|BIT(1);// PB0、PB1 输出高电平举例三:将PB0数据寄存器的数值翻转,即如果是1时变成0,如果是0时变成1 PORTB^=BIT(0);//PB0 输出高电平与0异或,不变,与1异或,原来0的为1,1的为0举例四:将PB0、PB1数据寄存器的数值翻转,即如果是1时变成0,如果是0时变成1 PORTB^=BIT(0)|BIT(1);// PB0 输出高电平这个很好,可惜我之前没看到举例五:将PB2、PB3定义为输入,不带上拉电阻DDRB&=~(BIT(2)|BIT(3));//定义PB2、PB3为输入PORTB&=~(BIT(2)|BIT(3));// 将PORT 置0,没有上拉电阻举例六:将PB2、PB3定义为输入,带上拉电阻。
即没有引用这些引脚时,缺省值为高电平SFIOR&=~BIT(PUD);//SFIOR寄存器的上拉电阻控制位PUD置0,在整个代码中,这句话可以不出现,或仅出现一次即可。
因为它是一个控制全部上拉电阻的控制位。
DDRB&=~(BIT(2)|BIT(3));//定义PB2、PB3为输入PORTB|=BIT(2)|BIT(3);// 将PORT 置1,满足上拉电阻的另一个条件举例七:DDRB=BIT(0)|BIT(1) 与DDRB|=BIT(0)|BIT(1) 的区别假定在执行上面两句指令前,DDRB 的状态为:1000 0000如果执行DDRB=BIT(0)|BIT(1) ,DDRB的状态变为:0000 0011如果执行DDRD|=BIT(0)|BIT(1),,DDRB的状态变为:1000 0011那前一句会先清空以前的所有状态,后一句保留前面的状态。
8位微控制器应用手册Rev. 1497B–AVR–05/02AVR035:如何用AVR C高效编程特点:• 访问I/O内存定位• 访问内存映射I/O• 访问闪存中数据• 访问EEPROM中数据• 创建EEPROM中的数据文件• 变量和数据类型的有效使用• 位域和位掩码的使用• 宏和函数的使用• 减少代码尺寸的18种方法• 减少内存需求的五种方法• 程序调试清单绪论C高级语言(HLL) 在微控制器编程中已经变得越来越普遍了。
使用C语言较之汇编语言的优点是多方面的:更少的开发时间,更好的可维护性,可移植性和更好的代码复用性。
随之而来的缺点是更大的代码量和经常产生降低执行速度的结果。
为了减少这些不利,AVR体系结构优化了由C编译器生成的标准指令的有效解码和执行。
在AVR体系结构和指令集规范完成以前,C编译器的开发是由IAR系统(公司)来做的。
编译器开发团队和AVR开发团队之间的合作结果是产生了一款专为控制器生成高效率、高性能代码的控制器。
本应用手册描述如何利用AVR体系结构的优点和开发工具来生成比任何其他微控制器更高效的C代码。
为C代码优化的体系结构32个工作寄存器是进行有效的C编程的关键之一。
除了有32个这样的寄存器外,这些寄存器作为传统的累加器具有相同的功能。
在一个时钟周期内,AVR能够提供两个任意的寄存器从寄存器文件到ALU,执行一个操作,并将结果写回到寄存器文件。
由于数据被存储于32个工作寄存器中,因此不必在执行每条算术指令时将数据在内存和寄存器之间来回移动。
某些寄存器可被组合成16位指针以便在数据和程序内存中有效地存取数据。
对于大内存尺寸,可由三个8位寄存器组成一个24位的内存指针来存取16M字节的无需分页(寻址)的数据。
寻址模式AVR体系结构有四个内存指针用来存取数据和程序内存。
栈指针(SP)用于存储函数返回(return)后的返回地址。
C编译器分配一个指针作为参数堆栈。
两个保留指针作为通用目的指针被C编译器用来加载和储存数据。
ICCAVR位操作的宏定义:C语言位操作的功能是其比较出色的地方,现在许多微处理器都支持C编译器也可看出。
汇编语言的位操作也很直观,但在某些C编译器对位操作却不能象汇编中对单个的位进行直接处理,而要转化为对一个字节的操作,在ICCAVR中这种限制充分体现出来。
如要对PORTA 的PORTA0置1,则应写成PORTA|=0X01;这样的位处在字节的两端还比较直观,但若是要置PORTA的第3位,第6位置1,则写出的程序可读性不强了。
于是便有了一些宏操作定义#define BIT(x) (1 << (x))(需要包含头文件macros.h)才可直接使用。
下面给出两种置1和清0的宏定义:置1:1,#define set(x) (1<<(x))2,#define setb(temp,x) temp|=(1<<(x))清0:1,#define clr(x) (~(1<<(x)))2,#define clrb(temp,x) temp&=~(1<<(x))比较上述置1的两种宏定义,第1种在写1时需写成 PORTA|=set(x); x表要处理的位,第2种更象函数,使用起来也比较直观:setb(PORTA,x),可读性比较强 但是第2种却并非是万能的,以MEGA8515为例,USART的UBRRH和UCSRC共用一个I/O location,在写UBRRH 的时候最高位必须为0,写UCSRC时则正好相反。
上述第2种的置1和清0操作只能对1 个BIT进行操作,所以是不能处理UBRRH和UCSRC的写入工作的,而第1种置1和清0操作却可以通过或/与进行多个位操作的,比如写入UCSRC 0x01可以写作:UCSRC|=set(7)|set(0);而如果用第2种宏定义则让它太为难了。
两种方法有其利弊,使用的时候靠自己衡量了。
用C语言操纵AVR的IO端口(以ICCAVR为例):例一:将PB0定义为输出,且输出为高电平DDRB=BIT(0); // 定义 PB0为输出PORTB|=BIT(0); // PB0 输出高电平例二:将PB0、PB1定义为输出,且PB0输出低电平,PB1均为高电平DDRB|=BIT(0)|BIT(1); // 定义 PB0、PB1为输出PORTB|=BIT(0)|BIT(1); // PB0、PB1 输出高电平例三:将PB0数据寄存器的数值翻转,即如果是1时变成0,如果是0时变成1PORTB^=BIT(0); // PB0 输出高电平与0异或,不变;// 与1异或,原来0的为1,1的为0例四:将PB0、PB1数据寄存器的数值翻转,即如果是1时变成0,如果是0时变成1 PORTB^=BIT(0)|BIT(1); // PB0 输出高电平例五:将PB2、PB3定义为输入,不带上拉电阻DDRB&=~(BIT(2)|BIT(3)); // 定义 PB2、PB3为输入PORTB&=~(BIT(2)|BIT(3)); // 将 PORT 置0,没有上拉电阻例六:将PB2、PB3定义为输入,带上拉电阻。
A VR的I/O口详解及正确的熔丝配置--更改被锁死的单片机A VR的I/O口结构与使用详解其实采用真正双向IO结构的新型MCU很多,常用的有增强型51,PIC,A VR等A VR的IO是真正双向IO结构,由于大部分网友都是从标准51转过来的,受标准51的准双向IO和布尔操作概念影响,没能掌握A VR的IO操作,所以有必要撰文说明一下先简单的回顾一下标准51的准双向IO结构这种准双向IO结构的特点是1 输出结构类似OC门,输出低电平时,内部NMOS导通,驱动能力较强(800uA);输出高电平靠内部上拉电阻,驱动能力弱(60uA)。
2 永远有内部电阻上拉(P0口除外),高电平输出电流能力很弱,所以即使IO口长时间短路到地也不会损坏IO口(同理,IO口低电平输出能力较强,作低电平输出时不能长时间短路到VCC)3 作输入时,因为OC门有"线与"特性,必须把IO口设为高电平(所以按键多为共地接法)4 作输出时,输出低电平可以推动LED(也是很弱的),输出高电平通常需要外接缓冲电路(所以LED多为共阳接法)5 软件模拟OC结构的总线反而比较方便-----例如IIC总线* P0口比较特殊,做外部总线时,是推挽输出,做普通IO时没有内部上拉电阻,所以P0口做按键输入需要外接上拉电阻。
* OC门:三极管的叫集电极开路,场效应管的叫漏极开路,简称开漏输出。
具备"线与"能力,有0得0。
* 为什么设计成输出时高电平弱,低电平强----是考虑了当年流行的TTL器件输入特性A VR的真正双向IO结构就复杂多了,单是控制端口的寄存器也有4个PORTx.DDRx,PINx,SFIOR(PUD位),不过功能也强劲多了作为通用数字I/O 使用时,所有AVR I/O 端口都具有真正的读- 修改- 写功能。
这意味着用SBI 或CBI 指令改变某些管脚的方向( 或者是端口电平、禁止/ 使能上拉电阻) 时不会无意地改变其他管脚的方向( 或者是端口电平、禁止/ 使能上拉电阻)。
AVR C语言编程的小技巧-IO口置位给单片机IO口置位是编程用的比较多的操作,这是我在学习C语言编程中的一点小小心得,希望大家觉得有用!AVR单片机的IO口是标准的双向端口,首先要设置IO口的状态,即:输入还是输出DDRx寄存器就是AVR单片机的端口方向寄存器,通过设置DDRx可以设置x端口的状态。
DDRx端口方向寄存器相应位设置为1则对应的x端口相应位为输出状态,DDRx端口方向寄存器相应位设置为0则对应的x端口相应位为输入状态。
例如:DDRA=0xFF;//设置端口A所有口为输出状态,因为0xFF对应的二进制为11111111bDDRA=0x0F//设置端口A高4位为输入状态,低4位为输出状态,因为0x0F对应的二进制为00001111bPORTx寄存器是AVR单片机的输出寄存器,端口输出状态设定好后通过设置PORTx可以使端口x的相应位输入高电平或低电平来控制外部设备。
例如:PORTA=0xFF;//端口A所有口线输出高电平PORTA=0x0F;//端口A高4位输出低电平,低4位输出高电平小贴士:利用位逻辑运算符对特定的端口进行设定。
PORTA=1<<3;//端口A第4位置为高电平,其它为低电平,应为00000001左移3位后是00001000PORTA=1<<7;//同理,第8位置高电平有时候我们期望端口某一位设置成高电平,但是其它位的高低电平要保持不变,如何做呢?C语言是很强大的,有办法!如下:PORTA|=1<<3;//实现端口A第4位置为高电平,其它位的高低电平不受影响上面的语句是简化的写法,分解一下就是:PORTA=PORTA|(1<<3);//数字1左移3位后与端口A进行按位或,结果就是端口A 第4位置为高电平,其它位的高低电平不受影响那么大家就会问了,如何实现设置某一位为低电平,其它位的高低电平不变呢?建议大家思考1分钟再看下面的内容。
????????????????????PORTA&=~(1<<3);//解释一下,首先将1左移3位变成00001000b,然后再按位取反变成11110111b,然后再与端口A做按位与运算,这样就实现了设置端口A第4位为低电平,其它位的高低电平不变。
分解后的语句为:PORTA=PORTA&(~(1<<3));//结果是一样的将某端口相应位的高低电平翻转,即原来高电平变为低电平,低电平变为高电平,呵呵!好简单呦!PORTA=~PORTA;//将PORTA按位取反后再赋值给PORTA按位逻辑运算还有一个异或,这个也非常有意思,它能实现电平翻转,有兴趣大家看看书,算是给大家留个想头吧!到这里基本的IO口置位的操作就聊完了,希望大家积极跟贴进行讨论,讲一讲自己的体会和心得,一起学习学习!如何用C语言设置,清除和检查是否置位I/O寄存器?Question如何用C语言设置,清除和检查是否置位I/O寄存器?Answer在AVR Studio的Help菜单中选择the online AVR Tools User Guide然后打开AVR Assembler-->User Guide-->expression_rs,在这里你能获得很多汇编语言关于算术运算方面的应用。
下面是一些简单和常用的算术运算操作,这个例子是改变或检查AVR控制器PORTB端口上PB6和PB4的bit值。
设置PB6和PB4,同时不改变其他管脚状态PORTB|=(1<<PB6)|(1<<PB4);清除PB6和PB4,同时不改变其他管脚状态PORTB&=~((1<<PB6)|(1<<PB4));检查PB6是否被置1if(PORTB&=(1<<PB6)){//Do something,for instance set a value in a variable}检查PB6和PB4是否被置1if((PORTB&=((1<<PB6)|(1<<PB4)))==((1<<PB6)|(1<<PB4))){//Do something,for instance set a value in a variable}AVR单片机I/O口位操作,置位、清位、取反。
2008-06-0511:50下面是用的宏定义方式:#define LED_SET PORTD|=(1<<PD5)//位置高#define LED_CLR PORTD&=~(1<<PD5)//位置低#define LED_COM PORTD^=(1<<PD5)//位取反#define LED_R PIND&(1<<PD5)//位读取通过上面的定义就可以在程序中直接操作I/O了。
动手学AVR单片机八、流水灯C语言程序讲解已有776次阅读2009-05-2011:26标签:AVR单片机C语言流水程序动手学AVR单片机八、流水灯C语言程序讲解上一讲我们对流水灯实验的电路实现和程序进行了分析,由于我们使用的是C 语言编写AVR单片机程序,很多初学者对于怎样使用C语言来编写单片机程序有着很多迷惑,比如为什么PORTB=0Xff这句话就能实现将单片机的PB口输出高电平。
这一讲我们就来进行一下C语言编写AVR单片机程序的入门讲解。
我们还是先把上一讲的程序按行编号后进行分析讲解。
#include<avr/io.h>//io端口寄存器配置文件,必须包含1#include<util/delay.h>//GCC中的延时函数头文件2int main(void)//GCC中main文件必须为返回整形值的函数,没有参数3{PORTB=0Xff;//PORTB输出高电平,使LED熄灭4DDRB=0XFF;//配置端口PB全部为输出口5while(1)6{unsigned char Flow_LED,Delay500ms;//定义流水灯循环次数以及延时时间变量7for(Flow_LED=0;Flow_LED<=7;Flow_LED++)//流水灯从0-7总共循环8次8{PORTB=0xff&(~(1<<Flow_LED));//每次循环中点亮一个LED9for(Delay500ms=0;Delay500ms<20;Delay500ms++)//延时500ms10{_delay_ms(100);//delay.h中的延时1ms函数11 }}}}AVR单片机C语言编程例子讲解1、首先一个单片机的C语言程序框架如下://头文件包含(即预定义语句)#include<文件名>或者#include"文件名",<>和""的区别是程序在编译的时候搜索头文件的路径不同,<>包含起来的文件先搜索编译软件安装目录下是否有这个文件,而""包含起来的文件则是先从我们编写程序存放的目录开始搜索,搜不到则再去搜索安装目录下的文件。
习惯上我们在包含编译软件自带的头文件时使用<>,而包含我们自定义的头文件时使用"";//全局变量定义,定义单片机程序中需要使用的全局变量,如果没有,这一块可以省略;//函数声明声明主函数中需要调用的子函数主函数格式为int main(void),用大括号{}把主函数的内容包含起来{局部变量定义定义本函数中使用到的变量,没有可以省略初始化代码主要是对单片机上电后的端口,寄存器,以及变量进行初始化while(1)每个单片机C语言主程序必须包含一个死循环,用{}将循环体中需要执行的代码包括起来,确保单片机程序能够一直执行{程序代码需要循环执行的程序代码}函数定义对函数声明中声明的函数进行定义,即定义函数需要实现的功能,编写相应程序代码2、本程序的第一、第二两行是预定义语句,#include<文件名>的意思是包含(include的英文本意就是"包含")一个文件,通知编译器在编译程序的时候要将“我”这里包含的文件一起编译。
io.h是GCC中对AVR单片机的各种寄存器进行标识的一个文件,主要是用一些含义明显的标识符来定义各种寄存器。
delay.h是GCC编译器中定义的一些延时函数,有了这些延时函数我们就不必每次编写程序的时候都要自己辛苦的编写延时函数了,直接调用编译器定义好的延时函数即可。
3、PORTB=0Xff;DDRB=0XFF;这两句话是对寄存器进行赋值,其中PORTB和DDRB就是io.h头文件中定义好的寄存器标识符,我们直接使用就可以了(从这里可以看出预编译指令里包含的头文件的重要性),这里用到了标准C语言的两个知识:赋值表达式和赋值语句,赋值表达式与赋值语句的区别从程序里面来看就是赋值语句后面比赋值表达式多了一个“;”。
用C语言编写AVR 单片机程序的最主要内容其实就是使用赋值语句给各种寄存器赋值(给寄存器赋予不同的值将使单片机相应模块处于不同的工作状态),或者是读取各个寄存器的值(获得模块的工作状态等信息)。
4、unsigned char Flow_LED,Delay500ms;是变量的定义语句,由于是在函数内部,所以它们是局部变量,其实这个变量定义的位置有些不大规范,标准一点的做法是将这一语句放在程序的第四行之前。
一般情况下局部变量的定义尽量放在一个函数的开始部分。
局部变量定义的位置不同,它的作用域也就不同,关于变量的作用域请查看相关的C语言书籍。
这里不作深究。
变量是单片机程序中经常使用的。
AVR单片机C语言中的变量与标准C语言的大致相同,但是需要提醒的是,在单片机中定义的变量应该避免使用实型变量,如果实在需要实型变量,应使用整形变量加小数点的方法。
这样可以提高程序效率。
5、第8、9、10、11这几行包含了两个循环语句,单片机C语言中的循环语句的定义和使用与标准C语言中的循环语句的定义和使用相同。
以上我们分析完了流水灯C语言程序的总体结构和语句功能,在实际使用中不同的程序其结构一般不会脱离这个框架(函数声明和定义部分有的人喜欢不声明函数,直接在主函数之前定义子函数,但这种方法不直观,使程序的可读性变差,不提倡)。
新学AVR单片机C语言的时候会感到有些不知如何下手,其实这是很多人都会有的感觉,不必感到恐慌,我们可以先从最简单的程序入手,对照C语言教材,一行一行跟C语言对照,分清哪些是变量定义、预定义指令、赋值语句、选择语句、循环语句等,仔细分析几个程序后就会有豁然开朗的感觉了。