折腾了一晚上,才把ds18b20的驱动移植到stm32上来。以前在51上使用过单个和多个连接的ds18b20,有现成的程序了,以为很快就能弄好,结果还是被卡住了,下面说下几个关键点吧:
首先是延时的问题,stm32上若用软件延时的话不太好算时间,所以要么用定时器要么用systick这个定时器来完成延时的计算。相比之下用systick来的简单方便点。
接着是stm32 io脚的配置问题,因为51是双向的io,所以作为输入输出都比较方便。stm32的io是准双向的io,网上查了下资料,说将stm32的io配置成开漏输出,然后外接上拉即可实现双向io。于是我也按规定做了,但调了老半天都不成功,是因为ds18b20没有响应的信号。在烦躁之际只有试下将接dq的io分别拉低和拉高看能不能读入正确的信号。结果果然是读入数据不对,原来我将io配成开漏输出后相当然的以为读数据是用gpio_readoutputdatabit(),这正是问题所在,后来将读入的函数改为gpio_readinputdatabit()就ok了。现在温度是现实出来了,但跟我家里那台德胜收音机上显示的温度相差2度,都不知道是哪个准了,改天再找个温度计验证下。
下面引用一段ds18b20的时序描述,写的很详细:
ds18b20的控制流程
根据ds18b20的通信协议,ds18b20只能作为从机,而单片机系统作为主机,单片机控制ds18b20完成一次温度转换必须经过3个步骤:复位、发送rom指令、发送ram指令。每次对ds18b20的操作都要进行以上三个步骤。
复位过程为:单片机将数据线拉低至少480us,然后释放数据线,等待15-60us让ds18b20接收信号,ds18b20接收到信号后,会把数据线拉低60-240us,主机检测到数据线被拉低后标识复位成功;
发送rom指令:rom指令表示主机对系统上所接的全部ds18b20进行寻址,以确定对那一个ds18b20进行操作,或者是读取某个ds18b20的rom序列号。
发送ram指令:ram指令用于单片机对ds18b20内部ram进行操作,如读取寄存器的值,或者设置寄存器的值。
具体的ram和ram指令请查阅ds18b20的数据手册。下面简单介绍:
1、rom操作命令:ds18b20采用一线通信接口。因为一线通信接口,必须在先完成rom设定,否则记忆和控制功能将无法使用。一旦总线检测到从属器件的存在,它便可以发出器件rom操作指令,所有rom操作指令均为8位长度,主要提供以下功能命令:
1 )读rom(指令码0x33h):当总线上只有一个节点(器件)时,读此节点的64位序列号。如果总线上存在多于一个的节点,则此指令不能使用。
2 )rom匹配(指令码0x55h):此命令后跟64位的rom序列号,总线上只有与此序列号相同的ds18b20才会做出反应;该指令用于选中某个ds18b20,然后对该ds18b20进行读写操作。
3 )搜索rom(指令码0xf0h):用于确定接在总线上ds18b20的个数和识别所有的64位rom序列号。当系统开始工作,总线主机可能不知道总线上的器件个数或者不知道其64位rom序列号,搜索命令用于识别所有连接于总线上的64位rom序列号。
4 )跳过rom(指令码0xcch):此指令只适合于总线上只有一个节点;该命令通过允许总线主机不提供64位rom序列号而直接访问ram,以节省操作时间。
5 )报警检查(指令码0xech):此指令与搜索rom指令基本相同,差别在于只有温度超过设定的上限或者下限值的ds18b20才会作出响应。只要ds18b20一上电,告警条件就保持在设置状态,直到另一次温度测量显示出非告警值,或者改变th或tl的设置使得测量值再一次位于允许的范围之内。储存在eeprom内的触发器用于告警。
2、ram指令
ds18b20有六条ram命令:
1)温度转换(指令码0x44h):启动ds18b20进行温度转换,结果存入内部ram。
2)读暂存器(指令码0xbeh):读暂存器9个字节内容,此指令从ram的第1个字节(字节0)开始读取,直到九个字节(字节8,crc值)被读出为止。如果不需要读出所有字节的内容,那么主机可以在任何时候发出复位信号以中止读操作。
3)写暂存器(指令码0x4eh):将上下限温度报警值和配置数据写入到ram的2、3、4字节,此命令后跟需要些入到这三个字节的数据。
4)复制暂存器(指令码0x48h):把暂存器的2、3、4字节复制到eeprom中,用以掉电保存。
5)重新调e2ram(指令码0xb8h):把eerom中的温度上下限及配置字节恢复到ram的2、3、4字节,用以上电后恢复以前保存的报警值及配置字节。
6)读电源供电方式(指令码0xb4h):启动ds18b20发送电源供电方式的信号给主cpu。对于在此命令送至ds18b20后所发出的第一次读出数据的时间片,器件都会给出其电源方式的信号。“0”表示寄生电源供电。“1”表示外部电源供电。
下面是结合实际测试总结出来的ds18b20的操作流程:
1、ds18b20的初始化
(1) 先将数据线置高电平“1”。
(2) 延时(该时间要求的不是很严格,但是尽可能的短一点)。
(3) 数据线拉到低电平“0”。
(4) 延时490微秒(该时间的时间范围可以从480到960微秒)。
(5) 数据线拉到高电平“1”。
(6)延时等待(如果初始化成功则在15到60毫秒时间之内产生一个由ds18b20所返回的低电平“0”。据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时控制)。
(7)若cpu读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(5)步的时间算起)最少要480微秒。
(8) 将数据线再次拉高到高电平“1”后结束。
2、ds18b20的写操作
(1) 数据线先置低电平“0”。
(2) 延时确定的时间为2(小于15)微秒。
(3) 按从低位到高位的顺序发送字节(一次只发送一位)。
(4) 延时时间为62(大于60)微秒。
(5) 将数据线拉到高电平,延时2(小于15)微秒。
(6) 重复上(1)到(6)的操作直到所有的字节全部发送完为止。
(7) 最后将数据线拉高。
3、 ds18b20的读操作
(1)将数据线拉高“1”。
(2)延时2微秒。
(3)将数据线拉低“0”。
(4)延时2(小于15)微秒。
(5)将数据线拉高“1”,同时端口应为输入状态。
(6)延时4(小于15)微秒。
(7)读数据线的状态得到1个状态位,并进行数据处理。
(8)延时62(大于60)微秒。
顺便把程序也贴上来吧,给大家参考下。
使用的方法:
只要调用一次 ds18b20_start() 来初始化ds18b20,然后每次读温度时直接调用 ds18b20_read()就可以了。如
ds18b20_start();
while(1)
{
for(i=1000000;i>0;i--);
val =
ds18b20_read();
}
view plaincopy to
clipboardprint?
//========================================================
// ds18b20.c by
ligh
//========================================================
#include
"stm32lib\stm32f10x.h"
#include
"ds18b20.h"
#define
enableint()
#define
disableint()
#define ds_port gpioa
#define ds_dqio gpio_pin_1
#define ds_rcc_port rcc_apb2periph_gpioa
#define ds_precision 0x7f //精度配置寄存器 1f=9位; 3f=10位; 5f=11位; 7f=12位;
#define ds_alarmth 0x64
#define ds_alarmtl 0x8a
#define
ds_convert_tick 1000
#define resetdq()
gpio_resetbits(ds_port,ds_dqio)
#define setdq() gpio_setbits(ds_port,ds_dqio)
#define getdq() gpio_readinputdatabit(ds_port,ds_dqio)
static
unsigned char
tempx_tab[16]={0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x08,0x09,0x09};
void
delay_us(u32
nus)
{
systick->load=nus*9; //时间加载
systick->ctrl|=0x01; //开始倒数
while(!(systick->ctrl&(1<<16)));
//等待时间到达
systick->ctrl=0x00000000; //关闭计数器
systick->val=0x00000000; //清空计数器
}
unsigned
char resetds18b20(void)
{
unsigned char
resport;
setdq();
delay_us(50);
resetdq();
delay_us(500); //500us
(该时间的时间范围可以从480到960微秒)
setdq();
delay_us(40); //40us
//resport =
getdq();
while(getdq());
delay_us(500); //500us
setdq();
return
resport;
}
void
ds18b20writebyte(unsigned char
dat)
{
unsigned char
i;
for(i=8;i>0;i--)
{
resetdq(); //在15u内送数到数据线上,ds18b20在15-60u读数
delay_us(5); //5us
if(dat &
0x01)
setdq();
else
resetdq();
delay_us(65); //65us
setdq();
delay_us(2); //连续两位间应大于1us
dat >>=
1;
}
}
unsigned
char ds18b20readbyte(void)
{
unsigned char
i,dat;
setdq();
delay_us(5);
for(i=8;i>0;i--)
{
dat >>= 1;
resetdq(); //从读时序开始到采样信号线必须在15u内,且采样尽量安排在15u的最后
delay_us(5); //5us
setdq();
delay_us(5); //5us
if(getdq())
dat|=0x80;
else
dat&=0x7f;
delay_us(65); //65us
setdq();
}
return
dat;
}
void readrom(unsigned
char *read_addr)
{
unsigned char
i;
ds18b20writebyte(readrom);
for(i=8;i>0;i--)
{
*read_addr=ds18b20readbyte();
read_addr ;
}
}
void
ds18b20init(unsigned char precision,unsigned char alarmth,unsigned char
alarmtl)
{
disableint();
resetds18b20();
ds18b20writebyte(skiprom);
ds18b20writebyte(writescratchpad);
ds18b20writebyte(alarmtl);
ds18b20writebyte(alarmth);
ds18b20writebyte(precision);
resetds18b20();
ds18b20writebyte(skiprom);
ds18b20writebyte(copyscratchpad);
enableint();
while(!getdq()); //等待复制完成 ///////////
}
void
ds18b20startconvert(void)
{
disableint();
resetds18b20();
ds18b20writebyte(skiprom);
ds18b20writebyte(startconvert);
enableint();
}
void
ds18b20_configuration(void)
{
gpio_inittypedef
gpio_initstructure;
rcc_apb2periphclockcmd(ds_rcc_port,
enable);
gpio_initstructure.gpio_pin =
ds_dqio;
gpio_initstructure.gpio_mode = gpio_mode_out_od;
//开漏输出
gpio_initstructure.gpio_speed = gpio_speed_50mhz;
//2m时钟速度
gpio_init(ds_port,
&gpio_initstructure);
}
void
ds18b20_start(void)
{
ds18b20_configuration();
ds18b20init(ds_precision,
ds_alarmth,
ds_alarmtl);
ds18b20startconvert();
}
unsigned
short ds18b20_read(void)
{
unsigned char
temperaturel,temperatureh;
unsigned int temperature;
disableint();
resetds18b20();
ds18b20writebyte(skiprom);
ds18b20writebyte(readscratchpad);
temperaturel=ds18b20readbyte();
temperatureh=ds18b20readbyte();
resetds18b20();
enableint();
if(temperatureh
& 0x80)
{
temperatureh=(~temperatureh) | 0x08;
temperaturel=~temperaturel 1;
if(temperaturel==0)
temperatureh =1;
}
temperatureh=(temperatureh<<4) ((temperaturel&0xf0)>>4);
temperaturel=tempx_tab[temperaturel&0x0f];
//bit0-bit7为小数位,bit8-bit14为整数位,bit15为正负位
temperature=temperatureh;
temperature=(temperature<<8)
|
temperaturel;
ds18b20startconvert();
return temperature;
}
//============================================
// ds18b20.h
//============================================
#ifndef
__ds18b20_h
#define
__ds18b20_h
#define skiprom 0xcc //跳过rom
#define searchrom 0xf0 //搜索rom
#define readrom 0x33 //读rom
#define matchrom 0x55 //匹配rom
#define alarmrom 0xec //告警rom
#define startconvert 0x44 //开始温度转换,在温度转换期间总线上输出0,转换结束后输出1
#define readscratchpad 0xbe //读暂存器的9个字节
#define writescratchpad 0x4e //写暂存器的温度告警th和tl
#define copyscratchpad 0x48 //将暂存器的温度告警复制到eeprom,在复制期间总线上输出0,复制完后输出1
#define recalleeprom 0xb8 //将eeprom的温度告警复制到暂存器中,复制期间输出0,复制完成后输出1
#define readpower 0xb4 //读电源的供电方式:0为寄生电源供电;1为外部电源供电
void
ds18b20_start(void);
unsigned short
ds18b20_read(void);
#endif
『本文转载自网络,皇冠最新app版本的版权归原作者所有,如有侵权请联系删除』