蓝桥杯单片机组备赛指南请查看这篇文章:戳此跳转蓝桥杯备赛指南文章
本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。
型号:国信天长4T开发板(绿板),芯片:IAP15F2K61S2
(使用国信天长蓝板也可以完美兼容,与绿板几乎无差别)
万幸,蓝桥杯比赛板上使用onewire进行通信的外设只有温度传感器——DS18B20
1. 代码目的
通过对开发板外设DS18B20的正确设置,获取温度传感器的温度数值,并显示到数码管上。
2. 头文件设置
对于需要进行通信的外设,官方提供了底层onewire.c文件,我们可以直接采用该文件中已经定义好的函数,来对DS18B20进行正确的通信设置。但是,从16年开始为了增加难度,官方提供的代码会故意出现一些错误与遗漏,我们以2023年官方提供的底层文件为参考
下载链接:链接:https://pan.baidu.com/s/1LfixDiinqsOhYbhrAptbMQ 提取码:1111
要正确使用官方提供的底层文件onewire.c有两种方式:
方式一:(直接复制)
将该文档中的代码全部复制粘贴到我们建立的工程文件的C文件中,并直接在我们自己的C文件中进行函数调用。缺点是会造成我们的代码文件破500行,调试起来眼花缭乱;优点是简单粗暴,学习成本不高。
方式二:(自定义头文件,推荐)
该方法比较推崇,建议学会,小蜜蜂老师有一节课专门讲如何操作,这里只介绍该项目的操作过程。
步骤一:建立新建一个头文件,在项目的source group文件夹中右键选择“add new items”,然后添加一个后缀为.h的文件:
然后keil5就会打开一个新的文本编辑页面,这个页面就需要写我们对官方提供的onewire.c文件的头文件,在这个文件中输入以下内容:
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);#endif
中间三行为需要在主函数文件中调用的函数声明,直接在onewire.c文件中复制过来。其他的部分是头文件固定格式,需要牢记。
步骤二:正确添加底层文件onewire.c
在项目的source group文件夹中右键选择“add existing items”,然后添加百度网盘下载的onewire.c的文件:
然后keil5工程栏就出现了一个onewire.c的文件,然后我们需要检查该文件是否缺少了一些东西。
打开该文件,点击编译,我们来看看有什么反应:
报错提示文件中的DQ变量不存在,因此我们需要在该文件的最上方对DQ变量进行定义,查看比赛提供的原理图:
可以发现DQ端口外部连接到了P1^4引脚上,因此我们需要在onewire.c中,将DQ变量看作一个特殊位变量,并对其进行定义,定义好之后再次进行编译查看:
发现报错反而多了一个P1,而P1在公共二进制文件库中可以由头文件reg52.h引用,因此我们需要在onewire.c定义该头文件:
此时编译后我们发现,错误全部消失,只有一些提示我们存在未调用程序的警告,无所谓,说明我们的修改成功了。
到此处,对于官方提供的底层代码文件添加完毕,在比赛中,我们可以采用这种一边编译,一边排错的方式对官方埋得坑进行避雷。
需要我们额外添加的代码只有两行,全部底层文件onewire.c如下:
/* # 单总线代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求,进行代码调试和修改。
*/#include <reg52.h> //引入头文件,排除P1不存在错误sbit DQ = P1^4; //根据原理图定义特殊位变量DQ//
void Delay_OneWire(unsigned int t)
{unsigned char i;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;
}
步骤三:在主函数文件中,添加头文件:
3. 原理图介绍
该外设已经有了固定的硬件连接,因此我们只需要关注其DQ位与单片机的P14引脚连接到了一起。
对DS18B20外设的额外介绍:
DQ全称为数字信号输入输出端,该传感器的最低温度精度为0.0625℃,并将读取到的温度保存到MSB、LSB两个8位寄存器当中。寄存器各位如下:
MSB S S S S S LSB 从表格中进行观察我们可以发现,
高位的MSB寄存器的前5位用于表示温度是否为整数,当全为0时极为整数,编程时不需要在意。最后3位与LSB的高4位共同构成温度的整数部分值
LSB的低4位构成了温度的小数部分。
温度具体数值转换方式与二进制转十进制相同,编程时,只需要将MSB和LSB中的有效数值位读取出来,合并成一个11位的2进制数,并乘以精度0.0625即可得到当前的准确温度值。
但是温度是存在小数的,因此我们常常将温度值乘以10,构成一个无小数的整数,从而便于在数码管中进行显示。
4. DS18B20操作流程
4.1 时序操作流程
1 | DS18B20初始化复位 |
2 | 写入0xCC,跳过ROM寻址指令 |
3 | 写入0x44,开始温度转换 |
4 | 延时700~900ms |
5 | DS18B20初始化复位 |
6 | 写入0xCC,跳过ROM寻址指令 |
7 | 写入0xBE,读取高速暂存器 |
8 | 读取温度数值存入LSB |
9 | 读取温度数值存入MSB |
以上所有的操作,除了第4步延时以外,其余函数都在官方提供的底层代码中直接调用。为了方便使用,我们常常自己写延时函数,并且在延时函数中放入我们的数码管刷新函数,避免在这段延时期间,数码管产生明显的闪烁。
4.2 程序操作流程
一般我们不可能遇到温度为零下的可能,因此我直接去掉了判断温度是否大于零的部分。
最后三行为数值提取的思路,不要死记,建议理解,注意对temperature的左移和右移会引起其值的大小变化:右移4位相当于除以16,或者乘以0.0256
5. 代码参考
#include <reg52.h>
#include <intrins.h>
#include "onewire.h"void SMGrunning ();unsigned char code duanma[10] = { 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 , 0x90 };
unsigned char code duanma_dot[10] = { 0x40 , 0x79 , 0x24 , 0x30 , 0x10 , 0x12 , 0x02 , 0x78 , 0x00 , 0x10 };void Delay2ms() //@12.000MHz
{unsigned char i, j;i = 20;j = 50;do{while (--j);} while (--i);
}void Delay800ms() //@12.000MHz
{unsigned char j;_nop_();_nop_();j = 20;do{SMGrunning ();} while (--j);}void select_HC573 ( unsigned char channal )
{switch ( channal ){case 4:P2 = ( P2 & 0x1f ) | 0x80;break;case 5:P2 = ( P2 & 0x1f ) | 0xa0;break;case 6:P2 = ( P2 & 0x1f ) | 0xc0;break;case 7:P2 = ( P2 & 0x1f ) | 0xe0;break;}
}void SMG_state ( unsigned char pos_SMG , unsigned char value_SMG )
{select_HC573 ( 6 );P0 = 0x01 << pos_SMG;select_HC573 ( 7 );P0 = value_SMG;
}void SMG_all_down ()
{select_HC573 ( 6 );P0 = 0xff;select_HC573 ( 7 );P0 = 0xff;
}unsigned int temperature = 0;
void ds18b20_temperature ()
{unsigned char LSB,MSB;init_ds18b20();Write_DS18B20(0xcc); Write_DS18B20(0x44);Delay800ms();init_ds18b20();Write_DS18B20(0xcc); Write_DS18B20(0xbe);LSB = Read_DS18B20();MSB = Read_DS18B20();temperature = MSB;temperature = ( temperature << 8 ) | LSB;temperature = (temperature >> 4)*10 + (LSB & 0x0f)*0.625;}void SMGrunning ()
{SMG_state( 0 , 0xff );Delay2ms();SMG_state( 1 , 0xff );Delay2ms();SMG_state( 2 , 0xff );Delay2ms();SMG_state( 3 , 0xff );Delay2ms();SMG_state( 4 , 0xff );Delay2ms();SMG_state( 5 , duanma[temperature/100] );Delay2ms();SMG_state( 6 , duanma_dot[temperature/10%10] );Delay2ms();SMG_state( 7 , duanma[temperature%10] );Delay2ms();SMG_all_down ();}int main ()
{while ( 1 ){ds18b20_temperature ();SMGrunning ();}
}
6. 编程思路重述
定义一个全局变量temperature,用于使用官方提供的函数进行DS18B20温度获取
时序步骤:
初始化、写入0xCC、写入0X44
初始化、写入0XCC、写入0XBE
读取温度到LSB、读取温度到MSB
整合MSB和LSB获得最终数据