520 likes | 781 Views
第三章 AVR 通用 I/O 编程. 3.1. 3.3. 3.2. AVR 通用 I/O 口结构. 编程应用. 通用 I/O 口寄存器. 三、 AVR 通用 I/O 口. 有 PORTA 、 PORTB 、 PORTC 、 PORTD (简称 PA 、 PB 、 PC 、 PD ) 4 组 8 位,共 32 路通用 I/O 接口,分别对应于芯片上 32 根 I/O 引脚。
E N D
第三章 AVR通用I/O编程 3.1 3.3 3.2 AVR通用I/O口结构 编程应用 通用I/O口寄存器
三、AVR通用I/O口 有PORTA、PORTB、PORTC、PORTD(简称PA、PB、PC、PD)4组8位,共32路通用I/O接口,分别对应于芯片上32根I/O引脚。 所有这些I/O口都是双(有的为3)功能复用的。其中第一功能均作为数字通用I/O接口使用,而复用功能则分别用于中断、时钟/计数器、USRAT、I2C和SPI串行通信、模拟比较、捕捉等应用。
三、AVR通用I/O口 1、I/O口结构 结构图
三、AVR通用I/O口 1、I/O口结构 DDRx:控制I/O口的输入输出方向,即控制I/O口的工作方式为输出方式还是输入方式。 当DDRx=1时,I/O口处于输出工作方式。此时数据寄存器PORTx中的数据通过一个推挽电路输出到外部引脚:
三、AVR通用I/O口 1、I/O口结构 ●当PORTx=1时,I/O引脚输出高电平,可提供输出20mA的电流; ●而当PORTx=0时,I/O引脚呈现低电平,同时可吸纳20mA电流。AVR的I/O在输出方式下提供了比较大的驱动能力,可以直接驱动LED等小功率外围器件。 PORTx:数据输出寄存器; Pin:数据读入寄存器
三、AVR通用I/O口 1、I/O口结构 当DDRx=0时,I/O处于输入工作方式。此时引脚寄存器PINx中的数据就是外部引脚的实际电平,通过读I/O指令可将物理引脚的真实数据读入MCU。此外,当I/O口定义为输入时(DDRx=0),通过PORTx的控制,可使用或不使用内部的上拉电阻。
三、AVR通用I/O口 1、I/O口结构 表中的PUD为寄存器SFIOR中的D2位,它的作用相当AVR全部I/O口内部上拉电阻的总开关。当PUD=1时,AVR所有I/O内部上拉电阻都不起作用(全局内部上拉无效);而PUD=0时,各个I/O口内部上拉电阻取决于PORTXn的设置。
三、AVR通用I/O口 1、I/O口结构 • Atmega通用I/O特点: • 双向可独立位控的I/O口 • ATmega16的PA、PB、PC、PD四个端口都是8位双向I/O口,每一位引脚都可以单独的进行定义,相互不受影响。如用户可以在定义PA口第0、2、3、4、5、6位用于输入的同时定义第1、7位用于输出,互不影响。 • Push-Pull大电流驱动 (最大40mA) • 每个I/O口输出方式均采用推挽式缓冲器输出,提供大电流的驱动,可以输出(吸入)20mA的电流,因而能直接驱动LED显示器。 • 可控制的引脚内部上拉电阻 • 当I/O口被用于输入状态,且内部上拉电阻被激活(有效)时,如果外部引脚被拉低,则构成电流源输出电流(uA量级) • DDRx可控的方向寄存器。
三、AVR通用I/O口 1、I/O口结构 I/O口使用注意事项: 1)使用AVR的I/O口,首先要正确设置其工作方式,确定其工作在输出方式还是输入方式。 2)当I/O工作在输入方式,要读取外部引脚上的电平时,应读取PINxn的值,而不是PORTxn的值。 3)当I/O工作在输入方式,要根据实际情况使用或不使用内部的上拉电阻。如能利用AVR内部I/O口的上拉电阻,可以节省外部的上拉电阻 4) 一旦将I/O口的工作方式由输出设置成输入方式后,必须等待一个时钟周期后才能正确的读到外部引脚PINxn的值。 5)每次读写I/O前都要先配置DDRx寄存器。
三、AVR通用I/O口 2、I/O口寄存器 每个I/O口3个寄存器,共用12个寄存器。
三、AVR通用I/O口 2、I/O口寄存器 下图是PA口寄存器—PORTA、DDRA、PINA各个位的具体定义,及复位初始值。其它3个口的寄存器的情况与PA口相同,只是地址不一样。
三、AVR通用I/O口 3、I/O口编程应用 一种标准的C程序可以采用以下的写法: #define BIT0 0 …… #define BIT7 7 PORTC = 1<<(BIT0) | 1<<(BIT3); /* PC口的第0位和第3位输出“1”,其它为“0”*/ 1<<(BIT0)表示逻辑1左移0位,结果为0b00000001; 1<<(BIT3) 表示逻辑1左移3位,结果为0b00001000。0b00000001在同0b00001000相或,结果为0b00001001。 以上的逻辑运算不产生具体的操作指令,由编译器在编译时运算完成,得到结果,最后只是产生将结果赋值到PORTC寄存器的操作指令。 这种表示方法有利于“不同CPU的移植”。
三、AVR通用I/O口 3、I/O口编程应用 这种表示方式,比直接赋值0b00001001更容易理解程序的作用。在后面的程序将广泛使用这种表达方法,如对USART串口编程中大量使用了这样的描述方式: #define RXB8 1 #define TXB8 0 #define UPE 2 #define OVR 3 #define FE 4 #define UDRE 5 #define RXC 7 #define FRAMING_ERROR (1<<FE) #define PARITY_ERROR (1<<UPE) #define DATA_OVERRUN (1<<OVR)
三、AVR通用I/O口 3、I/O口编程应用 #define DATA_REGISTER_EMPTY (1<<UDRE) #define RX_COMPLETE (1<<RXC) ……… char status; status = UCSRA; if (( status & ( FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN )) == 0 ) {……} // 接收数据无错误处理过程 else {……} //接收数据产生错误处理过程
三、AVR通用I/O口 3、I/O口编程应用 程序中的UCSRA为ATmega16的串行接口USART的状态寄存器,UPE是UCSRA的第2位,当UPE为1时表示接收到的数据产生了校验错误。 程序中采用了定义语句,定义 PARITY_ERROR为(1 << UPE), 实际就是0b00000100。 因此一旦USART的值为PARITY_ERROR时, 表示接受的数据产生了校验错误,使程序的阅读非常明了。
三、AVR通用I/O口 (1)、LED发光二极管驱动
三、AVR通用I/O口 (1)、LED发光二极管驱动 #include <iom16v.h>void delay(unsignedchar t);/*声明函数*/void main(void) { char position = 0; // positionΪ¿ØÖÆλµÄλÖà PORTA=0xFF; // PA¿ÚÊä³öÈ«1£¬LEDÈ«Ãð DDRA=0xFF; // PA¿Ú¹¤×÷ΪÊä³ö·½Ê½ while (1) { PORTA = ~(1<<position); //×óÒÆһλif (++position >= 8) position = 0; delay(100); } } /*定义函数*/void delay(unsignedchar t) {unsignedchar a,b;for(a=0;a<100;a++)for(b=0;b<t;b++); }
三、AVR通用I/O口 (2)、继电器控制
三、AVR通用I/O口 (3)、步进电机驱动
三、AVR通用I/O口 (3)、步进电机驱动 单极3相步进电机有三个磁激励相,分别用A、B、C表示,每相有一个磁激线圈。通过控制三个磁激线圈电流的通断的先后时间顺序和通断频率就可以改变步进电机的变旋转方向和控制转速,图6-9是单极3相步进电机的原理图。 单极3相步进电机有3相3拍和3相6拍两种驱动方式,图6-10给出它们的控制时序图。3相3拍就是A、B、C三相分别通电,正转为A-B-C-A-B-C,反转为A-C-B-A-C-B,每拍转动3°。3相6拍中有三拍是两相同时通电,正转为A-AB-B-BC-C-CA,反转为A-AC-C-CB-B-BA,每拍转动1.5°。
三、AVR通用I/O口 (3)、步进电机驱动
三、AVR通用I/O口 (3)、步进电机驱动 单极3相步进电机有三个磁激励相,分别用A、B、C表示,每相有一个磁激线圈。通过控制三个磁激线圈电流的通断的先后时间顺序和通断频率就可以改变步进电机的变旋转方向和控制转速,图6-9是单极3相步进电机的原理图。 单极3相步进电机有3相3拍和3相6拍两种驱动方式,图6-10给出它们的控制时序图。3相3拍就是A、B、C三相分别通电,正转为A-B-C-A-B-C,反转为A-C-B-A-C-B,每拍转动3°。3相6拍中有三拍是两相同时通电,正转为A-AB-B-BC-C-CA,反转为A-AC-C-CB-B-BA,每拍转动1.5°。
三、AVR通用I/O口 (3)、步进电机驱动 #include <mega16.h> #include <delay.h> flash char step_out[6]={0x04,0x06,0x02,0x03,0x01,0x05}; void main(void) { char i = 0; int delay = 500; PORTA=0x00; DDRA=0x07; while (1) { PORTA = step_out[i]; if (++i >= 6) i = 0; delay_ms(delay); }; }
三、AVR通用I/O口 (3)、数码管显示(并行)
三、AVR通用I/O口 (3)、数码管显示(并行) 假设PA.0-a…PA.7-h 要显示数字‘1’ 共阴极:PA输出? 共阳级:PA输出? 与此同时,公共端接法? 欲连续显示数字0-9该如何?
三、AVR通用I/O口 (3)、数码管显示(并行)
三、AVR通用I/O口 (3)、数码管显示(并行)
三、AVR通用I/O口 (3)、数码管显示(并行) #include <mega16.h> #include <delay.h> flash char led_7[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; flash char position[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf}; char time[3]; // 时、分、秒计数 char dis_buff[6]; // 显示缓冲区,存放要显示的6个字符的段码值 char time_counter; // 1秒计数器 ,全局变量 bit point_on; // 秒显示标志 ,全局变量
三、AVR通用I/O口 (3)、数码管显示(并行) void display(void) // 扫描显示函数,执行时间12ms { char i; for(i=0;i<=5;i++) { PORTA = led_7[dis_buff[i]]; if (point_on && ( i==2 || i==4 )) PORTA |= 0x80; // (1) PORTC = position[i]; delay_ms(2); // (2) PORTC = 0xff; // (3) } }
三、AVR通用I/O口 (3)、数码管显示(并行) void time_to_disbuffer(void) // 时间值送显示缓冲区函数 { char i,j=0; for (i=0;i<=2;i++) { dis_buff[j++] = time[i] % 10; dis_buff[j++] = time[i] / 10; } } void main(void) { DDRA=0xFF; //PA全部置为输出; PORTA=0x00; // PORTA初始化 DDRC=0x3F; //PC的低6位置为输出,其他为输入; PORTC=0x3F; // PORTC初始化
三、AVR通用I/O口 (3)、数码管显示(并行) time[2] = 23; time[1] = 58; time[0] = 55; // 时间初值 time_to_disbuffer(); while (1) { display(); // 显示扫描,执行时间12ms If (++time_counter >= 40) { time_counter = 0; // (4) point_on = ~point_on; // (5) if (++time[0] >= 60) { time[0] = 0; if (++time[1] >= 60) { time[1] = 0; if (++time[2] >= 24) time[2] = 0; } } time_to_disbuffer(); } delay_ms(13); // 延时13ms,可进行其它处理(6) }}
三、AVR通用I/O口 (3)、数码管显示(串行)
三、AVR通用I/O口 (3)、数码管显示(串行)
三、AVR通用I/O口 (3)、数码管显示(串行) #define HC164_data PORTA.0 #define HC164_clk PORTA.1 void HC164_send_byte(char byte) { char i; for (i=0;i<=7;i++) { HC164_data = byte & 1<< i; HC164_clk = 1; HC164_clk = 0; } } void display(void) { char temp,i; for(i=0;i<=5;i++) { temp = led_7[dis_buff[i]]; if (point_on && (i==2 || i==4)) HC164_send_byte(temp | 0x80); else HC164_send_byte(temp); PORTC = position[i]; delay_ms(2); PORTC = 0xff; } } 。。。。。。。。。
三、AVR通用I/O口 (4)、字符LCD
三、AVR通用I/O口 (4)、字符LCD
三、AVR通用I/O口 (4)、字符LCD
三、AVR通用I/O口 (4)、字符LCD 指令1:清显示,光标复位到地址00H位置。 指令2:光标复位,光标返回到地址00H。 指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移,S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效。
三、AVR通用I/O口 (4)、字符LCD 指令4:显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示, C:控制光标的开与关,高电平表示有光标,低电平表示无光标, B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。 指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标。R/L,高向左,低向右。 指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线, N:低电平时为单行显示,高电平时双行显示, F: 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符。(有些模块是 DL:高电平时为8位总线,低电平时为4位总线)
三、AVR通用I/O口 (4)、字符LCD 指令7:字符发生器RAM地址设置,地址:字符地址*8+字符列数,列数:0-15 指令8:置显示地址,第0行为:80H,——8FH,第1行为:C0H,——CFH,数:0-1。 指令9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。 指令10:写数据。 指令11:读数据。
三、AVR通用I/O口 (4)、字符LCD 编程:分成应用程序和驱动程序,两部分。 • 驱动程序: • 写命令 • 初始化,清零,设置光标,是否移位等操作; • 写数据 • 显示一个字符,显示一串字符串; • 应用程序: • 直接调用驱动程序的相关函数,完成对LCD的初始化和数据显示。
三、AVR通用I/O口 (4)、字符LCD 函数功能简介: 1)void LCD_init(void) 该函数对LCD进行初始化,将显示位置回到第0行的第0列的起始位置处。函数的参数应是LCD显示器的列数(一行能够显示的字符数)。使用LCD显示器时,必须先使用该函数对LCD显示器进行初始化。 2)void LCD_clear(void) 该函数清除LCD的显示,并将显示位置回到第0行的第0列的起始位置处。 3)void LCD_write_data(unsigned char data) 写数据到LCD。 4)LCD_write_char(unsigned char x,unsigned char y,unsigned char data) 在指定位置显示单个字符。 5)void LCD_write_str(unsigned char x,unsigned char y,unsigned char *s)该函数将在从当前的显示位置开始,显示连续字符串(str为SRAM中定义的字符串的指针)。
三、AVR通用I/O口 (4)、字符LCD 6)void LCD_write_com(unsigned char com) 写命令到LCD。
三、AVR通用I/O口 (4)、字符LCD Lcd_driver.h 程序内容: #include <iom16v.h> #define RS_CLR PORTD &= ~(1 << PD3)#define RS_SET PORTD |= (1 << PD3) #define RW_CLR PORTD &= ~(1 << PD4)#define RW_SET PORTD |= (1 << PD4) #define EN_CLR PORTD &= ~(1 << PD6)#define EN_SET PORTD |= (1 << PD6) #define DATA_PORT PORTB void delay_us(unsigned int n) { if (n == 0) return ; while (--n);} void delay_ms(unsigned char i) { unsigned char a, b; for (a = 1; a < i; a++) for (b = 1; b; b++) ; }
三、AVR通用I/O口 (4)、字符LCD /*写命令函数*/void LCD_write_com(unsigned char com) { RS_CLR; RW_CLR; EN_SET; DATA_PORT = com; delay_us(5); EN_CLR;}
三、AVR通用I/O口 (4)、字符LCD /*显示数据*/void LCD_write_data(unsigned char data) { RS_SET; RW_CLR; EN_SET; PORTB = data; delay_us(5); EN_CLR;}
三、AVR通用I/O口 (4)、字符LCD /*显示字符串*/void LCD_write_str(unsigned char x,unsigned char y,unsigned char *s) { if (y == 0) LCD_write_com(0x80 + x); else LCD_write_com(0xc0 + x); while (*s) {LCD_write_data( *s); s ++; } }
三、AVR通用I/O口 (4)、字符LCD /*显示屏单字符写入函数*/void LCD_write_char(unsigned char x,unsigned char y,unsigned char data) { if (y == 0) LCD_write_com(0x80 + x); else LCD_write_com(0xC0 + x); LCD_write_data( data); }
三、AVR通用I/O口 (4)、字符LCD /*显示屏清空显示*/void LCD_clear(void) { LCD_write_com(0x01); delay_ms(5);}
三、AVR通用I/O口 (4)、字符LCD /*显示屏初始化函数*/void LCD_init(void) { LCD_write_com(0x3c); /*显示模式设置 0011 1000*/ delay_ms(5); LCD_write_com(0x08); /*显示关闭*0000 1000/ LCD_write_com(0x01); /*显示清屏*0000 0001/ LCD_write_com(0x06); /*显示光标移动设置*0000 01100/ delay_ms(5); LCD_write_com(0x0C); /*显示开及光标设置*0000 1100 /}