1-Wired一线式总线

 常见的几种通信接口

 一线式总线

定义

  • 一线式:说明CPU和外设之间数据通信只需一根信号线,此信号线必然是数据线,并且数据线连接了一个上拉电阻,默认为高电平
  • 串行:说明CPU和外设的数据通信一个时钟周期传输一个bit位

问:没有时钟控制信号线,哪来的时钟呢,怎么去传输1个bit位呢?不像I2C总线的"低放高取",
因为它有时钟控制信号线
答:看协议

  • 总线:说明这根数据线上可以连接挂接多个外设

 

 硬件设计

DS18B20硬件特性

DS18B20芯片内部集成了64bit容量的ROM(只读存储器),存储每一个DS18B20唯一的序列码(类似身份证号)所以:CPU将来要想访问某个DS18B20,只需通过ROM中的序列码即可访问,也就是CPU只需向总线发送对应的DS18B20的序列码即可访问某个外设

ROM用来区分不同的设备

 

根据芯片手册P10,访问DS18B20遵循以下三步骤:

  • 第一步CPU向总线发送初始化复位信号,类似I2C的START信号,UART的起始位
  • 第二步CPU向总线发送ROM命令,为了找到要访问的外设,类似I2C总线发送设备地址
  • 第三步CPU向总线发送功能命令(一旦找到外设以后,下面就是读还是写还是其他功能)
     

代码部分

初始化

// 函数定义
void DS18B20_Init(void){
	// 1.打开GPIOG控制器时钟 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
	
	// 2.配置PG11 - 推挽输出, 50MHz
	GPIO_InitTypeDef GPIO_Config;
	GPIO_Config.GPIO_Pin = DS18B20_PIN;
	GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Config.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_Init(DS18B20_PORT, &GPIO_Config);
}

// 配置PG11 - 推挽输出, 50MHz
void  DS18B20_OUT(void){
	GPIO_InitTypeDef GPIO_Config;
	GPIO_Config.GPIO_Pin = DS18B20_PIN;
	GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Config.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_Init(DS18B20_PORT, &GPIO_Config);
}

// 配置PG11 - 上拉输入 
void  DS18B20_IN(void){
	GPIO_InitTypeDef GPIO_Config;
	GPIO_Config.GPIO_Pin = DS18B20_PIN;
	GPIO_Config.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 
	GPIO_Init(DS18B20_PORT, &GPIO_Config);
}

CPU向总线发送初始化复位信号

void DS18B20_Reset(void){
	u8 tempTime = 0;
	// 1.CPU将PG11拉低, >=480us	-> 复位脉冲
	DS18B20_OUT();
	DS18B20_IO_OUT = 0; // 拉低 
	delay_us(500);
	
	// 2.CPU将PG11拉高, 15-60us
	DS18B20_IO_OUT = 1; // 拉高 
	delay_us(30);
	
	// 3.DS18B20若在总线上, 拉低, 60-240us	-> 存在脉冲 
	DS18B20_IN(); // 输入 
	// 判断DS18B20是否拉低, PG11 = 0
	// 1.tempTime >= 240
	// 2.DS18B20_IO_IN == 0
	while(DS18B20_IO_IN && tempTime < 240){
		tempTime++;
		delay_us(1);
	}
	if(tempTime >= 240){
		printf("RESET FAILED\n");
	}else{
		printf("RESET SUCCESS\n");
		tempTime = 0;
	}
	
}

CPU向总线写数据

// 从低位开始发送 , 
// 参数 : data 要发送的数据, 命令(0XCC,0XBE,...)
void DS18B20_Write_Byte(u8 data){
	u8 i;
	DS18B20_OUT();
	for(i = 0; i < 8; i++){
		if(data & 0x01){ // 发送1bit的1
			// 1.PG11拉低, >1us
			DS18B20_IO_OUT = 0;
			delay_us(2);
			// 2.PG11拉高, 60us 
			DS18B20_IO_OUT = 1;
			delay_us(60);
		}else{ // 发送1bit的0
			// 1.PG11拉低, 60us
			DS18B20_IO_OUT = 0;
			delay_us(60);
			// 2.PG11拉高, 2us
			DS18B20_IO_OUT = 1;
			delay_us(2);
		}
		data >>= 1; 
	}
}

 CPU从总线读数据

 

