目录
一.I/O口介绍
1.I/O口配置
·P1/P2/P3/P4上电复位后为准双向口/弱上拉模式
·P0则为开漏输出模式
·P0作为总线扩展用时不用加上拉电阻,作为I/O口使用时需加10K-4.7K上拉电阻
·P0口的灌电流最大为12mA,其他I/O口的灌电流最大为6mA
准双向口输出:
1.当口线输出为1时驱动能力很弱,允许外部装置将其拉低。当引脚输出为低时,它的驱动能力很强,可吸收相当大的电流。
2.在三个上拉晶体管中分别有“强上拉”、“极弱上拉”、“弱上拉”。
3.此单片机为3V器件,若用户在引脚上加5V电压,将有电流从引脚流向Vcc,导致额外的功率消耗。若要使用,应加限流电阻,或用二极管做输入隔离,或用三极管做输出隔离。
4.准双向口读外部状态前,要先锁存为‘1’,才可读到外部正确的状态。
5.输出图如下:
注:门电路标识符
开漏输出:
开漏输出就是不输出电压,控制输出低电平时引脚接地,控制输出高电平时引脚既不输出高电平,也不输出低电平,为高阻态。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机电压低的时候。
输出图如下:
2.I/O口控制LED数码管
1.I/O口动态扫描驱动数码管时,可以一次点亮一个数码管中的8段,但为降低功耗,建议一次只点亮其中的4段或者2段。
2.下图是共阴极的数码管参考图:
3.下图是共阳极数码管参考图:(注:加了PNP的三极管)
我们的单片机是共阴极数码管。
数码管原理图与74HC138译码器:
74HC138译码器逻辑图:
数码管代码:
//在这里,最右边是第一位,如0X3F是0011 1111在位选中的排列是dp g f e d c b a的格式
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,
0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char Location,Number)
{
switch (Location)
{
//在74HC138译码器中P22,P23,P24操控LED的段选,并且给高电平为选中
case 1 : P2_4=1;P2_3=1;P2_2=1;break;
case 2 : P2_4=1;P2_3=1;P2_2=0;break;
case 3 : P2_4=1;P2_3=0;P2_2=1;break;
case 4 : P2_4=1;P2_3=0;P2_2=0;break;
case 5 : P2_4=0;P2_3=1;P2_2=1;break;
case 6 : P2_4=0;P2_3=1;P2_2=0;break;
case 7 : P2_4=0;P2_3=0;P2_2=1;break;
case 8 : P2_4=0;P2_3=0;P2_2=0;break;
}
P0=NixieTable[Number];
//以下两行代码为了消影
Delay(1);
P0=0x00;
}
至于为什么是高电平选中,看不懂逻辑图,看不懂芯片手册,我也不知道。
二.中断系统
1.介绍
中断系统的目的是为了使CPU具有对外界紧急事件的实时处理能力。当外界发出紧急事件的请求,可要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完成后,再回到原来被中断的地方,继续原来的工作。
1.请示CPU中断的请求源称中断源。一般存在多个中断源,每个中断源有优先级别。因此可以实现中断嵌套
2.此单片机有8个中断,如下图,Int是外部中断,Timer是定时器中断,UART是串口中断
2.结构
1.结构图:
2.定时器0和1的中断请求标志位是TF0和TF1。当定时器寄存器THx/TLx(x=0/1)溢出时,溢出标志位TFx(x=0/1)会被置位,定时器中断发生。当执行定时器中断时,TFx(x=0/1)会被硬件清除。
3.串行口接收中断请求的标志位RI和串行口1发送中断请求标志位TI中的任何一个被置为1后,串行口中断都会发生。
3.中断寄存器
单片机中断相关的所有寄存器:
只介绍Timer寄存器TCON和SCON (串行口控制寄存器)
1.TCON
2.SCON
三.定时器
1.寄存器介绍
不可位寻址:即此地址只可用十六进制一次性赋值,不可单位单位赋值如P2_1这样。
2.工作模式
模式0
原理图:
描述:TL0低五位溢出则向TH0进位,TH0溢出则置位TCON中的溢出标志TH0
具体看不太懂,就不描述了。
3.代码解释
void Timer0_Init(void) //1毫秒@11.0592MHz,
{
//配置寄存器TMOD
TMOD &= 0xF0; //设置定时器模式,保持高位不变
TMOD |= 0x01; //设置定时器模式,保持低位其他位不变
//设置初始值
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
//配置寄存器TCON
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
//中断部分
ET0=1; //允许中断
EA=1; //允许总中断
PT0=0; //中断优先级等于0,低优先级
}
四.串口通信
1.串口相关寄存器
1.全部相关寄存器
2.SCON寄存器
需要软件控制TI RI,在代码中有说明
3.PCON电源控制寄存器
4.SBUF寄存器(数据缓存寄存器)
主机必须在该帧结束前从SBUF缓冲器中读取数据,否则前一帧数据将丢失。SBUF以并行方式送往内部数据总线。
2.相关代码
串口通信代码:
void UartInit() //4800bps@11.0592MHz定时器1(8位自动重装)
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFA; //设置定时初始值
TH1 = 0xFA; //设置定时重载值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
//以下代码只要在接收时才需要加
EA=1; //接口中断
ES=1; //使能中断
}
发送字节的代码
void UartSendByte (unsigned char x)
{
SBUF=x;//SBUF是缓冲区,用来存放将发送的数据
while(TI==0);//当TI置1时发送完成
TI=0;//需要软件置0
//若发送数据有问题,可以试着延时
}
串口接收代码
void URATRoutine() interrupt 4 //串口中断的函数
{
if(RI==1)
{
P2=SBUF//让单片机做出SBUF的相关反应P2可换成别的东西
RI=0;//RI需要靠软件回零
}
}
五.74HC595
1.介绍
74HC595是串行输入并行输出的移位寄存器,可用三根线输入串行数据,八根线输出并行数据,多片级联后,可输出十六位,二十四位,三十二位等,常用于IO口扩展。
2.原理
英文原理图看不懂就不解释了
看代码
void _74HC595_Init()
{
SCK=0;//初始时SCK是高电平,需要调为低电平
RCK=0;//初始时RCK是高电平,需要调为低电平
}
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //给寄存器各位上填数字1或0
SCK=1;//给高电平,来一个上升沿
SCK=0;//给低电平,为下一次上升沿做准备
}
RCK=1;//给RCK上升沿就能使寄存器中的数输出
RCK=0;
}
六.DS1302时钟芯片
1.工作电路
2.数据传输
一.命令字
如图所示,位7必须为1,位6逻辑为1则RAM为0则CK(时钟),位5至位1表示输入输出的指定寄存器地址,位0逻辑为1则是读逻辑为0则为写。与数据输入图中相反,所以每次都从最右边开始读。
二.CE
CE拉高则开始对移位寄存器进行读写控制逻辑,拉低则中断。
三.数据输入
输入写命令字后的八个SCLK周期的上升沿数据被输入
四.数据输出
输入读命令字后的八个SCLK周期的下降沿,数据被输出
3.寄存器地址/定义
4.代码解释
void DS1302_Init()
{
//把CE和SCLK置0实现初始化
DS1302_CE=0;
DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;//CE拉高,数据传输开始
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);//字节是从右往左读的,所以每一位需要左移
DS1302_SCLK=1;//每次拉高则数据输入
DS1302_SCLK=0;//需要软件拉低
}
for(i=0;i<8;i++)//每次输入完写命令字后开始输入数据
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;//关闭数据传输
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
//局部变量十六进制若不初始化,则不一定是0.全局变量则一定是0
unsigned char i,Data=0x00;
Command|=0x01;//在写地址的第一位肯定是0,改一下第一位则可以不用多加宏定义
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
//先给0再给1目的是避免多出一个SCLK周期
//在读命令字结束时,那个下降沿就会读出数据而不是上升沿
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
//后面八个SCLK周期与写命令字一样
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO) Data|=(0x01<<i);
}
DS1302_CE=0;
DS1302_IO=0;
return Data;
}
5.BCD码
DS1302芯片采用BCD码的形式,写入芯片时需要将数据转化成BCD码,读出时需要转化为十进制
void DS1302_SetTime()
{
DS1302_WriteByte(DS1302_WP,0X00);//关闭写保护,允许地址数据被修改
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATA,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0X80);//打开写保护,地址数据不允许被修改
}
void DS1302_ReadTime()
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATA);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
七.I2C总线&AT24C02
1.I2C时序及代码
void I2C_Start()
{
//保证在start之前SDA和SCL都是高电平
I2C_SDA=1;
I2C_SCL=1;
//SCL为高电平,SDA下降沿是起始条件
I2C_SDA=0;
I2C_SCL=0;
}
void I2C_Stop ()
{
//保证SDA是低电平
I2C_SDA=0;
I2C_SCL=1;
//SCL是高电平,SDA上升沿是停止条件
I2C_SDA=1;
}
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
//SCL低电平时可写入数据
I2C_SCL=0;
for(i=0;i<8;i++)
{
//把Byte给SDA
I2C_SDA=Byte&(0x80>>i);
//SCL低电平时数据允许被修改,高电平时数据稳定,每个周期读取一个字节
I2C_SCL=1;
I2C_SCL=0;
}
}
unsigned char I2C_ReceiveByte()
{
unsigned char i, Byte=0x00;
I2C_SDA=1;
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA) Byte|=(0x80>>i);
I2C_SCL=0;
}
return Byte;
}
void I2C_SendAck(bit AckBit)
{
I2C_SDA=AckBit;//在SCL低电平时发送是否应答
I2C_SCL=1;
I2C_SCL=0;
}
unsigned char I2C_ReceiveAck()
{
bit AckBit;
I2C_SDA=1;
I2C_SCL=1;//SCL高电平时读取应答,若SDA是高电平则无应答,若低电平则应答
AckBit=I2C_SDA;
I2C_SCL=0;
return AckBit;
}
应答图如下:
2.AT24C02时序及代码
void AT24C02_WriteByte (unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);//AT24C02_ADDRESS是0xA0
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);//给AT24C02_ADDRESS读地址
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
八.DS18B20
1.介绍
DS18B20数字温度计提供9位温度读书,数据经过单线接口从DS18B20送入或送出,因此仅需一条数据线即可完成读写,且由数据线提供电源。一个DS18B20有唯一的序列号,所以可以有多个18B20存在于同一条总线上。这允许在许多不同的地方放置温度灵敏器件。
范围:HVAC环境控制,建筑物、设备或机械内的温度检测,以及过程监视和控制中的温度检测。
特性:单总线。从-55℃至+125℃,增量为0.5℃。在一秒(典型值)内把温度变换为数字
2.使用方法
1.原理图
由P37引脚控制单总线协议。
2.单总线协议
1.初始化
主机将总线拉低至少480us,然后释放总线,等待15-60us后,存在的从机会拉低总线60-240us以响应主机,之后从机释放总线。
代码段:
unsigned char OneWire_Init()
{
unsigned char i,AckBit;
OneWire_IO=1;//拉高为1,做准备
OneWire_IO=0;//拉低
//延迟至少480微秒,接着主机释放此线,并进入接收方式
i = 230;while (--i);//500微秒
OneWire_IO=1;
//等待15~60微秒
i = 38;while (--i);
AckBit=OneWire_IO;
i = 218;while (--i);//延时500微秒,让程序走完
return AckBit;
}
2.写时间片
主机把总线拉低60-120us表示发送0,主机将总线拉低后1-15us把总线拉高表示发送1。(尽量贴近15us末尾)
代码段:
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_IO=0;
i = 4;while (--i);
OneWire_IO=Bit;
i = 25;while (--i);
OneWire_IO=1;
}
3.读时间片
主机把总线拉低1-15us,然后主机释放总线,并在此期间读取总线电平。若是低电平则读取为“0”,若为高电平则读取为“1”。注意:总时长至少为60us
代码段:
unsigned char OneWire_ReceiveBit()
{
unsigned char Bit,i;
OneWire_IO=0;
i=2;while(--i);//延迟五微秒,避免程序还未读出就跳过了
OneWire_IO=1;
i=2;while(--i);
Bit=OneWire_IO;
i = 30;while (--i);//让总时长超过60us
return Bit;
}
4.发送和接收一个字节
代码段:
//注意单总线是低位在前
//发送字节
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
//接收字节
unsigned char OneWire_ReceiveByte()
{
unsigned char i,Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()) Byte|=(0x01<<i);
}
return Byte;
}
3.DS18B20实例
命令集:
//ROM命令集
#define READ_ROM 0x33
#define MATCH_ROM 0x55
#define SKIP_ROM 0xCC
#define SEARCH_ROM 0xF0
#define ALARM_SEARCH_ 0xEC
//
//功能命令集
#define CONVERTT 0x44
#define RECALLE2 0xB8
#define READPAD 0xBE
#define COPYPAD 0x48
#define READP 0xB4
·通过单总线访问DS18B20的协议
1.初始化
2.ROM操作命令
3.存储器操作命令
4.处理/数据
传递温度值代码段:
void DS18B20_ConvertT()
{
OneWire_Init();
OneWire_SendByte(SKIP_ROM);//因为只有一个DS18B20所以直接跳过ROM操作,不需要再搜索ROM
OneWire_SendByte(CONVERTT);
}
温度存储格式
BIT0~3是存小数,BIT11~15是存符号(+或-)。
读取温度数据代码段:
float DS18B20_ReadT()
{
unsigned char Tlsb,Tmsb;
int Temp;
float T;
OneWire_Init();
OneWire_SendByte(SKIP_ROM);
OneWire_SendByte(READPAD);//读取数据
Tlsb=OneWire_ReceiveByte();//低八位
Tmsb=OneWire_ReceiveByte();//高八位
Temp=(Tmsb<<8)|Tlsb;
T=Temp/16.0;//传递的温度值最低位是从1/16开始的
//而这里是从1开始,所以需要除以16
return T;
}