#申请原创# https://bbs.21ic.com/icview-3248814-1-1.html
国产的18B20也是非常好用,轻松教你1分钟移植到M487
@21小跑堂
新唐提供的原例程在上述链接下载,接下来我们让这个例程的库函数统一到51单片机里去。
如何统一呢,其实就是实现延时函数的移植。
由于51单片机通常采用跑空指令延时的方法,因此我们这里要借助示波器即可很好的实现延时脉宽长度的测量,确保相差无几,即可实现完美移植。
原库函数如下所示,使用的是CLK_SysTickDelay()函数,这里我们替换它们即可。
static void DS18B20_Reset(void)
{
uint8_t i;
i = 1;
while (i)
{
DQ = 0; //Send a low level reset signal
CLK_SysTickDelay(480); //Delay at least 480us
DQ = 1; //Release data line
CLK_SysTickDelay(60); //Waiting for 60us
i = DQ; //Detect the presence of pulses
CLK_SysTickDelay(420); //Waiting for the device to release the data line
}
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Read 1 byte data from DS18B20
* @param None
* [url=home.php?mod=space&uid=266161]@return[/url] u8dat: Read data
*/
static uint8_t DS18B20_ReadByte(void)
{
uint8_t i;
uint8_t u8dat = 0;
for (i = 0; i < 8; i++)
{
u8dat >>= 1;
DQ = 0; //Start time slice
CLK_SysTickDelay(1); //Delay waiting
DQ = 1; //Ready to receive
CLK_SysTickDelay(1); //Reception delay
if (DQ) u8dat |= 0x80; //Read data
CLK_SysTickDelay(60); //Waiting for the end of time
}
return u8dat;
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Write 1 byte of data to the DS18B20
* @param u8dat Write data
* [url=home.php?mod=space&uid=266161]@return[/url] None
*/
static void DS18B20_WriteByte(uint8_t u8dat)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
DQ = 0; //Start time slice
CLK_SysTickDelay(1); //Delay waiting
if (u8dat & 0x01) DQ = 1; //Send data
CLK_SysTickDelay(60); //Waiting for the end of time
DQ = 1; //Restore data line
CLK_SysTickDelay(1); //Recovery delay
u8dat >>= 1;
}
}
我们注意到一共有4种时长的延时,1us,60us,420us,480us
1us简单,我们引入头文件 #include<intrins.h> //包含_nop_()函数定义的头文件,如果不包含这个,可能会被编译器优化掉时长。
然后编写一个替换上面库函数的delay实现替换新唐BSP库函数的基于滴答定时器的延时
void Delay(uint8_t i)
{
while(i--) _nop_();
}
经过测试,定义如上的延时函数可以在晶振12MHz的情况下给8051提供一个比例为10:1的us级别延时
即Delay(48) 为480us,Delay(42)为420us,Delay(6)约为60us。具体误差很小这里不再说明,大家根据自己的实际情况可以用示波器进行测量。
以下简单的C51程序为我这次测量时候写大家简单函数。
#include<reg51.h> //包含单片机寄存器的头文件
#include<intrins.h> //包含_nop_()函数定义的头文件
#define uchar unsigned char
#define uint unsigned int
#define uint8_t unsigned char
#define int16_t unsigned int
sbit DQ=P1^0;
void Delay(uint8_t i)
{
while(i--) _nop_();
}
void main(void)
{
while(1)
{
DQ=1;
Delay(6);
DQ=0;
Delay(6);
}
}
效果如何呢?利用这个方法就是一次点亮了。为了验证这个想法,把我上古年间在大学生跳蚤市场买的二手51开发板都拉出来了。
怎么样,学会了吗?
完成代码如下:
ds18b20.h
/************************************************************************
以下是DS18B20的操作程序
************************************************************************/
sbit DQ=P1^0;
#define CMD_SKIP_ROM 0xCC
#define CMD_CONVERT_T 0x44
#define CMD_READ_SCRATCHPAD 0xBE
#define uchar unsigned char
#define uint unsigned int
#define uint8_t unsigned char
#define int16_t unsigned int
static void DS18B20_Reset(void);
static void DS18B20_WriteByte(uint8_t u8dat);
static uint8_t DS18B20_ReadByte(void);
void Delay(uint8_t i)
{
while(i--) _nop_();
}
/**
* @brief Read temperature from DS18B20
* @param None
* @return i16temp: temperature
*/
int16_t DS18B20_ReadTemperature(void)
{
uint8_t u8tempH, u8tempL;
int16_t i16temp;
DS18B20_Reset();
DS18B20_WriteByte(CMD_SKIP_ROM);
DS18B20_WriteByte(CMD_CONVERT_T);
while (!DQ); //Waiting for conversion to complete
DS18B20_Reset();
DS18B20_WriteByte(CMD_SKIP_ROM);
DS18B20_WriteByte(CMD_READ_SCRATCHPAD);
u8tempL = DS18B20_ReadByte(); //Read temperature low byte
u8tempH = DS18B20_ReadByte(); //Read temperature high byte
i16temp = (u8tempH << 8) | u8tempL;
return (i16temp);
}
/**
* @brief Reset the DS18B20 and check if the device is present
* @param None
* @return None
*/
static void DS18B20_Reset(void)
{
uint8_t i;
i = 1;
while (i)
{
DQ = 0; //Send a low level reset signal
Delay(48); //Delay at least 480us
DQ = 1; //Release data line
Delay(6); //Waiting for 60us
i = DQ; //Detect the presence of pulses
Delay(42); //Waiting for the device to release the data line
}
}
/**
* @brief Read 1 byte data from DS18B20
* @param None
* @return u8dat: Read data
*/
static uint8_t DS18B20_ReadByte(void)
{
uint8_t i;
uint8_t u8dat = 0;
for (i = 0; i < 8; i++)
{
u8dat >>= 1;
DQ = 0; //Start time slice
_nop_(); //Delay waiting
DQ = 1; //Ready to receive
_nop_(); //Reception delay
if (DQ) u8dat |= 0x80; //Read data
Delay(6); //Waiting for the end of time
}
return u8dat;
}
/**
* @brief Write 1 byte of data to the DS18B20
* @param u8dat Write data
* @return None
*/
static void DS18B20_WriteByte(uint8_t u8dat)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
DQ = 0; //Start time slice
_nop_(); //Delay waiting
if (u8dat & 0x01) DQ = 1; //Send data
Delay(6); //Waiting for the end of time
DQ = 1; //Restore data line
_nop_(); //Recovery delay
u8dat >>= 1;
}
}
main.c
/******************************************************************************************************************
main.c 主文件
*******************************************************************************************************************/
#include<reg51.h> //包含单片机寄存器的头文件
#include<intrins.h> //包含_nop_()函数定义的头文件
#include"ds18b20.h"
uchar value[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //定义0~9编码的数组,后面只需要调用数组
uchar choice[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //定义数码管管位置编码数组,调用数组即可点亮对应的字
/*用于扫描显示的延时,延时越长闪烁越严重,此处小于100效果较好,大于100晃动时候可以隐隐约约感觉到闪烁*/
void delay(void)
{
uchar t;
for(t=0;t<50;t++);
}
/*显示温度整数*/
void DispLed_N(uint s)
{
/* 因为经过PNP三极管驱动,因此低电平导通三极管,然后由集电极供电给共阳极数码管的阳极。*/
P2=~choice[3];
P0=value[s%10];
delay();
P2=~choice[4];
P0=value[s/10];
delay();
}
/*显示温度小数*/
void DispLed_D(uint s)
{
/* 因为经过PNP三极管驱动,因此低电平导通三极管,然后由集电极供电给共阳极数码管的阳极。*/
P2=~choice[0];
P0=value[s%10];
delay();
P2=~choice[1];
P0=value[s/10];
delay();
}
/*显示分割线*/
void div(void)
{
P2=~choice[2];
P0=0xbf;
delay();
}
void main(void)
{
float t=15.0;
while(1)
{
t = DS18B20_ReadTemperature() * 0.0625;
DispLed_N((uint)(t));
t=t-(uint)(t);
t=t*100;
DispLed_D((uint)(t));
div();
}
}
|