// 功能 : 读取1字节数据 
// 读取bit, 循环读8次, 读取数据从低位读 
u8 DS18B20_Read_Byte(void){
	u8 i = 0, data = 0;// 暂存读取到的数据
	
	for(i = 0; i < 8; i++){
		// 1.CPU拉低PG11, 2us
		DS18B20_OUT();
		DS18B20_IO_OUT = 0; 
		delay_us(2);
		
		// 2.配置为输入, 释放总线权限
		// 外设发0, 主动将PG11拉低
		// 外设发1, 外设什么不做, 上拉电阻拉高, 高电平
		DS18B20_IN();
		// 延时等待, 等待高电平/低电平
		delay_us(8);
		// ----> 现在检测PG11高低电平, 高电平/低电平
		
		data |= DS18B20_IO_IN << i;
		delay_us(50); 
	}
	return data;
}

 读取ROM函数

// 定义读取ROM的函数(64bit - 8byte)
// 当做测试函数用(命令)
void DS18B20_ReadRom(void){
	// 1.初始化
	DS18B20_Reset();
	// 2.发送读ROM命令 - 0X33
	DS18B20_Write_Byte(0X33);
	// 3.循环读取8字节数据 
	u8 i;
	for(i = 0; i < 8; i++){
		rom[i] = DS18B20_Read_Byte();
		printf("%#x ", rom[i]);
	}
	printf("\n");
}

匹配ROM函数

// 定义匹配ROM的函数 
void DS18B20_MatchRom(void){
	// 1.发送MATCH ROM (0X55)
	DS18B20_Write_Byte(0X55);
	// 2.发送ROM值
	u8 i;
	for(i = 0; i < 8; i++)
		DS18B20_Write_Byte(rom[i]);
}	

获取温度值

这里的难点是解决温度值的转换

 第15:11位是表示正负的,(temp & 0xf800) == 0xf800满足这个条件就说明是负数

可以通过这个方法实现转换:取反加一, 转十添负 

第0位是1/16就是0.0625,再乘以多少个就得到温度值了

//获取温度
// 初始化 - MATCH ROM - CONVERT T - 延时 
// 初始化 - MATCH ROM - READ - 读两个字节 
float DS18B20_GetTemperature(void){
	u8 temp_lsb = 0, temp_msb = 0;
	u16 temp = 0;
	float value; // 暂存转换后的温度值 
	//1.初始化 
	DS18B20_Reset();
	//2.CPU发送MATCH ROM(0X55)
	DS18B20_MatchRom();
	//3.CPU发CONVERT T(0X44)
	DS18B20_Write_Byte(0X44);
	//4.休眠800ms
	delay_ms(800);
	///5.初始化
	DS18B20_Reset();
	//6.CPU发送MATCH ROM(0X55)
	DS18B20_MatchRom();
	//7.CPU发READ SCRATCHPAD(0XBE)
	DS18B20_Write_Byte(0XBE);
	//8.读数据(读2次即可)
	temp_lsb = DS18B20_Read_Byte(); // byte0
	temp_msb = DS18B20_Read_Byte(); // byte1
	// 低位和高位数据的合并, temp读取到的温度值
	temp = temp_msb << 8 | temp_lsb; 
	// 判断零上/零下 
	if((temp & 0xf800) == 0xf800){ // 负数/零下
		temp = (~temp) + 1;  // 获取temp的绝对值
		value = temp * (-0.0625);
	}else{// 零上/正数
		value = temp * 0.0625;
	}
	return value;
}  

测试代码

void DS18B20_Test(void){
	float temp = DS18B20_GetTemperature();
	printf("CURRENT TEMPERATURE : %.3f\n", temp);
} 

将温度传感器初始化和测试代码加入到初始化函数和cmd函数中

init.c中
static PINIT_T init_func[] = {
	LED_Init,			// led灯初始化
	BEEP_Init,		// beep初始化 
	Systick_init,	// 滴答定时器初始化
	KEY_Init,			// 按键初始化
	My_EXTI_Init,	// 中断初始化 
	UART_Init,		// 串口初始化
	AT24C02_Init, // AT24C02初始化
	DS18B20_Init, // 温度传感器的初始化
	0
};


cmd.c中
cmd_t cmd[] = {
	{"led on", LED_On},
	{"led off", LED_Off},
	{"beep on", BEEP_On},
	{"beep off", BEEP_Off},
	{"EEPROM R", AT24C02_ReadOne}, // 读取单字节
	{"EEPROM W", AT24C02_WriteOne}, //写入单字节
	{"EEPROM RS", AT24C02_ReadMul}, // 读取多字节
	{"EEPROM WS", AT24C02_WriteMul},	// 写入多字节
	{"temp", DS18B20_Test},  // 获取温度命令
	{"rom", DS18B20_ReadRom},	// 读取ROM值命令
};

结果展示

每个DS8B20的ROM都是不一样的,执行前需要读取下自己手中的DS8B20的ROM值,再写入到寄存器中进行匹配,匹配后才能读取到正确的数据

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值