1.主体代码(初始化 数码管 键盘 )
//@12.0Mhz
#include <STC15F2K60S2.H>
#define keyscan() keyscan_KBD()//函数替换
typedef unsigned char uchar;
typedef unsigned int uint;
//独立按键
sbit k7=P3^0;
sbit k6=P3^1;
sbit k5=P3^2;
sbit k4=P3^3;
//矩阵键盘
sbit C4=P3^4;
sbit C3=P3^5;
sbit C2=P4^2;
sbit C1=P4^4;
//数码管变量
code uchar smg_dm[]={
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //10- 关闭
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
};
uchar smg_num[8]={10,10,10,10,10,10,10,10};
uchar com=0;
//全局变量
bit key_flag=0;//键盘扫描标志位
uchar key_con=0;//键盘扫描计数
uchar key_s=0;//键值
uchar key_temp=0;//键值暂存
uchar key_press=0;//按下计数(用于消抖)
uchar led_s=0;//保存led的值(高电平有效)
uchar pos=0;
uchar led_con=0;
//函数
void hc138(uchar n){//选通573
if(n==0) P2=P2&0x1f|0x00;
if(n==4) P2=P2&0x1f|0x80;
if(n==5) P2=P2&0x1f|0xa0;
if(n==6) P2=P2&0x1f|0xc0;
if(n==7) P2=P2&0x1f|0xe0;
}
void init(){//初始化关闭外设
hc138(4);
P0=0xff;
hc138(5);
P0=0x00;
hc138(0);
}
void dsp(){//数码管显示
hc138(6);
P0=0x00;
hc138(0);
hc138(7);
P0=smg_dm[smg_num[com]];
hc138(0);
hc138(6);
P0=0x01<<com;
hc138(0);//这一步是必要的,可以更好的排除其他操作(如LED)对显示的干扰
com=(com+1)%8;//使com在0-7循环
}
uchar keyscan_BTN(){//独立按键扫描程序
k7=k6=k5=k4=1;
if(k7==0) return 7;
if(k6==0) return 6;
if(k5==0) return 5;
if(k4==0) return 4;
return 0;
}
uchar keyscan_KBD(){//矩阵键盘扫描程序
uchar i;
C1=C2=C3=C4=1;
for(i=0;i<=3;i++){
// P3=(P3&0xf0)|((~(0X01<<i))&0x0f);
P3=~(0x01<<i);
if(C1==0){
return 3-i+4;
}
if(C2==0){
return 3-i+8;
}
if(C3==0){
return 3-i+12;
}
if(C4==0){
return 3-i+16;
}
}
return 0;
}
void keyout(){//键值输出程序
if(key_flag){
key_flag=0;
key_temp=keyscan();
if(key_temp==key_s) return;
if(key_temp!=0){
key_press++;
}else{
key_s=0;
key_press=0;
return;
}
if(key_press==3){
key_press=0;
key_s=key_temp;
return;
}
}
}
void led(){//LED测试
P0=0xff;
hc138(4);
P0=~led_s;
hc138(0);
P0=0xff;
}
//================定时器================//
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x20; //设置定时初始值
TH0 = 0xD1; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Timer0ser() interrupt 1{//定时器0服务程序,用于数码管显示和其他各种程序定时
//(定时器1可用于串口,555频率检测等)注意:定时器2的使能位在IE2,不能位寻址
dsp();
led();
if(key_con++==10){//键盘扫描计时
key_con=0;
key_flag=1;
}
if(led_con++==200){//流水灯
led_con=0;
led_s=0x01<<pos;
pos=(pos+1)%8;
}
}
//=====================================//
void main(){
init();
Timer0Init();
EA=1;
ET0=1;
while(1){
keyout();
smg_num[6]=key_s/10;
smg_num[7]=key_s%10;
}
}
2. DS18B20温度传感器
头文件书写 onewire.h(注意最后空一行)
头文件引入和管脚定义写在.c文件里面
#ifndef _ONEWIRE_H
#define _ONEWIRE_H
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);
#endif
驱动文件修改
温度读取和数据处理
void start(){
init_ds18b20();
Write_DS18B20(0xcc);//跳过rom
Write_DS18B20(0x44);//开始转换
}
long read_temp(){
long temp;
unsigned char low,high;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);//读取温度
low=Read_DS18B20();//先读低位
high=Read_DS18B20();//再读高位
temp=(high<<8)|low;
temp=temp*625;//得到一个六位数 后四位为小数部分
return temp;
}
在主函数中的写法为:
//先在初始化的时候开始转换,然后每700ms读取一次温度,读取结束后再开始转换
//(为避免数据抽风,温度转换间隔需要至少700ms)
void main(){
init();
Timer0Init();
EA=1;
ET0=1;
start();
while(1){
keyout();
smg_num[6]=key_s/10;
smg_num[7]=key_s%10;
testapp();
}
}
void testapp(){//测试数据
if(test_flag){
test_flag=0;
test=read_temp();
start();
smg_num[0]=test/100000%10;
smg_num[1]=test/10000%10+11;
smg_num[2]=test/1000%10;
smg_num[3]=test/100%10;
smg_num[4]=test/100%10;
}
}
3.时钟DS1302
头文件书写 ds1302.h
#ifndef _DS1302_H
#define _DS1302_H
void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte ( unsigned char address );
#endif
原理
读写操作的地址如上表所示:
-
秒寄存器的 BIT7 定义为时间暂停位,当 BIT1 为 1 时,时钟振荡器停止工作,DS1302 进入低功耗模式,电源消耗小于 100 微安,当 BIT1 为 0 时,时钟振荡器启动,DS1302 正常工作。
-
小时寄存器的 BIT7 定义为 12 或 24 小时工作模式选择位,当 BIT7 为高时,为 12 小时工作模式,此时 BIT5 为 AM/PM 位,低电平标示 AM,高电平标示PM,在 24 小时模式下,BIT5 为第二个 10 小时位标示(20~23 时)。
-
写保护寄存器的 BIT7:WP 是写保护位,工作时,出 WP 外的其他位都置为0,对时钟/日历寄存器或 RAM 进行写操作之前,WP 必须为 0,当 WP 为高电平的时候,不能对任何时钟/日历寄存器或 RAM 进行写操作。
-
DS18B20的数据为16进制的BCD码,获取高位数字应该/16,获取低位数字%16.
时间读取
变量
uchar DS1302r_add[] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //DS1302读数据的地址
uchar DS1302w_add[] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //DS1302写数据的地址
uchar timer[] = {0x50,0x59,0x23,0x05,0x09,0x07,0x23}; //2023年,4月1日,23时59分50秒
初始化函数
void DS1302_Init(void)//ds1302初始化
{
uint n;
Write_Ds1302_Byte(0x8E,0x00);//允许写
for(n=0;n<7;n++)
{
Write_Ds1302_Byte(DS1302w_add[n],timer[n]);
}
Write_Ds1302_Byte(0x8E,0x80);//禁止写
}
读时间
void Read_DS1302_Time(void)//读日期
{
uint n;
for(n=0;n<7;n++)
{
timer[n] = Read_Ds1302_Byte ( DS1302r_add[n]);
}
}
数据显示
smg_num[0]=timer[0]/16;//秒十位
smg_num[1]=timer[0]%16;//秒个位
smg_num[2]=timer[1]/16;//。。。
smg_num[3]=timer[1]%16;
smg_num[4]=timer[2]/16;
smg_num[5]=timer[2]%16;
4.A/DC和EEPROM
eeprom(设备地址A0)
void eeprom_write(unsigned char add,dat){
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char eeprom_read(unsigned char add){
unsigned char dat;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
dat=I2CReceiveByte();
I2CSendAck(1);//1-非应答 0-应答
I2CStop();
return dat;
}
A/DC (设备地址90)
原理
ADC
以光敏电阻为例子,I2CSendByte(0x01); bit7为1的时候是模拟输出,bit7为0的时候是数字转换读取数据
unsigned char adc(){
unsigned char dat;
I2CStart();
I2CSendByte(0x90);//设备地址写
I2CWaitAck();
I2CSendByte(0x01);//写入开始转换的通道 AIN1 RB1光敏电阻
I2CWaitAck();
I2CStart();//重新开始IIC
I2CSendByte(0x91);//设备地址读
I2CWaitAck();
dat=I2CReceiveByte();
I2CSendAck(1);//非应答1
I2CStop();
return dat;
}
数据处理
rb1数据类型为uint,乘100再除以51是为了转换为三位的整数值代表电压,0-5v,后两位为小数部分
rb1=adc();
rb1=rb1*100/51;
smg_num[0]=rb1/100+11;
smg_num[1]=rb1/10%10;
smg_num[2]=rb1%10;
DAC
void dac(unsigned char dat){
I2CStart();
I2CSendByte(0x90);//设备地址
I2CWaitAck();
I2CSendByte(0x41);//开始DA转换,写入地址
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
数据处理
要转换的0-5v电压乘以51即可。
5.串口通信(时钟1为频率源)
void bytesend(uchar dat){//传输单个字符
SBUF=dat;
while(TI==0);
TI=0;
}
void stringsend(char *p){//传输字符串
while(*p!='\0'){//\0为字符结束标志
bytesend(*p);
p++;
}
}
void testapp(){//测试数据
if(test_flag){
test_flag=0;
stringsend("abcdefg\n");
stringsend(rec_str);
}
}
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器时钟1T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xC7; //设置定时初始值
TH1 = 0xFE; //设置定时初始值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
ES = 1; //开启串口中断
}
void UART() interrupt 4{
if(RI){
RI=0;
dat=SBUF;
if(dat!='\n'){
rec_str[a]=dat;
a++;
}else{
rec_str[a+1]='\n';
rec_str[a+2]='\0';
a=0;
}
}
}
6.555频率测量
需要用到定时器0的计数器模式,
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD |= 0x04; //设置定时器模式 0x04为 bit3 C/T位置1 变为计数器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x00; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1;
ET0=1;
}
在定时器1中断中一秒钟读取一次TL0和TH0的数据 得到的就是频率
if(fre_con++==1000){//pcf8591 1s刷新一次
TR0=0;
fre_con=0;
fre_temp=TH0<<8;
fre_temp+=TL0;
TH0=0x00;
TL0=0x00;
frequency=fre_temp;
TR0=1;
}
超声波(用CAP定时器)
定时器初始化(开始的时候就发送一个超声波)然后开始计时,负脉冲中断使能
void initpca(){//12M pca定时器
CCON = 0;//清除控制寄存器
CMOD = 0x01;//1mhz模式 打开溢出中断使能
CCAPM0 = 0x11;//负脉冲 使能ccp0
CR=0;
sendultrasonic();//发送一个超声波
}
超声波发送程序
发送八个方波,延时13us!!
并且开始计时
void sendultrasonic(){
EA=0;
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;Delay13us();
p10=1;Delay13us();p10=0;
EA=1;
CL=0;//清零计时器
CH=0;//清零计时器
CR=1;//开始计时
}
接收的中断程序(每100ms接收一次)
读取定时的值然后再发送超声波,若超量程则变成999
void testapp(){//测试数据
if(test_flag){
test_flag=0;
if(CF){
CF=0;
distance=999;//超量程
}else{
distance=CCAP0H;
distance=(distance<<8)|CCAP0L;
distance+=150;//加上发送时间的150个值
distance=(float)distance*0.017;
}
smg_num[0]=distance/100;
smg_num[1]=distance/10%10;
smg_num[2]=distance%10;
sendultrasonic();
}
}