国信长天单片机竞赛训练之DS18B20温度报警实验(三)
目标要求:通过板载的DS18B20获取温度,并在数码管上保留4位小数显示,温度超过25.0000度,蜂鸣器报警;低于等于25.0000度,所有LED间隔2秒闪烁;温度超过25.0000度,通过串口发送“ temp high”字符串;低于等于25.0000,通过串口上传实时温度。
ds18b20例程的代码在资料中有提供(基于stc89c52的),而国信长天的则是stc15k60s2,由于芯片工作频率不同需要在相关代码上做些修改,需要注意的是DS18B20是存取16位数据,分为高八位和低八位,高八位的高四位为符号位,低八位的低四位是小数位,其余为为整数位,另外ds18b20的资料代码中读取采样的数据是从低位开始读取。
附上本次训练的代码:
main.c文件
#include <STC15F2K60S2.h>
#include "onewire.h"
#include "uart.h"
#include "time.h"
#include "seg.h"
static bit flag=1;
static bit flag2=1;//灯是否翻转的标志
unsigned int count=0;//计数
void Init_all(void);
#define ON 1
#define OFF 0
sbit relay =P0^4;
sbit buzzer=P0^6;
void buff(bit sw ){//蜂鸣器控制
if(sw){
P2=(P2&0x1f)|0xa0;//开蜂鸣器 1010
buzzer=1;relay=1;
P2&=0x1f;
}
else {
P2=(P2&0x1f)|0xa0;//关蜂鸣器
buzzer=0;relay=0;
P2&=0x1f;
}
}
void led(bit sw){//led toggle
if(sw){
if(flag){
P2=(P2&0x1f)|0x80;
P0=0x00;
P2=(P2&0x1f);
flag=0;
}
else {
flag=1;
P2=(P2&0x1f)|0x80;
P0=0xff;
P2=(P2&0x1f);
}
}
else{
P2=(P2&0x1f)|0x80;
P0=0xff;
P2=(P2&0x1f);//关led灯
}
}
void alarm(float temp,char *temp_str){//温度报警函数
if(temp>25.0000){
buff(ON);//开蜂鸣器
led(OFF);//关灯
flag2=1;
send_string("temp high!\r\n");//串口发送高温 high temp
}
else if(temp<25.0000){
flag2=0;
buff(OFF);//关蜂鸣器
send_string(temp_str);//上传实时温度
send_string("\r\n");
}
}
void main(void)
{
float temperature=0.0;
unsigned long temp=0;
unsigned char temp_str[8]="\0";
Init_all();
Timer0Init();
UartInit();//初始化串口的发送函数
while (1)
{
/*显示温度保留2位小数通过数码管显示*/
temperature=rd_temperature();
temp=temperature*10000;//123456
segbuff[0]=temp/100000;
segbuff[1]=temp%100000/10000 + 11;//使其带上小数
segbuff[2]=temp%10000/1000;
segbuff[3]=temp%1000/100;
segbuff[4]=temp%100/10;
segbuff[5]=temp%10;
/*数字转换成字符 加上一个0x30将数字转换成字符*/
//200000
temp_str[0]=temp/100000+0x30;
temp_str[1]=temp%100000/10000+0x30;
temp_str[2]='.';
temp_str[3]=temp%10000/1000+0x30;
temp_str[4]=temp%1000/100+0x30;
temp_str[5]=temp%100/10;
temp_str[6]=temp%10;
/*通过串口发送温度,执行指令*/
alarm(temperature,temp_str);
}
}
void time0() interrupt 1 /*根据初始设置,1毫秒执行一次*/
{
segplay();
if(!flag2){
count++;
if(count==2000){
led(ON);
count=0;
}
}
}
void Init_all(void)
{
P2=0xa0;//关闭蜂鸣器继电器
P0=0x00;
P2=0x80;//关闭LED
P0=0xff;
}
time.c
#include "time.h"
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式 16位自动重载
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1; //这两个启动一定要加,否则不生效
}
onewire.c
#include "onewire.h"
void Delay_OneWire(unsigned int t)
{
/*传统8051单片机是12T,而15单片机是1T,理论上应该扩大延时为12倍,实际上8-12倍都可以*/
unsigned char i;
while (t--)
{
for (i = 0; i < 8; 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 rd_temperature(void)
{
unsigned char low,high;/*用来存暂存器的值*/
unsigned int temp;/*取整数*/
float result;
init_ds18b20();
Write_DS18B20(0xcc);/*跳过rom搜索*/
Write_DS18B20(0x44);/*开启温度转换*/
init_ds18b20();
Write_DS18B20(0xcc);/*跳过rom搜索*/
Write_DS18B20(0xbe);/*告诉总线准备读取暂存器*/
//DS18B20 16位 高八位的低四位和低八位的高四位为整数
low=Read_DS18B20();//低八位
high=Read_DS18B20();//高八位
temp =(high&0x0f);
temp<<=8;
temp|=low;
result =temp*0.0625;
return result;
}
uart.c
#include "uart.h"
unsigned char xdata com0_buf[32];
unsigned char com0cnt = 0;
void UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器1时钟为Fosc,即1T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设定定时器1为16位自动重装方式
TL1 = 0xE0; //设定定时初值
TH1 = 0xFE; //设定定时初值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void send_char(unsigned char chr)
{
SBUF=chr;/*没有完成TI=0 完成以后TI=1 单片机自动置1*/
while(!TI);
TI=0;
}
void send_string(unsigned char *str)//发送字符串
{
while(*str)
{
send_char(*str);
str++;
}
}
time.c
#include "time.h"
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式 16位自动重载
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1; //这两个启动一定要加,否则不生效
}
seg.c
#include "seg.h"
unsigned char code segtab[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xff, 0xc0 & 0x7f, 0xf9 & 0x7f, 0xa4 & 0x7f, 0xb0 & 0x7f, 0x99 & 0x7f, 0x92 & 0x7f, 0x82 & 0x7f, 0xf8 & 0x7f, 0x80 & 0x7f, 0x90 & 0x7f,0x7f}; //共阳极7段数码管数据编码,自己可以加一些特殊符号,注意每个符号在数组中的位置
unsigned char segbuff[] = {10, 10, 10, 10, 10, 10, 10, 10}; //显示的数据,加一级缓存的好处是更新数据时只要改此缓存,定时刷新会自动将数据输出
unsigned char segaddr = 0;
void segplay(void)
{
P2 = (P2 & 0x1f) | 0xe0; //消影
P0 = 0xff;
P2 &= 0x1f;
P2 = (P2 & 0x1f) | 0xc0; //选管
P0 = 1 << segaddr;
P2 &= 0x1f;
P2 = (P2 & 0x1f) | 0xe0; //送选中管子数据
P0 = segtab[segbuff[segaddr]];
P2 &= 0x1f;
if (++segaddr == 8)
segaddr = 0;
}
最后附上工程的下载链接:
链接:https://pan.baidu.com/s/1_JT0biT4lbQoT9SE0M_Kig
提取码:yzh1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?