目录
开发板图
- IAP15F2K61S2最小系统
- LCD模块
- 超声波传感器模块
- 红外传感器模块
- DS1302时钟模块
- AT24C02存储器模块
- 74HC138译码器
- 74HC02或非门
- 数码管模块
- DS18B20温度传感器模块
- LED模块
- 74HC573锁存芯片+ULN2003驱动芯片
- CH340串口转USB
- 三片74HC573锁存芯片
- 蜂鸣器
- 继电器
- LM386音频功率放大器
- NE555N计数器
- 独立按键
- 矩阵按键
- PCF8591数模转换器+LM324四路运算放大器
锁存器驱动程序
![]() | ![]() |
译码器 | 锁存器 |
#define SEL P2
void Open_Lat(int i)//选通某个锁存器
{
switch (i)
{
case 4:SEL=0X9F;break;//不可以用(P2^7=1;P2^6=0;P2^5=0;)这种形式
case 5:SEL=0XBF;break;//不能一个一个改变,会影响其他的模块
case 6:SEL=0XDF;break;
case 7:SEL=0XFF;break;
}
Delay1ms();
}
void Close_Lat()//关闭所有锁存器
{
SEL=0X1F;
Delay1ms();
}
可以看到,138译码器的输入端为P2^5,P2^6,P2^7三个引脚。这里我们用宏定义直接将P2定义为SEL,这是因为若使用位定义,必然存在各个位先后改变的问题:例如原本选中锁存器4(P2^7,6,5=100=4),要选中锁存器7(P2^7,6,5=111=7),先将P2^5置一,再将P2^6置一时,就会产生中间态101,这样就会干扰锁存器5
LED模块
![]() |
锁存器+LED模块 |
#define LED P0
void Light_One(int i)//点亮某个led
{
switch (i)//判断LED位选
{
case 0:LED=0XFE;break;
case 1:LED=0XFd;break;
case 2:LED=0XFb;break;
case 3:LED=0XF7;break;
case 4:LED=0Xef;break;
case 5:LED=0Xdf;break;
case 6:LED=0Xbf;break;
case 7:LED=0X7f;break;
}
Open_Lat(4);
Close_Lat();//锁存LED位选
}
void Light_FlowR()//向右流水灯
{
int i;
LED=0XFF;//清空LED灯
Open_Lat(4);
for(i=0;i<8;i++)
{
Light_One(i);
Delay100ms();
}
Close_Lat();
}
void Light_FlowL()//向左流水灯
{
int i;
LED=0XFF;//清空LED灯
Open_Lat(4);
for(i=7;i>-1;i--)
{
Light_One(i);
Delay100ms();
}
Close_Lat();
}
数码管模块
![]() |
锁存器+数码管模块 |
int word[]={
//0 1 2 3 4 5 6 7 8 9//
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
//A B C D E F NULL//
0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF
};//数码管显示的字符库
void Nix_One(int place,int num)//显示某个位置的数码管
{
LED=word[num];//选定需要显示的字符
Open_Lat(7);
Close_Lat();//锁存数码管段选
switch (place)//判断数码管位选
{
case 0:LED=0x01;break;
case 1:LED=0x02;break;
case 2:LED=0x04;break;
case 3:LED=0x08;break;
case 4:LED=0x10;break;
case 5:LED=0x20;break;
case 6:LED=0x40;break;
case 7:LED=0x80;break;
}
Open_Lat(6);
Close_Lat();//锁存数码管位选
}
独立/矩阵按键模块
![]() |
独立/矩阵按键模块 |
#define KEY P3
sbit WR=P4^2;
int Jdg_Sinkey()//判断独立按键
{
u8 value=0;
KEY=0XFF;
if(KEY!=0XFF)
{
Delay100us();
if(KEY!=0XFF)
{
switch (KEY)
{
case 0xfe:value=7;break;
case 0xfd:value=6;break;
case 0xfb:value=5;break;
case 0xf7:value=4;break;
}
}
}
return value;
}
int Jdg_Matkey()//转置法判断矩阵按键
{
u8 value=0;
KEY=0Xf0;WR=1;//将行置0,列置1
if(KEY!=0Xf0)
{
Delay100us();//消抖
if(KEY!=0Xf0)
{
switch (KEY)
{
case 0xe0:value=3;break;//判断列
case 0xd0:value=2;break;//判断列
}
}
}else if(WR!=1)
{
Delay100us();//消抖
if(WR!=1)
{
value=1;//判断列
}
}
KEY=0XCF;WR=0;//将列置0,行置1
if(KEY!=0Xcf)
{
Delay100us();//消抖
if(KEY!=0Xcf)
{
switch (KEY)
{
case 0xce:value=value;break;//判断行
case 0xcd:value=value+3;break;//判断行
case 0xcb:value=value+6;break;//判断行
case 0xc7:value=value+9;break;//判断行
}
}
}
return value;
}
DS1302时钟模块
![]() | |
DS1302时钟芯片 | |
![]() | ![]() |
读数据时序图 | 写数据时序图 |
#include "DS1302.h"
u8 WRITE_ADDR[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};//秒分时日月周年//写时间的控制字
u8 READ_ADDR[7]={0x81,0x83,0x85,0x87,0x89,0x8B,0x8d};//秒分时日月周年//读时间的控制字
u8 DS1302_TIME[7]={0x47,0x22,0x13,0x03,0x07,0x06,0x21};//秒分时日月周年
void DS1302_Write_Byte(u8 addr,u8 dat)//向DS1302写入一个字节数据//参数为控制字和数据
{
u8 i=0;
DS1302_RST=0;
_nop_();
DS1302_CLK=0;
_nop_();
DS1302_RST=1;
_nop_();
for(i=0;i<8;i++)
{
DS1302_IO=addr&0x01;
addr>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}//写入地址控制字
for(i=0;i<8;i++)
{
DS1302_IO=dat&0x01;
dat>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}//写入数据
DS1302_RST=0;
_nop_();
}
u8 DS1302_Read_Byte(u8 addr)//向DS1302读出一个字节数据//参数为地址控制字
{
u8 i=0,dat=0,abit=0;
DS1302_RST=0;
_nop_();
DS1302_CLK=0;
_nop_();
DS1302_RST=1;
_nop_();
for(i=0;i<8;i++)
{
DS1302_IO=addr&0x01;
addr>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}//写入地址控制字
for(i=0;i<8;i++)
{
abit=DS1302_IO;
dat=(abit<<7)|(dat>>1);
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}//读出数据
DS1302_RST=0;
_nop_();
return dat;
}
void DS1302_Init(void)//初始化写入时间
{
u8 i=0;
DS1302_Write_Byte(0x8e,0x00);//关闭写保护寄存器
for(i=0;i<8;i++)
{
DS1302_Write_Byte(WRITE_ADDR[i],DS1302_TIME[i]);//写入秒分时日月周年
}
DS1302_Write_Byte(0x8e,0x80);//打开写保护寄存器
}
void DS1302_Read_Time(void)//读时间
{
u8 i=0;
for(i=0;i<8;i++)
{
DS1302_TIME[i]=DS1302_Read_Byte(READ_ADDR[i]);//读出秒分时日月周年
}
}
DS18B20温度传感器模块
|
DS18B20温度传感器 |
![]() |
初始化时序图 |
![]() |
写/读时序图 |
void Delay_OneWire(unsigned int t) //延时函数
{
unsigned char i = 0;
while(t--){
for(i=0; i<12; i++);
}
}
//
void Write_DS18B20(unsigned char dat)//写一个字节数据
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)//读一个字节数据
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
bit init_ds18b20(void)//初始化函数
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
float ds18b20_read_temperture(void)//读数据
{
float temp=0;
u8 dath=0;
u8 datl=0;
u16 value=0;
init_ds18b20();//important
Write_DS18B20(0xcc);//跳过ROM
Write_DS18B20(0x44);//开始温度变换
Delay_OneWire(200);
init_ds18b20();//important
Write_DS18B20(0xcc);//跳过ROM
Write_DS18B20(0xbe);//读暂存存储器
datl=Read_DS18B20();//读出低字节
dath=Read_DS18B20();//读出高字节
//合并为16位数据
value=dath;
value<<=8;
value|=datl;
if((value&0xf800)==0xf800)//判断符号位,负温度
{
value=(~value)+1; //数据取反再加1
temp=value*(-0.0625);//乘以精度
}
else //正温度
{
temp=value*0.0625;
}
return temp;//返回温度
}
void Get_Temp()
{
int value=0;
u8 sign=0;
value=10*ds18b20_read_temperture();
if(value<0)
{
value=-value;
sign=12;
}else{
sign=11;
}
}
AT24C02存储器模块
| |
AT24C02 | |
![]() | |
I2C总线的应答 | |
![]() | ![]() |
写字节 | 读字节 |
AT24C02使用的是I2C协议进行通信,因此我们必须对I2C协议具有一定的了解。
另外,我们还需要注意AT24C02的通信格式:
写字节:
起始信号-写入器件地址(写)-等待应答-写入写入地址-等待应答-写入数据-等待应答-......
读字节:
起始信号-写入器件地址(写)-等待应答-写入读出地址-等待应答-起始信号-写入器件地址(读)-等待应答-读出数据-应答-......
void IIC_Start()//I2C起始信号
{
IIC_SCL=1;
IIC_SDA=1;
delay_10us(1);
IIC_SDA=0;
delay_10us(1);
IIC_SCL=0;
}
void IIC_Stop()//I2C停止信号
{
IIC_SCL=1;
IIC_SDA=0;
delay_10us(1);
IIC_SDA=1;
delay_10us(1);
}
void IIC_Ack()//I2C主机应答
{
IIC_SCL=0;
IIC_SDA=0;
delay_10us(1);
IIC_SCL=1;
delay_10us(1);
IIC_SCL=0;
}
void IIC_Nack()//I2C主机非应答
{
IIC_SCL=0;
IIC_SDA=1;
delay_10us(1);
IIC_SCL=1;
delay_10us(1);
IIC_SCL=0;
}
u8 IIC_Wait_Ack()//I2C等待应答
{
u8 time=0;
IIC_SCL=1;
delay_10us(1);
while(IIC_SDA)
{
time++;
if(time>100)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
void IIC_Write_Byte(u8 dat)//I2C写入一个字节数据
{
u8 i=0;
IIC_SCL=0;
for(i=0;i<8;i++)
{
if((dat&0x80)>0)
{
IIC_SDA=1;
}else{
IIC_SDA=0;
}
dat<<=1;
IIC_SCL=1;
delay_10us(1);
IIC_SCL=0;
delay_10us(1);
}
}
u8 IIC_Read_Byte(u8 ack)//I2C读出一个字节数据(参数决定是否继续)
{
u8 i =0;
u8 receive=0;
for(i=0;i<8;i++)
{
IIC_SCL=0;
delay_10us(1);
IIC_SCL=1;
delay_10us(1);
receive<<=1;
if(IIC_SDA)
{
receive++;
}
}
if(!ack)
{
IIC_Nack();
}else{
IIC_Ack();
}
return receive;
}
void AT24C02_Write_One_Byte(u8 addr,u8 dat)//向AT24C02中地址为addr处写入一个字节数据
{
IIC_Start();
IIC_Write_Byte(0xa0);
IIC_Wait_Ack();
IIC_Write_Byte(addr);
IIC_Wait_Ack();
IIC_Write_Byte(dat);
IIC_Wait_Ack();
IIC_Stop();
}
u8 AT24C02_Read_One_Byte(u8 addr)//从AT24C02中地址为addr处读出一个字节数据
{
u8 i=0;
IIC_Start();
IIC_Write_Byte(0xa0);
IIC_Wait_Ack();
IIC_Write_Byte(addr);
IIC_Wait_Ack();
IIC_Start();
IIC_Write_Byte(0xa1);
IIC_Wait_Ack();
i=IIC_Read_Byte(0);
IIC_Stop();
return i;
}
PCF8591数模转换器模块
| |
PCF8591 | |
![]() | |
地址字节 | |
![]() | |
控制字字节 |
u8 ADC_Read(u8 addr)//ADC读出数字信号
{
u8 dat=0;
IIC_Start();
IIC_Write_Byte(0x90);//写入芯片地址(写)
IIC_Wait_Ack();
IIC_Write_Byte(addr);//写入通道控制字开始转换
IIC_Wait_Ack();
IIC_Start();
IIC_Write_Byte(0x91);//写入芯片地址(读)
IIC_Wait_Ack();
dat=IIC_Read_Byte(0);//读出一个字节数字信号数据
IIC_Stop();
return dat;
}
void DAC_Read(u8 dat)//DAC输出模拟信号
{
IIC_Start();
IIC_Write_Byte(0x90);//写入芯片地址(写)
IIC_Wait_Ack();
IIC_Write_Byte(0x43);//使能通道3的模拟输出
IIC_Wait_Ack();
IIC_Write_Byte(dat);//写入需要转换的数字信号
IIC_Wait_Ack();
IIC_Stop();
}
各模块的驱动程序及综合例程笔者将会持续更新