51单片机学习第六课---B站UP主江协科技
DS18B20
1、基本知识讲解
2、DS18B20读取温度值
main.c
#include<regx52.h>
#include"delay.h"
#include"LCD1602.h"
#include"key.h"
#include"DS18B20.h"
float T;
void main ()
{
LCD_Init();
LCD_ShowString(1,1,"temp");
while(1)
{
DS18B20_convert();
T=DS18B20_read();
if(T<0)
{
LCD_ShowChar(2,1,'-'); //显示负号
T=-T;
}
else
{
LCD_ShowChar(2,1,'+'); //显示正号
T=T;
}
LCD_ShowNum(2,2,T,3); //显示正整数 ,精度是3,-55~125
LCD_ShowChar(2,5,'.'); //显示小数点
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示小数后面的数字,精度是4位 ,0.0623
}
}
onewire.c
#include<REGX52.h>
sbit onewire_DQ=P3^7;
unsigned char onewire_init()
{
unsigned char ackbit;
unsigned char i;
onewire_DQ=1;
onewire_DQ=0;
i = 247;
while (--i); //延时500us
onewire_DQ=1;
i = 32; //延时70us
while (--i);
ackbit=onewire_DQ;
i = 247; //延时500us,这里从机拉低60-240us会自动释放总线
while (--i);
return ackbit;
}
void onewire_sendbit(unsigned char Bit)
{
unsigned char i;
onewire_DQ=0;
i = 4;
while (--i);//延时10us
onewire_DQ=Bit; //如果bit=0,则DQ继续拉低50us,加上前面10us,刚好满足60us.如果bit=1,则DQ相当10us后总线释放,接着继续释放50us
i = 24;
while (--i);//延时50us
onewire_DQ=1;
}
unsigned char onewire_receivebit()
{
unsigned char i;
unsigned char Bit;
onewire_DQ=0;
i = 2;
while (--i);//主机拉低总线5us
onewire_DQ=1;
i = 2;
while (--i);//主机释放总线5us
Bit=onewire_DQ;//DS18B20将会发送0或1,此时主机读取数据
i = 24;
while (--i);//延时50us
return Bit;
}
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;
unsigned char byte=0x00;
for(i=0;i<8;i++)
{
if(onewire_receivebit())
{
byte=byte|(0x01<<i); //取整体用或
}
}
return byte;
}
DS18B20.C
#include<regx52.h>
#include"onewire.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_convert()
{
onewire_init() ; //初始化
onewire_sendbyte(DS18B20_SKIP_ROM); //跳过ROM
onewire_sendbyte(DS18B20_CONVERT_T); //温度转换
}
float DS18B20_read()
{
unsigned char TLSB,TMSB;
int temp;
float T;
onewire_init() ;
onewire_sendbyte(DS18B20_SKIP_ROM);
onewire_sendbyte(DS18B20_READ_SCRATCHPAD);//温度读取
TLSB=onewire_receivebyte(); //读取低八位
TMSB=onewire_receivebyte(); //读取高八位
temp=(TMSB<<8)|TLSB; //把低八位和高八位整合成一个数
T=temp/16.0; //由于存储小数的缘故,最低位不是从2的0次方开始,而是2的-4方开始,所以读取的值要除以16
return T;
}
3、DS18B20温度告警器
按键K1实现对温度上限值的加加,K2实现对温度上限值的减减,按键K3实现对温度下限值的加加,K4实现对温度下限值的减减,运用at24c02对温度上限值和下限值进行存储。超过上限值LCD1602显示ov:h,低于下限值LCD1602显示ov:l。
main.c
#include<regx52.h>
#include"delay.h"
#include"LCD1602.h"
#include"key.h"
#include"DS18B20.h"
#include"at24c02.h"
#include"timer0.h"
char Tlow,Thigh; //char类型表示数据位-127~128,满足DS18B20的测温范围-55~125
float T,Tshow;
unsigned char keynum;
void main ()
{
DS18B20_convert();
Delay(1000);
Thigh=at24c02_readbyte(0); //一开始就读取AT24C02里的数据,这个时候还没有写入
Tlow=at24c02_readbyte(1);
if(Thigh>125||Tlow<-55||Thigh<=Tlow){ Thigh=25;Tlow=5;} //读出的数据可能会超出范围,所以当超出范围时,赋初值
LCD_Init();
Timer0_Init();
LCD_ShowString(1,1,"T:");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
LCD_ShowSignedNum(2,4,Thigh,3);
LCD_ShowSignedNum(2,12,Tlow,3);
while(1)
{
keynum=key();
DS18B20_convert();
T=DS18B20_read();
if(T<0) {LCD_ShowChar(1,3,'-');Tshow=-T;}//为什么要定一个Tshow,主要是当T为负数时,要转为正数,这样Tshow就始终是正数
if(T>0) {LCD_ShowChar(1,3,'+');Tshow=T;} //因为shownum只能显示正整数,而且要显示小数部分,转成正整数方便转换
LCD_ShowNum(1,4,Tshow,3);
LCD_ShowChar(1,7,'.');
LCD_ShowNum(1,8,(unsigned long)(Tshow*100)%100,2);
if(keynum)
{
if(keynum==1)
{Thigh++;
if(Thigh>125){Thigh=125;} //温度上限值不能超过DS18B20的上限
}
if(keynum==2){
Thigh--;
if(Thigh<=Tlow) //温度上限值减减不能减到小于温度下限值
{
Thigh++;}
}
if(keynum==3){
Tlow++;
if(Tlow>=Thigh) //温度下限值加加不能加到大于温度上限值
{
Tlow--;
}
}
if(keynum==4)
{
Tlow--;
if(Tlow<-55){Tlow=-55;} //温度下限值不能超过DS18B20的下限
}
at24c02_writebyte(0,Thigh); //记住写入后要延时5ms
Delay(5);
at24c02_writebyte(1,Tlow);
Delay(5);
LCD_ShowSignedNum(2,4,Thigh,3);
LCD_ShowSignedNum(2,12,Tlow,3);
}
if(T>Thigh){
LCD_ShowString(1,13,"ov:H");} //实际温度超过设定上限值告警
else if(T<Tlow){
LCD_ShowString(1,13,"ov:L");} //实际温度低于设定下限值告警
else{LCD_ShowString(1,13," ");}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int count;
TL0 = 0x18;
TH0 = 0xFC;
count++;
if(count>=20)
{
count=0;
key_loop();
}
}
onewire.c
这里增加一步关闭中断,再打开中断
#include <REGX52.H>
sbit onewire_DQ=P3^7;
unsigned char onewire_init()
{
unsigned char ackbit;
unsigned char i;
onewire_DQ=1;
onewire_DQ=0;
EA=0;//单总线计时时不能进入中断,因为延时是us级别,中断之后再返回会计时不准,所以这里要关掉总中断,计时完后再打开。
i = 247;
while (--i); //延时500us
onewire_DQ=1;
i = 32; //延时70us
while (--i);
ackbit=onewire_DQ;
i = 247; //延时500us,这里从机拉低60-240us会自动释放总线
while (--i);
EA=1;
return ackbit;
}
void onewire_sendbit(unsigned char Bit)
{
unsigned char i;
onewire_DQ=0;
EA=0;
i = 4;
while (--i);//延时10us
onewire_DQ=Bit; //如果bit=0,则DQ继续拉低50us,加上前面10us,刚好满足60us.如果bit=1,则DQ相当10us后总线释放,接着继续释放50us
i = 24;
while (--i);//延时50us
EA=1;
onewire_DQ=1;
}
unsigned char onewire_receivebit()
{
unsigned char i;
unsigned char Bit;
onewire_DQ=0;
EA=0;
i = 2;
while (--i);//主机拉低总线5us
onewire_DQ=1;
i = 2;
while (--i);//主机释放总线5us
Bit=onewire_DQ;//DS18B20将会发送0或1,此时主机读取数据
i = 24;
while (--i);//延时50us
EA=1;
return Bit;
}
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;
unsigned char byte=0x00;
for(i=0;i<8;i++)
{
if(onewire_receivebit())
{
byte=byte|(0x01<<i); //取整体用或
}
}
return byte;
}
LCD1602
1、基本知识讲解
2、字符、字符串
2、LCD1602.c代码
#include<regx52.h>
sbit lcd_RS=P2^6;
sbit lcd_RW=P2^5;
sbit lcd_EN=P2^7;
#define port P0
void LCD_Delay() //@12.000MHz 1ms //延时函数
{
unsigned char data i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
void lcd_write_command(unsigned char command) //写命令
{
lcd_RS=0;
lcd_RW=0;
port=command;
lcd_EN=1;
LCD_Delay();//因为这里E脉冲宽度时间是us级别,而单片机执行一次命令是us级别,所以这里检测不到,所以加延时函数
lcd_EN=0;
LCD_Delay();
}
void lcd_write_Data(unsigned char Data) //写数据
{
lcd_RS=1;
lcd_RW=0;
port=Data;
lcd_EN=1;
LCD_Delay();//因为这里E脉冲宽度时间是us级别,而单片机执行一次命令是us级别,所以这里检测不到,所以加延时函数
lcd_EN=0;
LCD_Delay();
}
void lcd_init() //初始化
{
lcd_write_command(0x38);
lcd_write_command(0x0C);
lcd_write_command(0x06);
lcd_write_command(0x01);
}
void LCD_setcursor(unsigned char line,unsigned char column) //定义光标位置,在第几行第几列
{
if(line==1)
{
lcd_write_command(0x80|column-1);
}
else
{
lcd_write_command(0x80|(column-1)+0x40);
}
}
void LCD_ShowChar(unsigned char line,unsigned char column,unsigned char Char) //显示一个字符
{
LCD_setcursor(line,column);
lcd_write_Data(Char);
}
void LCD_ShowString(unsigned char line,unsigned char column,unsigned char *String) //显示字符串
{
unsigned char i;
LCD_setcursor(line,column);
for(i=0;String[i]!='\0';i++)
{
lcd_write_Data(String[i]);
}
}
unsigned int pow(x,y) //定义一个幂函数 例如x=6,y=4,则result=1*6*6*6*6=6^4
{
unsigned char result=1;
unsigned int i;
for(i=0;i<y;i++)
{
result*=x;
}
return result;
}
void LCD_ShowNum(unsigned char line,unsigned char column,unsigned int num,unsigned char length) //显示十进制数字
{
unsigned char i;
LCD_setcursor(line,column);
for(i=length;i>0;i--)
{
lcd_write_Data(0x30+num/(pow(10,i-1))%10);
}
}
void LCD_ShowsignNum(unsigned char line,unsigned char column, int num,unsigned char length)//显示有符号十进制数字
{
unsigned char i;
unsigned int num1;
LCD_setcursor(line,column);
if(num>=0)
{
lcd_write_Data('+');
num1=num;
}
else
{
lcd_write_Data('-');
num1=-num;//有符号int的负数最小值为-32768,正数最大值为32767,取反会超出范围,所以定义一个无符号int num1
}
for(i=length;i>0;i--)
{
lcd_write_Data(0x30+num1/(pow(10,i-1))%10);
}
}
void LCD_ShowHexNum(unsigned char line,unsigned char column, unsigned int num,unsigned char length) //显示十六进制数
{
unsigned char i;
unsigned char SingleNum;
LCD_setcursor(line,column);
for(i=length;i>0;i--)
{
SingleNum=num/(pow(16,i-1))%16;
if(SingleNum<10)
{lcd_write_Data(0x30+SingleNum); }
else
{
{lcd_write_Data(0x41+SingleNum-10); }
}
}
}
void LCD_ShowBinNum(unsigned char line,unsigned char column, unsigned int num,unsigned char length) //显示二进制数
{
unsigned char i;
LCD_setcursor(line,column);
for(i=length;i>0;i--)
{
lcd_write_Data(0x30+num/(pow(2,i-1))%2);
}
}
main.c
#include<regx52.h>
#include"LCD1602.h"
#include"delay.h"
void main ()
{
lcd_init();
LCD_ShowChar(2,4,'A');
// LCD_ShowString(1,3,"hello");
LCD_ShowNum(2,6,66,2);
LCD_ShowsignNum(2,10,-77,2);
// LCD_ShowHexNum(1,8,0x55,2);
LCD_ShowBinNum(1,1, 0x55,8);
LCD_ShowString(1,16,"welcome to my home!");
while(1)
{
lcd_write_command(0x1c); //流动字幕,移屏
Delay(500);
}
}
直流电机
1、基本知识讲解
2、LED呼吸灯
main.c
#include<regx52.h>
sbit LED=P2^0;
void Delay(unsigned int ms)
{
while(ms--);
}
void main ()
{
while(1)
{
unsigned char i,t;
for(i=100;i>0;i--)
{
for(t=0;t<20;t++)
{
LED=0;
Delay(i);
LED=1;
Delay(100-i);
}
}
for(i=0;i<100;i++)
{
for(t=0;t<20;t++)
{
LED=0;
Delay(i);
LED=1;
Delay(100-i);
}
}
}
}
3、直流电机调速
按K1键实现电机的调速,并在数码管上显示档数。
main.c
#include<regx52.h>
#include"key.h"
#include"nixie.h"
#include"Timer0.h"
sbit motor=P1^0;
unsigned char counter,compare,keynum,speed;
void main ()
{
Timer0_Init();
while(1)
{
keynum=key();
if(keynum==1)
{
speed++;speed%=4;
if(speed==0){ compare=0;}
if(speed==1){ compare=5;}
if(speed==2){ compare=50;}
if(speed==3){ compare=100;}
}
nixie (0,speed);
}
}
void Timer0_Routine() interrupt 1
{
TL0 = 0x9C;
TH0 = 0xFF;
counter++;
counter%=100;
if(counter<compare)
{
motor=1;
}
else
{
motor=0;
}
}
ps:对达林顿管还不是很清楚