1、基本概念
一线式串行总线
- “一线式”:说明CPU和外设之间数据通信只需一根信号线,此信号线必然是数据线并且数据线连接了一个上拉电阻,默认为高电平
- “串行”:说明CPU和外设的数据通信一个时钟周期传输一个bit位
问:没有时钟控制信号线,哪来的时钟呢,怎么去传输1个bit位呢?不像I2C总线的"低放高取",因为它有时钟控制信号线
答:看协议 - “总线”:说明这根数据线上可以连接挂接多个外设
2、DS18B20介绍
2.1 相关概念
- 1.ROM存储了64bit的串行码:后续对于ROM进行处理
DS18B20芯片内部集成了64bit容量的ROM(只读存储器),存储每一个DS18B20唯一的序列码(类似身份证号)所以:CPU将来要想访问某个DS18B20,只需通过ROM中的序列码即可访问,也就是CPU只需向总线发送对应的DS18B20的序列码即可访问某个外设。
- 温度值
temperature = msb << 8 | lsb;
-
访问DS18B20遵循以下三步骤:
第一步CPU向总线发送初始化复位信号,类似I2C的START信号,UART的起始位
第二步CPU向总线发送ROM命令,为了找到要访问的外设,类似I2C总线发送设备地址
第三步CPU向总线发送功能命令(一旦找到外设以后,下面就是读还是写还是其他功能) -
初始化序列
CPU发送复位脉冲
DS18B20发送了存在脉冲
-
CPU向总线发送ROM命令
-
CPU向总线发送功能命令
2.2 操作流程
-
初始化序列
-
发送ROM命令 SKIP ROM 命令
-
发送 CONVERT T 命令:开始采集温度
-
初始化序列:进行下一次温度获取
-
发送SKIP ROM命令
-
发送READ命令
-
读取两个字节的温度值
2.3 功能实现
在system目录下,新建DS18B20目录,打开keil工程,新建ds18b20.c和ds18b20.h文件
编辑ds18b20.h
#ifndef __DS18B20_H_
#define __DS18B20_H_
#include "stm32f10x.h"
#include "system.h"
// 定义DS18B20的相关信息 - GPIOG11
#define DS18B20_PORT GPIOG
#define DS18B20_PIN GPIO_Pin_11
#define DS18B20_IO_OUT PGout(11)
#define DS18B20_IO_IN PGin(11)
// 功能函数
void DS18B20_Init(void);// 初始化
void DS18B20_Reset(void);// 初始化序列
void DS18B20_Write_Byte(u8 data);// 发送单字节函数
u8 DS18B20_Read_Byte(void); // 读取单字节函数
float DS18B20_GetTemperature(void);// 获取读取温度值
void DS18B20_ReadRom();
// 测试函数
void DS18B20_Test(void); // 测试命令
#endif
编辑ds18b20.c
#include "ds18b20.h"
#include "systick.h"
#include "stdio.h"
/** 功能函数*/
// 初始化
void DS18B20_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
GPIO_InitTypeDef gpio_config;
gpio_config.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_config.GPIO_Pin = DS18B20_PIN;
gpio_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT,&gpio_config);
}
// 配置输出
void DS18B20_OUT(void){
GPIO_InitTypeDef gpio_config;
gpio_config.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_config.GPIO_Pin = DS18B20_PIN;
gpio_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT,&gpio_config);
}
// 配置输入
void DS18B20_IN(void){
GPIO_InitTypeDef gpio_config;
gpio_config.GPIO_Mode = GPIO_Mode_IPU;
gpio_config.GPIO_Pin = DS18B20_PIN;
gpio_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT,&gpio_config);
}
根据操作流程实现功能
// 初始化序列
void DS18B20_Reset(void){
DS18B20_OUT();
// 拉低,至少480us
DS18B20_IO_OUT=0;
delay_us(500);
// 拉高,保持15~60us
DS18B20_IO_OUT = 1;
delay_us(30);
// 循环等待240us,在此期间能检测到低电平,则有外设在总线上
int timeTime=0;
while(timeTime<240 && DS18B20_IO_IN){
delay_us(1);
timeTime++;
}
if(timeTime >= 240){
printf("reset failed\r\n");
}else{
printf("reset OK\r\n");
timeTime=0;
}
}
// 发送单字节函数 从低位开始数据传输
void DS18B20_Write_Byte(u8 data){
DS18B20_OUT();
for(int i=0;i<8;i++){
int temp = (data>>i)&0x01;
if(temp){ // 1
DS18B20_IO_OUT = 0;
delay_us(2);
DS18B20_IO_OUT = 1;
delay_us(60);
}else{ // 0
DS18B20_IO_OUT = 0;
delay_us(60);
DS18B20_IO_OUT = 1;
delay_us(2);
}
}
}
// 读取单字节函数 从低位开始读取
u8 DS18B20_Read_Byte(void){
u8 temp=0;
for(int i=0;i<8;i++){
DS18B20_OUT();
DS18B20_IO_OUT=0;
delay_us(2);
DS18B20_IN();
delay_us(8);
temp |=DS18B20_IO_IN<<i;
delay_us(50); // 延迟,读取一次数据的整体时间大于60us
}
return temp;
}
// 获取读取温度值
float DS18B20_GetTemperature(void){
float value;
u8 temp_lsb = 0, temp_msb = 0;
u16 temp;
// 初始化序列
DS18B20_Reset();
// 发送SKIP ROM命令
DS18B20_Write_Byte(0xcc);
// 发送CONVERT命令
DS18B20_Write_Byte(0x44);
// 初始化序列
DS18B20_Reset();
// 发送SKIP ROM命令
DS18B20_Write_Byte(0xcc);
DS18B20_Write_Byte(0xbe);
// 读取两个字节的温度值
temp_lsb = DS18B20_Read_Byte();
temp_msb = DS18B20_Read_Byte();
temp = temp_msb << 8 | temp_lsb;
// 将数字转化为温度值
if((temp & 0xf800) == 0xf800){
temp = (~temp) + 1;
value = temp * (-0.0625);
}else{
value = temp * 0.0625;
}
return value;
}
// 静态数组存储ROM值 - 本地化
static u8 rom[8]={0};
// 获取ROM
void DS18B20_ReadRom(void){
// 初始化序列
DS18B20_Reset();
// 读取ROM值
DS18B20_Write_Byte(0x33);
// 循环读8次
for(int i=0;i<8;i++){
rom[i] = DS18B20_Read_Byte();
printf("%#x ",rom[i]);
}
printf("\r\n");
}
void DS18B20_MatchRom(void){
// 先发送MATCH_ROW
DS18B20_Write_Byte(0x55);
// 循环将rom值发送到总线上匹配
for(int i=0;i<8;i++){
DS18B20_Write_Byte(rom[i]);
}
}
// 获取读取温度值
float DS18B20_GetROMTemperature(void){
float value;
u8 temp_lsb = 0, temp_msb = 0;
u16 temp;
// 初始化序列
DS18B20_Reset();
// 发送MATCH ROM命令
DS18B20_MatchRom();// 发送ROM
// 发送CONVERT命令
DS18B20_Write_Byte(0x44); // 进行温度转换
delay_ms(1000);
// 初始化序列
DS18B20_Reset();
// 发送MATCH ROM命令
DS18B20_MatchRom();
DS18B20_Write_Byte(0xbe); // 读取温度
// 读取两个字节的温度值
temp_lsb = DS18B20_Read_Byte();
temp_msb = DS18B20_Read_Byte();
temp = temp_msb << 8 | temp_lsb;
if((temp & 0xf800) == 0xf800){
temp = (~temp) + 1;
value = temp * (-0.0625);
}else{
value = temp * 0.0625;
}
return value;
}
/* 测试函数 */
void DS18B20_Test(void){// 测试命令
float temp = DS18B20_GetTemperature();
// float temp = DS18B20_GetROMTemperature();
printf("CURRENT TEMP : %.3f\r\n", temp);
}