ATmega16通用IO口

Download Report

Transcript ATmega16通用IO口

第三章 AVR通用I/O编程
3.1
3.2
3.3
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(unsigned char 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(unsigned char t)
{unsigned char 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+字符列数,列数:015
指令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)
void delay_ms(unsigned char i)
{ if (n == 0)
{ unsigned char a, b;
return ;
for (a = 1; a < i; a++)
while (--n);
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);
delay_ms(5);
LCD_write_com(0x08);
LCD_write_com(0x01);
LCD_write_com(0x06);
delay_ms(5);
LCD_write_com(0x0C);
}
/*显示模式设置
0011 1000*/
/*显示关闭*
0000 1000/
/*显示清屏*
0000 0001/
/*显示光标移动设置* 0000 01100/
/*显示开及光标设置* 0000 1100 /
三、AVR通用I/O口
(4)、字符LCD
初始化及主程序:
#include <iom16v.h>
#include “lcd_driver.h"
void main(void)
{ unsigned char i;
unsigned char *p;
delay_ms(100);
LCD_init();
while (1)
{
i = 1;
p = “Welcom to G403”;
LCD_clear();
LCD_write_str(5,0,“Hello LCD..");
delay_ms(250);
while (*p)
{ LCD_write_char(i,1,*p);
i ++;
p ++;
delay_ms(250);
}
delay_ms(250);
}
}