本笔记用于记录STC89C51单片机开发板入门学习,此部分为基础实验例程。开始实验前建议学习《10天学会51单片机-郭天祥》,另外还需要一定的C语言基础。
准备环境:
本条所要实现的功能是:点亮D1发光二极管,即让P2.0管脚输出一个低电平。
/*********************
点亮第一个LED灯实验
实验现象:下载程序后D1指示灯点亮
日期:2020-04-11
作者:lixs
**********************/
#include
sbit led=P2^0;//将单片机P2.0端口定义为led
void main()
{
led=0;//p2.0端口设置为低电平
while(1);//循环语句
}
编译前需要在KeiluVision5软件进行设置:1)options选项;2)output选项;3)勾选create HEX file;4)命名Name of Executable。如下图所示:
设置完成后进行编译。如下图所示:
编译完成后会生成.hex文件。如下图所示:
至此编译工作完成。
编译生成.hex文件后,打开STC-ISP软件,将此.hex文件下载到单片机中。
STC-ISP软件设置。如下图所示:
软件设置完成后,开始下载程序:1)点击打开程序文件;2)选择.hex文件;3)点击下载/编程。如下图所示:
点击下载程序后会出现:正在检测目标单片机…,此时检查单片机是否上电。如已上电,请关闭电源,重新上电,即可完成程序下载。如下图所示:
单片机正常连接后,会完成程序下载,STC-ISP软件会提示程序下载结果。如下图所示:
无
本条所要实现的功能是:让D1发光二极管固定周期闪烁,即让P2.0管脚先输出一个低电平点亮D1,然后固定周期后输出一个高电平熄灭D1。这就需要增加一个延时,我们利用c语言中的循环函数,让CPU不断循环执行即可实现延时功能。
/*********************
LED灯闪烁实验
实验现象:下载程序后D1指示灯闪烁
日期:2020-04-11
作者:lixs
**********************/
#include
typedef unsigned int uint16;
sbit led=P2^0;//将单片机P2.0端口定义为led
void delay(uint16 x);
void main()
{
while(1)
{
led=0;//p2.1端口设置为低电平,即点亮D1
delay(50000);//循环50000次,即大约延时500ms
led=1;//p2.1端口设置为高电平,即熄灭D1
delay(50000);//循环50000次,即大约延时500ms
}
}
void delay(uint16 x)//延时函数,当x=1时,大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
问题1:为什么如果将延时函数中的形参x赋值超过65535即LED灯延时间隔即呈现非正常状态?
解决办法:因为x数据类型为unsigned int类型,最大值为65535,因此x会溢出,导致非正常状态出现,x的赋值一定要严格在数据类型的取值范围内,否则不会得到预期结果。
/*********************
LED流水灯实验
实验现象:下载程序后D1-D8指示灯循环逐个点亮
日期:2020-04-11
作者:lixs
**********************/
#include
#include //左右移函数头文件
#define led P2 //定义P2端口为led,即可用led代替P2端口
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
uchar8 i;//初始化变量i
void delay(uint16);//声明延时函数
//主函数:实现led灯流水效果(采用<<左移运算符实现)
/*void main()
{
led=~0x01;
delay(50000);
while(1)
{
for(i=0;i<8;i++)
{
led=~(0x01<
//主函数:实现led灯流水效果(采用左移函数实现)
void main()
{
led=~0x01;//0x01取反即为0xFE,也可直接写led=0xFE
delay(50000);//循环50000次,即大约延时500ms
while(1)
{
for(i=0;i<8;i++)//循环8次,即与D1-D8一一对应
{
led=_crol_(led,1);//左移函数,将led的值左移1位,然后将结果赋值到led中
delay(50000);//循环50000次,即大约延时500ms
}
}
}
//延时函数,x=1时,大约延时10us
void delay(uint16 x)
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
问题1: 为什么LED流水灯点亮的同时,数码管也会跟着流动点亮?
解决办法:经查看开发板原理图得知,此现象是由于硬件电路导致,非程序影响。
由于工作原因,无法保证定期更新实验例程,还请谅解。以下实验例程会不定期更新,欢迎随时光临指导。
/*********************
蜂鸣器发声实验
实验现象:下载程序后蜂鸣器模块发声
日期:2020-04-11
作者:lixs
**********************/
#include
sbit buzzer=P1^5;//将单片机P1.5端口定义为buzzer
typedef unsigned int uint16;//对数据类型进行声明定义
void delay(uint16 x);//声明延时函数
void main()
{
while(1)
{
//通过给P1.5端口赋值方式使蜂鸣器发生
/*buzzer=0;//将P1.5端口置0,即蜂鸣器端输入低电平
delay(1000);
buzzer=1;//将P1.5端口置1,即蜂鸣器端输入高电平
delay(1000);*/
//通过取反方式使蜂鸣器发生
buzzer=~buzzer;//buzzer刚开始是0x01,然后循环取反,即可达到0/1切换的效果
delay(100);//循环100次,即大约延时1ms
}
}
void delay(uint16 x)//延时函数,当x=1时,大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
动态数码管实验
实验现象:下载程序后动态数码管依次显示数字0-7
日期:2020-04-13
作者:lixs
**********************/
#include
sbit LSA=P2^2;//将单片机P2.2端口定义为LSA
sbit LSB=P2^3;//将单片机P2.3端口定义为LSB
sbit LSC=P2^4;//将单片机P2.4端口定义为LSC
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
uchar8 code duanxuan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段选显示0-F的值
void delay(uint16);//声明延时函数
void DigDisplay();//声明数码管显示函数
void main()
{
//发送位选
while(1)
{
DigDisplay();
}
}
void DigDisplay()//位选、段选函数,即选择点亮第几个数码管,并显示数字几
{
uchar8 i;
for(i=0;i<8;i++)
{
switch(i)
{
case(0):LSA=1;LSB=1;LSC=1;break;//显示第0位,即点亮LED8,左1
case(1):LSA=0;LSB=1;LSC=1;break;//显示第1位,即点亮LED7,左2
case(2):LSA=1;LSB=0;LSC=1;break;//显示第2位,即点亮LED6,左3
case(3):LSA=0;LSB=0;LSC=1;break;//显示第3位,即点亮LED5,左4
case(4):LSA=1;LSB=1;LSC=0;break;//显示第4位,即点亮LED4,左5
case(5):LSA=0;LSB=1;LSC=0;break;//显示第5位,即点亮LED3,左6
case(6):LSA=1;LSB=0;LSC=0;break;//显示第6位,即点亮LED2,左7
case(7):LSA=0;LSB=0;LSC=0;break;//显示第7位,即点亮LED1,左8
}
P0=duanxuan[i];//发送段选,即选择显示数字几
delay(100);//延时函数,大约延时1ms
P0=0x00;//消隐
}
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
独立按键实验
实验现象:下载程序后按下K1按键可以对D1灯状态取反
日期:2020-04-13
作者:lixs
**********************/
#include
sbit led=P2^0;//将单片机P2.0端口定义为led
sbit k1=P3^1;//将单片机P3.1端口定义为k1
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
void delay(uint16);//声明延时函数
void keypros();
void main()
{
led=1;
while(1)
{
keypros();
}
}
void keypros() //按键处理函数
{
if(k1==0) //检测按键是否按下
{
delay(1000);//延时10ms,消除抖动
if(k1==0)
{
led=~led;//led状态取反
}
while(!k1); /*!k1 = !k1==1 = k1==0 检测按键是否松开,
如k1=0则表示还没松开,保持在当前循环,
k1=1则表示松开,跳出当前循环等待下次状态变化*/
}
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
矩阵按键实验
实验现象:下载程序后数码管显示0,按下矩阵按键上的按键显示对应的数字
日期:2020-04-13
作者:lixs
**********************/
#include
#define GPIO_DIG P0//将单片机P0端口定义为GPIO_DIG,即可用GPIO_DIG代替P0
#define GPIO_KEY P1//将单片机P1端口定义为GPIO_KEY,即可用GPIO_DIG代替P1
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
uchar8 code duanxuan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段选显示0-F的值
uchar8 KeyValue;
void delay(uint16);//声明延时函数
void KeyDown();
void main()
{
while(1)
{
KeyDown();
GPIO_DIG=duanxuan[KeyValue];
}
}
void KeyDown()
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)
{
delay(1000);
if(GPIO_KEY != 0x0f)
{
GPIO_KEY=0x0f;
switch(GPIO_KEY)
{
case(0x07):KeyValue=0;break;
case(0x0b):KeyValue=1;break;
case(0x0d):KeyValue=2;break;
case(0x0e):KeyValue=3;break;
}
GPIO_KEY=0xf0;
switch(GPIO_KEY)
{
case(0x70):KeyValue=KeyValue;break;
case(0xb0):KeyValue=KeyValue+4;break;
case(0xd0):KeyValue=KeyValue+8;break;
case(0xe0):KeyValue=KeyValue+12;break;
}
}
}
while((a<50)&&(GPIO_KEY != 0xf0))
{
delay(100);
a++;
}
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
点阵LED实验
实验现象:下载程序后,
点阵LED灯从左往右依次点
亮,类似流水灯效果
日期:2020-04-14
作者:lixs
**********************/
#include
#include
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
uchar8 ledNum;
//**定义IO口**//
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
sbit LED=P0^7;
//**声明函数**//
void Hc595SendByte(uchar8 dat);//声明发送数据函数
void delay(uint16);//声明延时函数
void main()
{
LED = 0;
ledNum = 0x01;//0000 0001
while(1)
{
Hc595SendByte(ledNum);
ledNum = _crol_(ledNum,1);//0000 0010
delay(50000);
}
}
void Hc595SendByte(uchar8 dat)//向74H595发送一个字节数据
{
uchar8 a;
SRCLK = 1;
RCLK = 1;
for (a = 0; a < 8; a++)
{
SER = dat >> 7;//将dat中的最高位D7位赋给SER
dat <<= 1;//左移将dat中的最高位去掉,用D6位取代D7位
SRCLK = 0;//移位时钟
_nop_();
_nop_();
SRCLK = 1;//移位时钟
}
RCLK = 0;//储存寄存器的时钟
_nop_();//51中的延时指令,延时一个机器周期,大约1us
_nop_();//51中的延时指令,延时一个机器周期,大约1us
RCLK = 1;//储存寄存器的时钟,一个上升沿将数据存在存储寄存器内
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
点阵LED实验
实验现象:下载程序后,
点阵LED灯第一个点亮
日期:2020-04-14
作者:lixs
**********************/
#include
#include
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
uchar8 Num;
//**定义IO口**//
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
sbit LED=P0^7;
//**声明函数**//
void Hc595SendByte(uchar8 dat);//声明发送数据函数
void main()
{
LED = 0;
Num = 0x01;
while(1)
{
Hc595SendByte(Num);
}
}
void Hc595SendByte(uchar8 dat)//向74H595发送一个字节数据
{
uchar8 a;
SRCLK = 1;
RCLK = 1;
for (a = 0; a < 8; a++)
{
SER = dat >> 7;//将dat中的最高位D7位赋给SER
dat <<= 1;//左移将dat中的最高位去掉,用D6位取代D7位
SRCLK = 0;//移位时钟
_nop_();
_nop_();
SRCLK = 1;//移位时钟
}
RCLK = 0;//储存寄存器的时钟
_nop_();//51中的延时指令,延时一个机器周期,大约1us
_nop_();//51中的延时指令,延时一个机器周期,大约1us
RCLK = 1;//储存寄存器的时钟,一个上升沿将数据存在存储寄存器内
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
点阵LED(点亮数字)实验
实验现象:下载程序后,
点阵LED显示数字0
日期:2020-04-16
作者:lixs
**********************/
#include
#include
typedef unsigned int uint16;//对数据类型进行声明定义
typedef unsigned char uchar8;//对数据类型进行声明定义
#define LED P0
uchar8 ledduan[]= {0x00,0x00,0x3C,0x42,0x42,0x3C,0x00,0x00};//段选,即行,数字0
//uchar8 ledduan[]= {0x38,0x7C,0x7E,0x3F,0x3F,0x7E,0x7C,0x38};//段选,即行,心形
uchar8 ledwei[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//位选,即列
//**定义IO口**//
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
void Hc595SendByte(uchar8 dat);
void delay(uint16 x);
void main()
{
uchar8 i=0;
while(1)
{
for(i=0;i<8;i++)
{
LED=ledwei[i];
Hc595SendByte(ledduan[i]);
delay(10);
Hc595SendByte(0x00);//消隐
}
}
}
void Hc595SendByte(uchar8 dat)//向74H595发送一个字节数据
{
uchar8 a;
SRCLK = 1;
RCLK = 1;
for (a = 0; a < 8; a++)
{
SER = dat >> 7;//将dat中的最高位D7位赋给SER
dat <<= 1;//左移将dat中的最高位去掉,用D6位取代D7位
SRCLK = 0;//移位时钟
_nop_();
_nop_();
SRCLK = 1;//移位时钟
}
RCLK = 0;//储存寄存器的时钟
_nop_();//51中的延时指令,延时一个机器周期,大约1us
_nop_();//51中的延时指令,延时一个机器周期,大约1us
RCLK = 1;//储存寄存器的时钟,一个上升沿将数据存在存储寄存器内
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
独立开关控制直流电机实验
实验现象:下载程序后,按
下独立按键,电机旋转
日期:2020-04-16
作者:lixs
**********************/
#include
#include
typedef unsigned int uint16;
typedef unsigned char uchar8;
sbit k1=P3^1;//将单片机P3.1端口定义为k1
sbit moto=P1^0;
void delay(uint16 x);
void keypros();
void main()
{
moto=0;//关闭电机
while(1)
{
keypros();
}
}
void keypros() //按键处理函数
{
if(k1==0) //检测按键是否按下
{
delay(1000);//延时10ms,消除抖动
if(k1==0)
{
moto=~moto;//moto状态取反
}
while(!k1); /*!k1 = !k1==1 = k1==0 检测按键是否松开,
如k1=0则表示还没松开,保持在当前循环,
k1=1则表示松开,跳出当前循环等待下次状态变化*/
}
}
void delay(uint16 x)//延时函数,x=1时大约延时10us
{
while(x--);
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
外部中断0实验
实验现象:下载程序后,操
作K3按键使D1状态取反
日期:2020-04-17
作者:lixs
**********************/
#include
typedef unsigned int uint16;
typedef unsigned char uchar8;
sbit k3=P3^2;//定义K3按键
sbit LED=P2^0;//定义P2.0口是LED
void delay(uint16 x)//延时函数
{
while(x--);
}
void Int0Init()//外部中断0设置
{
//设置INT0
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开INT0的中断允许
EA=1;//打开总中断
}
void main()//主函数
{
Int0Init();//设置外部中断0
while(1);
}
void Int0() interrupt 0 //外部中断0函数
{
delay(1000);//延时消抖
if(k3==0)//按键触发
{
LED=~LED;
}
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
定时器0中断实验
实验现象:下载程序后,
D1灯循环点亮1秒,熄灭1秒
日期:2020-04-18
作者:lixs
**********************/
#include
typedef unsigned int uint16;
typedef unsigned char uchar8;
sbit LED=P2^0;//定义P2.0口是LED
sbit buzzer=P1^5;//将单片机P1.5端口定义为buzzer
void Timer0Init()
{
TMOD=0x01;//选择为定时器0模式,工作方式1,仅用TR0打开启动
ET0=1;
EA=1;
TR0=1;
}
void main()
{
Timer0Init();
while(1);
}
void Timer0() interrupt 1
{
static uint16 i;
TH0=0xFC;
TL0=0x18;
i++;
if(i==1000)
{
i=0;
LED=~LED;
}
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
#include
typedef unsigned int u16;
typedef unsigned char u8;
void UsartInit()
{
TMOD=0X20; //设置计数器工作方式2
TH1=0XFA; //初始赋值,波特率9600
TL1=0XFA; //初始赋值,波特率9600
PCON=0X80; //波特率加倍
TR1=1; //打开计数器
SCON=0X50; //串口工作方式
ES=1; //串口中断打开
EA=1; //总中断打开
}
void main()
{
UsartInit();
while(1);
}
void Usart() interrupt 4
{
u8 receiveData;
receiveData=SBUF; //出去接收到的数据
RI = 0; //清除接收中断标志位
SBUF=receiveData; //将接收到的数据放入到发送寄存器
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
/*********************
i2c驱动程序
实验现象:
日期:2020-04-18
作者:lixs
**********************/
#include "i2c.h"
//延时函数
void Delay10us()
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
//i2c启动函数
void I2cStart()
{
SDA=1;
Delay10us();
SCL=1;
Delay10us();
SDA=0;
Delay10us();
SCL=0;
Delay10us();
}
//i2c终止函数
void I2cStop()
{
SDA=0;
Delay10us();
SCL=1;
Delay10us();
SDA=1;
Delay10us();
}
//i2c发送数据函数
unsigned char I2cSendByte(unsigned char dat)
{
unsigned char a=0,b=0;
for(a=0;a<8;a++)
{
SDA=dat>>7;
dat=dat<<1;
Delay10us();
SCL=1;
Delay10us();
SCL=0;
Delay10us();
}
SDA=1;
Delay10us();
SCL=1;
while(SDA)
{
b++;
if(b>200)
{
SCL=0;
Delay10us();
return 0;
}
}
SCL=0;
Delay10us();
return 1;
}
//i2c读取数据函数
unsigned char I2cReadByte()
{
unsigned char a=0,dat=0;
SDA=1;
Delay10us();
for(a=0;a<8;a++)
{
SCL=1;
Delay10us();
dat<<=1;
dat|=SDA;
Delay10us();
SCL=0;
Delay10us();
}
return dat;
}
//往24c02的一个地址写入一个数据
void At24c02Write(unsigned char addr,unsigned char dat)
{
I2cStart();
I2cSendByte(0xa0);
I2cSendByte(addr);
I2cSendByte(dat);
I2cStop();
}
//读取24c02的一个地址的一个数据
unsigned char At24c02Read(unsigned char adrr)
{
unsigned char num;
I2cStart();
I2cSendByte(0xa0);
I2cSendByte(adrr);
I2cStart();
I2cSendByte(0xa1);
num=I2cReadByte();
I2cStop();
return num;
}
/*********************
i2c头文件
实验现象:
日期:2020-04-18
作者:lixs
**********************/
#ifndef _I2C_H_
#define _I2C_H_
#include
sbit SCL=P2^1;
sbit SDA=P2^0;
void I2cStart();
void I2cStop();
unsigned char I2cSendByte(unsigned char dat);
unsigned char I2cReadByte();
void At24c02Write(unsigned char addr,unsigned char dat);
unsigned char At24c02Read(unsigned char adrr);
#endif
/*********************
i2c实验
实验现象:下载程序后,
数码管后4位显示0,按下
k1保存显示的数据,按下k2
读取上次保存的数据,按下
k3显示数据加1,按下k4显示
数据清零。最大能写入的数据
是255
日期:2020-04-18
作者:lixs
**********************/
#include
#include "i2c.h"
typedef unsigned int uint16;
typedef unsigned char uchar8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;
sbit k4=P3^3;
char num=0;
uchar8 disp[4];
uchar8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(uint16 x)
{
while(x--);
}
//按键处理函数
void Keypros()
{
if(k1==0)
{
delay(1000);
if(k1==0)
{
At24c02Write(1,num);
}
while(!k1);
}
if(k2==0)
{
delay(1000);
if(k2==0)
{
num=At24c02Read(1);
}
while(!k2);
}
if(k3==0)
{
delay(1000);
if(k3==0)
{
num++;
if(num>255) num=0;
}
while(!k3);
}
if(k4==0)
{
delay(1000);
if(k4==0)
{
num=0;
}
while(!k4);
}
}
//数据处理函数
void datapros()
{
disp[0]=smgduan[num/1000];//千位
disp[1]=smgduan[num%1000/100];//百位
disp[2]=smgduan[num%1000%100/10];//十位
disp[3]=smgduan[num%1000%100%10];//个位
}
//数码管显示函数
void DigDisplay()
{
uchar8 i;
for(i=0;i<4;i++)
{
switch(i) //位选,选择点亮的数码管
{
case(0):LSA=1;LSB=1;LSC=0;break;//显示第0位
case(1):LSA=0;LSB=1;LSC=0;break;//显示第1位
case(2):LSA=1;LSB=0;LSC=0;break;//显示第2位
case(3):LSA=0;LSB=0;LSC=0;break;//显示第3位
}
P0=disp[i];//发送段选,即选择显示数字几
delay(100);//延时函数,大约延时1ms
P0=0x00;//消隐
}
}
void main()
{
while(1)
{
Keypros();
datapros();
DigDisplay();
}
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
初始化函数
#include "temp.h"
//延时函数
void Delay1ms(uint y)
{
uint x;
for(;y>0;y--)
{
for(x=110;x>0;x--);
}
}
//初始化函数
uchar Ds18b20Init()
{
uchar i;
DSPORT=0;//将总线拉低
i=70;
while(i--);//延时642us
DSPORT=1;//然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
i=0;
while(DSPORT)
{
Delay1ms(1);
i++;
if(i>5)//等待>5ms
{
return 0;//初始化失败
}
}
return 1;//初始化成功
}
//向18B20写入一个字节
void Ds18b20WriteByte(uchar dat)
{
uint i,j;
for(j=0;j<8;j++)
{
DSPORT=0;//每写入一位数据之前先把总线拉低1us
i++;
DSPORT=dat & 0x01;//然后写入一个数据,从最低位开始
i=6;
while(i--);//延时68us,持续时间最少60us
DSPORT=1;//然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
dat>>=1;
}
}
//读取一个字节
uchar Ds18b20ReadByte()
{
uchar byte,bi;
uint i,j;
for(j=8;j>0;j--)
{
DSPORT=0;//先将总线拉低1us
i++;
DSPORT=1;//然后释放总线
i++;
i++;//延时6us等待数据稳定
bi=DSPORT;//读取数据,从最低位开始读取
//将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0
byte=(byte>>1)|(bi<<7);
i=4;//读取完之后等待48us再接着读取下一个数
while(i--);
}
return byte;
}
//让18b20开始转换温度
void Ds18b20ChangTemp()
{
Ds18b20Init();
Delay1ms(1);
Ds18b20WriteByte(0xcc);//跳过ROM操作指令
Ds18b20WriteByte(0x44);//温度转换命令
//Delay1ms(100);//等待转换成功,而如果你是一只刷着的话,就不用这个延时了
}
//发送读取温度命令
void Ds18b20ReadTempCom()
{
Ds18b20Init();
Delay1ms(1);
Ds18b20WriteByte(0xcc);//跳过ROM操作指令
Ds18b20WriteByte(0xbe);//温度转换命令
}
//读取温度
int Ds18b20ReadTemp()
{
int temp=0;
uchar tmh,tml;
Ds18b20ChangTemp();//先写入转换命令
Ds18b20ReadTempCom();//然后等待转换完成后发送读取温度命令
tml=Ds18b20ReadByte();//读取温度值共16位,先读低字节
tmh=Ds18b20ReadByte();//再读高字节
temp=tmh;
temp<<=8;
temp|=tml;
return temp;
}
主函数
/*********************
DS18b02温度传感器实验
实验现象:下载程序后,数码管会显示检测到的温度值
日期:2020-04-20
作者:lixs
**********************/
#include
#include "temp.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
char num=0;
u8 DisplayData[8];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//延时函数
void delay(u16 i)
{
while(i--);
}
//温度读取处理转换函数
void datapros(int temp)
{
float tp;
if(temp<0)//当温度值为负
{
DisplayData[0]=0x40;
//因为读取的温度是实际温度的补码,所以减1,再取反求出源码
temp=temp-1;
temp=~temp;
tp=temp;
temp=tp*0.0625*100+0.5;//留两个小数点就是*100,+0.5就是四舍五入
}
else
{
DisplayData[0]=0x00;
tp=temp;
temp=tp*0.0625*100+0.5;
}
DisplayData[1]=smgduan[temp%10000/1000];
DisplayData[2]=smgduan[temp%1000/100];
DisplayData[3]=smgduan[temp%100/10];
DisplayData[4]=smgduan[temp%10/1];
}
//数码管显示函数
void DigDisplay()
{
u8 i;
for(i=0;i<6;i++)
{
switch(i)
{
case(0):LSA=1;LSB=1;LSC=1;break;//显示第0位
case(1):LSA=0;LSB=1;LSC=1;break;//显示第1位
case(2):LSA=1;LSB=0;LSC=1;break;//显示第2位
case(3):LSA=0;LSB=0;LSC=1;break;//显示第3位
case(4):LSA=1;LSB=1;LSC=0;break;//显示第4位
case(5):LSA=0;LSB=1;LSC=0;break;//显示第5位
}
P0=DisplayData[i];//发送数据
delay(100);
P0=0x00;
}
}
//主函数
void main()
{
while(1)
{
datapros(Ds18b20ReadTemp());//数据处理函数
DigDisplay();//数码管显示函数
}
}
头文件
#ifndef _TEMP_H_
#define _TEMP_H_
#include
//重定义关键字
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//定义使用的IO口
sbit DSPORT=P3^7;
//声明全局函数
void Delay1ms(uint y);
uchar Ds18b20Init();
void Ds18b20WriteByte(uchar dat);
uchar Ds18b20ReadByte();
void Ds18b20ChangTemp();
void Ds18b20ReadTempCom();
int Ds18b20ReadTemp();
#endif
编译过程同1.2一致。
程序下载过程同1.3一致。
无
初始化函数
#include "ds1302.h"
//DS1302写入和读取时分秒的地址命令
//秒分时日月周年最低位读写位
uchar code READ_RTC_ADDR[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};//读地址{秒,分,小时,日,月,星期,年}
uchar code WRITE_RTC_ADDR[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};//写地址{秒,分,小时,日,月,星期,年}
//DS1302时钟初始化2016年5月7日星期六12点00分00秒
//存储顺序是秒分时日月周年,存储格式是用BCD码
uchar TIME[7]={0,0,0x12,0x07,0x05,0x06,0x16};//{秒,分,时,日,月,星期,年}
//单片机向DS1302发送(地址+数据)
void Ds1302Write(uchar addr,uchar dat)
{
uchar n;
RST=0;
_nop_();
SCLK=0;//先将SCLK置于低电平
_nop_();
RST=1;//然后将RST(CE)置高电平
_nop_();
for(n=0;n<8;n++)//开始传送8位地址命令
{
DSIO=addr & 0x01;//数据从低位开始传送
addr>>=1;
SCLK=1;//数据在上升沿时,DS1302读取数据
_nop_();
SCLK=0;
_nop_();
}
for(n=0;n<8;n++)
{
DSIO=dat & 0x01;
dat>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;//传送数据结束
_nop_();
}
//读取一个地址的数据
uchar Ds1302Read(uchar addr)
{
uchar n,dat,dat1;
RST=0;
_nop_();
SCLK=0;
_nop_();
RST=1;
_nop_();
for(n=0;n<8;n++)//开始传送8位地址命令
{
DSIO=addr & 0x01;//数据从低位开始传送
addr>>=1;
SCLK=1;//数据在上升沿时,DS1302读取数据
_nop_();
SCLK=0;
_nop_();//数据在下降沿时,放置数据
}
_nop_();
for(n=0;n<8;n++)//读取8位数据
{
dat1=DSIO;//从最低位开始接收,DISO从低位开始依次发送8位数据*********
dat=(dat>>1)|(dat1<<7);//将DSIO的最低位左移7位至最高位,然后将dat右移1位,
//即最高位置0,即可将DSIO的最高位放置dat中,循环8次即可将addr数据放置dat中
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;
_nop_();//以下为DS1302复位的稳定时间
SCLK=1;
_nop_();
DSIO=0;
_nop_();
DSIO=1;
_nop_();
return dat;
}
//初始化DS1302
void Ds1302Init()
{
uchar n;
Ds1302Write(0x8e,0x00);//关闭写保护功能
for(n=0;n<7;n++)
{
Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);
}
Ds1302Write(0x8e,0x80);//打开写保护
}
//读取时钟信息
void Ds1302ReadTime()
{
uchar n;
for(n=0;n<7;n++)//读取7个字节的时钟信号:分秒时日月周年
{
TIME[n]=Ds1302Read(READ_RTC_ADDR[n]);
}
}
主函数
/*********************
DS1302时钟实验
实验现象:下载程序后,数码管会显示时钟
日期:2020-04-21
作者:lixs
**********************/
#include
#include "ds1302.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
char num=0;
u8 DisplayData[8];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//延时函数
void delay(u16 i)
{
while(i--);
}
//时间读取处理转换函数
void datapros()
{
Ds1302ReadTime();
DisplayData[0]=smgduan[TIME[2]/16];//时
DisplayData[1]=smgduan[TIME[2]&0x0f];
DisplayData[2]=0x40;
DisplayData[3]=smgduan[TIME[1]/16];//分
DisplayData[4]=smgduan[TIME[1]&0x0f];
DisplayData[5]=0x40;
DisplayData[6]=smgduan[TIME[0]/16];//秒
DisplayData[7]=smgduan[TIME[0]&0x0f];
}
//数码管显示函数
void DigDisplay()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i)
{
case(0):LSA=1;LSB=1;LSC=1;break;//显示第0位
case(1):LSA=0;LSB=1;LSC=1;break;//显示第1位
case(2):LSA=1;LSB=0;LSC=1;break;//显示第2位
case(3):LSA=0;LSB=0;LSC=1;break;//显示第3位
case(4):LSA=1;LSB=1;LSC=0;break;//显示第4位
case(5):LSA=0;LSB=1;LSC=0;break;//显示第5位
case(6):LSA=1;LSB=0;LSC=0;break;//显示第6位
case(7):LSA=0;LSB=0;LSC=0;break;//显示第7位
}
P0=DisplayData[i];//发送数据
delay(100);
P0=0x00;
}
}
//主函数
void main()
{
Ds1302Init();
while(1)
{
datapros();//数据处理函数
DigDisplay();//数码管显示函数
}
}
头文件
#ifndef _DS1302_H_
#define _DS1302_H_
//包含头文件
#include
#include
//重定义关键词
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//定义ds1302的IO口
sbit DSIO=P3^4;
sbit RST=P3^5;
sbit SCLK=P3^6;
//定义全局函数
void Ds1302Write(uchar addr,uchar dat);
uchar Ds1302Read(uchar addr);
void Ds1302Init();
void Ds1302ReadTime();
//加入全局变量
extern uchar TIME[7];
#endif
编译过程同1.2一致。
程序下载过程同1.3一致。
无
初始化函数
#include "XPT2046.h"
//使用SPI写入数据
void SPI_Write(uchar dat)
{
uchar i;
CLK=0;
for(i=0;i<8;i++)
{
DIN=dat>>7;//放置最高位
dat<<=1;
CLK=0;
CLK=1;
}
}
//使用SPI读数据
uint SPI_Read(void)
{
uint i,dat=0;
CLK=0;
for(i=0;i<12;i++)
{
dat<<=1;
CLK=1;
CLK=0;
dat|=DOUT;
}
return dat;
}
//读取电位器数据
uint Read_AD_Data(uchar cmd)
{
uchar i;
uint AD_Value;
CLK=0;
CS=0;
SPI_Write(cmd);
for(i=6;i>0;i--);//延时等待转换结果
CLK=1;//发送一个时钟周期,清楚BUSY
_nop_();
_nop_();
CLK=0;
_nop_();
_nop_();
AD_Value=SPI_Read();
CS=1;
return AD_Value;
}
主函数
/*********************
AD模数转换实验
实验现象:下载程序后,
数码管前4位显示电位器检测
的AD值,范围是0-4095
日期:2020-04-28
作者:lixs
**********************/
#include
#include "XPT2046.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;//将单片机P2.2端口定义为LSA
sbit LSB=P2^3;//将单片机P2.3端口定义为LSB
sbit LSC=P2^4;//将单片机P2.4端口定义为LSC
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71};//段选显示0-F的值
u8 disp[4];
//延时函数
void delay(u16 i)
{
while(i--);
}
//数据处理函数
void datapros()
{
u16 ADC_Value;
float ADC_Voltage;
static u8 i;
if(i==50)
{
i=0;
ADC_Value=Read_AD_Data(0x94);
ADC_Voltage=(2.5*(float)ADC_Value)/4096;//将读取到的值转换为电压值
}
i++;
/*disp[0]=smgduan[temp/1000];//千位
disp[1]=smgduan[temp%1000/100];//百位
disp[2]=smgduan[temp%1000%100/10];//十位
disp[3]=smgduan[temp%1000%100%10];//个位*/
disp[0]=smgduan[(u8)(ADC_Voltage*100)/1000];//千位
disp[1]=smgduan[(u8)(ADC_Voltage*100)%1000/100];//百位
disp[2]=smgduan[(u8)(ADC_Voltage*100)%1000%100/10];//十位
disp[3]=smgduan[(u8)(ADC_Voltage*100)%1000%100%10];//个位
}
//数码管显示函数
void DigDisplay()//位选、段选函数,即选择点亮第几个数码管,并显示数字几
{
u8 i;
for(i=0;i<4;i++)
{
switch(i)
{
case(0):LSA=1;LSB=1;LSC=1;break;//显示第0位,即点亮LED8,左1
case(1):LSA=0;LSB=1;LSC=1;break;//显示第1位,即点亮LED7,左2
case(2):LSA=1;LSB=0;LSC=1;break;//显示第2位,即点亮LED6,左3
case(3):LSA=0;LSB=0;LSC=1;break;//显示第3位,即点亮LED5,左4
}
P0=disp[i];//发送段选数据
delay(100);//延时函数,大约延时1ms
P0=0x00;//消隐
}
}
//主函数
void main()
{
while(1)
{
datapros();
DigDisplay();
}
}
头文件
#ifndef _XPT2046_H_
#define _XPT2046_H_
//包含头文件
#include
#include
//重定义关键词
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#ifndef ulong
#define ulong unsigned long
#endif
//定义使用的IO口
sbit DOUT = P3^7; //输出
sbit CLK = P3^6; //时钟
sbit DIN = P3^4; //输入
sbit CS = P3^5; //片选
uint Read_AD_Data(uchar cmd);
uint SPI_Read(void);
void SPI_Write(uchar dat);
#endif
编译过程同1.2一致。
程序下载过程同1.3一致。
1、怎么把读取到的数值转为电压值?
答:ADC_Voltage=ADC_Value*2.5/4096,ADC_Value为读到的数值,2.5为参考电压,4096为2^12(根据分辨率决定)。
2、注意进行电压转换时,ADC_Voltage和ADC_Value需要为浮点型数据。
/*********************
DA数模转换实验
实验现象:下载程序后,
DA模块上的DA1指示灯呈
呼吸灯效果,由暗变亮再
由亮变暗
日期:2020-04-29
作者:lixs
**********************/
#include
typedef unsigned int u16;
typedef unsigned char u8;
sbit PWM=P2^1;
bit DIR;
u16 count,value,timer1;
//定时器1初始化
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动
TH1=0XFF;
TL1=0XFF;
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}
//主函数
void main()
{
Timer1Init();//定时器初始化
while(1)
{
if(count>100) //100*1us=0.1ms 计时100次后value开始++,此时value
{
count=0;
if(DIR==1)
{
value++;
}
if(DIR==0)
{
value--;
}
}
if(value==1000)
{
DIR=0;
}
if(value==0)
{
DIR=1;
}
if(timer1>1000)//PWM周期为1000*1us=1ms
{
timer1=0;
}
if(timer1<value)
{
PWM=1;
}
else
{
PWM=0;
}
}
}
//定时器的中断函数
void Time1(void) interrupt 3
{
TH1=0XFF;
TL1=0XFF;
timer1++;
count++;
}
编译过程同1.2一致。
程序下载过程同1.3一致。
无
初始化函数
#include "lcd.h"
//延时函数
void Lcd1602_Delay1ms(uint c)
{
uchar a,b;
for(;c>0;c--)
{
for(b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
//向LCD写入一个字节的命令
#ifndef LCD1602_4PINS
void LcdWriteCom(uchar com)
{
LCD1602_E=0; //使能
LCD1602_RS=0; //选择发送命令
LCD1602_RW=0; //选择写入
LCD1602_DATAPINS=com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
}
#else
void LcdWriteCom(uchar com)
{
LCD1602_E=0; //使能
LCD1602_RS=0; //选择发送命令
LCD1602_RW=0; //选择写入
LCD1602_DATAPINS=com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
LCD1602_DATAPINS=com<<4; //发送低四位
Lcd1602_Delay1ms(1);
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
}
#endif
//向LCD写入一个字节的数据
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat)
{
LCD1602_E=0; //使能
LCD1602_RS=1; //选择输入数据
LCD1602_RW=0; //选择写入
LCD1602_DATAPINS=dat; //写入数据
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
}
#else
void LcdWriteData(uchar dat)
{
LCD1602_E=0; //使能
LCD1602_RS=1; //选择写入数据
LCD1602_RW=0; //选择写入
LCD1602_DATAPINS=dat; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
LCD1602_DATAPINS=dat<<4; //发送低四位
Lcd1602_Delay1ms(1);
LCD1602_E=1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E=0;
}
#endif
//初始化LCD屏
#ifndef LCD1602_4PINS
void LcdInit()
{
LcdWriteCom(0x38); //开显示
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#else
void LcdInit()
{
LcdWriteCom(0x32); //将8位总线转为4位总线
LcdWriteCom(0x28); //在四位线下的初始化
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#endif
主函数
/*********************
LCD1602液晶实验
实验现象:下载程序后,插
上LCD1602液晶,即可显示Hello World
日期:2020-04-29
作者:lixs
**********************/
#include "reg52.h"
#include "lcd.h"
typedef unsigned int u16;
typedef unsigned char u8;
u8 Disp[]=" Hello World ";
void main()
{
u8 i;
LcdInit();
for(i=0;i<13;i++)
{
LcdWriteData(Disp[i]);
}
while(1);
}
头文件
#ifndef _LCD_H_
#define _LCD_H_
/*当使用的是4位数据传输的时候定义,使用8位取消这个定义*/
//#define LCD1602_4PINS
#include
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
void Lcd1602_Delay1ms(uint c);
void LcdWriteCom(uchar com);
void LcdWriteData(uchar dat);
void LcdInit();
#endif
编译过程同1.2一致。
程序下载过程同1.3一致。
无
普中51-单核-A2开发板的基础实验例程分享至此结束,本来打算紧接着下一篇做中级实验教程,后来由于工作,间隔了一段时间,感觉基础知识还是没吃透,所以下一篇准备做一下郭天祥的51单片机教程的课后题,温故而知新。因为手里没有开发板,到时会用proteus仿真进行结果展示,我会尽快整理完后分享给大家!(PS:关于本文章的例程结果展示动画,我会尽快补回来!!!)