第一节、1-Wire单总线的简述:
1.单总线的硬件结构:
下面以DS18B20传感器为例,介绍one-wire的硬件结构图:
2. 单总线1-Wire特性:
- 1-Wire由 美国Dallas(达拉斯) 公司推出
- 采用单根信号线,既可以供电又传输数据,而且数据传输是双向的,是一种半双工通信
- 单总线的数据传输速率一般为16.3Kbit/s,最大可达142 Kbit/s,通常情况下采用100Kbit/s以下的速率传输数据
- 1-Wire线端口为漏极开路构或三态门的端口,因此一般需要加上拉电阻Rp,通常选用5K~10KΩ
- 1-Wire数据传输的次序为从最低位到最高位,与I2C不同
3.单总线协议:
1.初始化:
2.写时序图:
3.读时序图:
第二节、DS18B20数字传感器芯片介绍:
1.DS18B20基本介绍
DS18B20是Dallas半导体公司推出的一种“一线总线”接口的温度传感器,工作在3~5.5V电压范围内,测量的温度范围为-55~+125℃。
每个DS18B20芯片在出厂时,都固化烧录了一个唯一的64位产品序列号在它的ROM中,可以看作是该DS18B20的地址序列码。64位ROM的排列位:前8位为产品家族码,接着的48位为DS18B20的序列号,最后8位为前56位的循环冗余校验码(CRC=X8+X5+X4+1)。ROM的作用是使每一个DS18B20各不相同,这样即可实现一根总线上挂载多个DS18B20。
2.DS18B20工作流程
DS18B20工作过程中的协议为:初始化,ROM操作命令,存储器操作命令,处理数据。
1.初始化
单总线上的所有通信都是以初始化序列开始,主机发出初始化信号后等待从设备的应答信号,以确定从设备是否存在并能正常工作。
2.ROM操作命令
主机检测到DS18B20的存在后,便可以发出ROM操作命令之一。一般我们不关心ROM中产品序列号,通常会发送0xCC跳过ROM的相关操作。
指令说明 十六进制代码
3.存储器操作指令
ROM命令操作完成之后,接下来可以发送相应的高速暂存存储器操作命令。其中0x44命令通知DS18B20温度传感器开始采样,而0xBE命令则开始读出DS18B20的采样值。
指令说明 十六进制代码
4.数据处理
DS18B20的高速暂存存储器由9个字节组成,当温度转换命令(0x44)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器前两个字节。接着可以发送读暂存存储器命令(0xBE)读出存储器里的值,存储器里的9个字节存储结构如下图所示:
如果我们只关心采样温度值的化的话,则只需要读前两个字节即可。其中Byte[0]为温度值的低字节,Byte[1]为温度值的高字节。这十六位数据的格式如下图所示:
- 9 位分辨率:每次测量结果的精度为 0.5°C,温度数据的低 7 位是无效的(固定为 0)。
- 10 位分辨率:每次测量结果的精度为 0.25°C,温度数据的低 6 位是无效的。
- 11 位分辨率:每次测量结果的精度为 0.125°C,温度数据的低 5 位是无效的。
- 12 位分辨率(默认设置):每次测量结果的精度为 0.0625°C,这是 DS18B20 可以达到的最高精度。
- 9 位:93.75 ms
- 10 位:187.5 ms
- 11 位:375 ms
- 12 位:750 ms
3.DS18b20芯片驱动的典型流程:
- 复位 1-Wire 总线,发送“Skip ROM”命令(跳过 ROM 地址匹配,如果单总线只有一个设备的话)。
- 发送“Convert T”命令,启动温度转换。
- 等待转换时间(或者轮询完成状态)。
- 再次复位 1-Wire 总线,发送“Skip ROM”命令,确保 DS18B20 准备好。
- 发送读取命令,从 DS18B20 获取温度数据。
第三节、构建DS18B20驱动:
1.单总线设备驱动.h:
#ifndef __DS18B20_H__
#define __DS18B20_H__
#include <stdint.h>
//单总线的复位功能:
uint8_t one_wire_reset(void);
//单总线发送一个bit的功能:
void one_wire_send1Bit(uint8_t bit);
//单总线发送一个字节的功能:
void one_wire_write1Byte(uint8_t byte);
//单总线上读取一个bit功能:
uint8_t one_wire_recv1Bit(void);
//单总线上读取一个字节的功能:
uint8_t one_wire_read1Byte(void);
//从ds18b20芯片上读取温度:
float get_DS18B20_Temp(void);
#endif
2.单总线设备驱动.c:
#include "ds18b20.h"
#include "gpio.h"
#include "tim2.h"
// 单总线的复位功能:
uint8_t one_wire_reset(void)
{
// 主机主动拉低单总线电平为低电平:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
// 维持480us:
tim2_u_delay(480);
// 释放单总线:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
tim2_u_delay(60);
GPIO_PinState status = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
tim2_u_delay(480);
return status == GPIO_PIN_RESET;
}
// 单总线发送一个bit的功能:
void one_wire_send1Bit(uint8_t bit)
{
if (bit == 1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
tim2_u_delay(2);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
tim2_u_delay(60);
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
tim2_u_delay(60);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
tim2_u_delay(2);
}
}
// 单总线发送一个字节的功能:
void one_wire_write1Byte(uint8_t byte)
{
for (int i = 0; i < 8; i++)
{
one_wire_send1Bit(byte & 0x01);
byte = byte >> 1;
}
}
// 单总线上读取一个bit功能:
uint8_t one_wire_recv1Bit(void)
{
// 发起读取标记:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
tim2_u_delay(2);
// 主机释放单总线:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
tim2_u_delay(12);
uint8_t bit = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
tim2_u_delay(50);
return bit;
}
// 单总线上读取一个字节的功能:
uint8_t one_wire_read1Byte(void)
{
uint8_t data = 0;
for (int i = 0; i < 8; i++)
{
data = data >> 1;
if (one_wire_recv1Bit())
{
data |= 0x80;
}
}
return data;
}
// 从ds18b20芯片上读取温度:
float get_DS18B20_Temp(void)
{
//1.复位单总线:
one_wire_reset();
//2.发送跳过ROM的命令
one_wire_write1Byte(0xcc);
//3.开启温度转换:
one_wire_write1Byte(0x44);
//4.等待数据转换(750ms)
HAL_Delay(750);
//5.再次复位
one_wire_reset();
//6.发送跳过ROm的命令
one_wire_write1Byte(0xcc);
//7.发送读取读暂存寄存器的命令
one_wire_write1Byte(0xbe);
//8.读取暂存寄存器的低八位与高八位,两个字节
uint8_t temp_LSB = one_wire_read1Byte();
uint8_t temp_MSB = one_wire_read1Byte();
uint16_t temp = temp_MSB << 8 | temp_LSB;
//9.把ds18b20的数据转换为浮点值:
if((temp & 0xf800) > 0)//负温度
{
temp = temp & 0x7ff;
return -temp * 0.0625;
}
//正温度:
temp = temp & 0x7ff;
return temp * 0.0625;
}
3.应用测试分支任务xxxtask.c:
void StartDefaultTask(void const *argument)
{
/* USER CODE BEGIN StartDefaultTask */
OLED_Init();
OLED_Clear();
const char *str = "Hello One_Wire";
uint8_t status = one_wire_reset();
//测试从ds18b20获取温度:
float temp = 0.0;
char buf[32] = {0};
/* Infinite loop */
for (;;)
{
memset(buf,0, sizeof(buf));
OLED_PrintString(0, 0, str);
OLED_PrintSignedVal(0, 2, status);
temp = get_DS18B20_Temp();
sprintf(buf, "temp = %.2f",temp);
OLED_PrintString(0,4,buf);
tim2_u_delay(1000000);
OLED_Clear();
tim2_u_delay(1000000);
}
第四节:构建DHT11驱动
1.单总线设备驱动.h
#ifndef __DHT11_H__
#define __DHT11_H__
#include "main.h"
extern uint8_t ucharT_data_H,ucharT_data_L,ucharRH_data_H,ucharRH_data_L,ucharcheckdata;
void DHT11_TEST(void); //温湿传感启动
#endif
2.单总线设备驱动.c
#include "dht11.h"
#include "stm32f4xx_hal.h"
#include "gpio.h"
#include "dht11.h"
#include "delay.h"
//温湿度定义
uint8_t ucharT_data_H=0,ucharT_data_L=0,ucharRH_data_H=0,ucharRH_data_L=0,ucharcheckdata=0;
void DHT11_TEST(void) //温湿传感启动
{
uint8_t ucharT_data_H_temp,ucharT_data_L_temp,ucharRH_data_H_humidity,ucharRH_data_L_humidity,ucharcheckdata_temp;
uint8_t ucharFLAG = 0,uchartemp=0;
uint8_t ucharcomdata;
uint8_t i;
D2_OUT_GPIO_Init();
HAL_GPIO_WritePin(DHT11_GPIO_Port,DHT11_Pin,GPIO_PIN_RESET);
delay_ms(25);
HAL_GPIO_WritePin(DHT11_GPIO_Port,DHT11_Pin,GPIO_PIN_SET);
D2_IN_GPIO_Init();
delay_us(40);
if(!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))
{
while ((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<10)//DHT11会拉低40~80us
{
ucharFLAG++;
delay_us(10);
};
if(ucharFLAG>=10)ucharFLAG=0;
else ucharFLAG=0;
while ((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<10)//DHT11拉低后会再次拉高40~80us
{
ucharFLAG++;
delay_us(10);
};
if(ucharFLAG>=10)ucharFLAG=0;
else ucharFLAG=0;
for(i=0;i<8;i++)
{
ucharFLAG=2;
while((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//
{
ucharFLAG++;
delay_us(1);
};
while((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//DHT11会拉低
{
ucharFLAG++;
delay_us(1);
};
delay_us(28);
ucharcomdata<<=1;
if(HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))ucharcomdata|=0x01;
}
ucharRH_data_H_humidity = ucharcomdata;
for(i=0;i<8;i++)
{
ucharFLAG=2;
while((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//
{
ucharFLAG++;
delay_us(1);
};
while((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//DHT11会拉低
{
ucharFLAG++;
delay_us(1);
};
delay_us(28);
ucharcomdata<<=1;
if(HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))ucharcomdata|=0x01;
}
ucharRH_data_L_humidity = ucharcomdata;
for(i=0;i<8;i++)
{
ucharFLAG=2;
while((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//
{
ucharFLAG++;
delay_us(1);
};
while((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//DHT11会拉低
{
ucharFLAG++;
delay_us(1);
};
delay_us(28);
ucharcomdata<<=1;
if(HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))ucharcomdata|=0x01;
}
ucharT_data_H_temp = ucharcomdata;
for(i=0;i<8;i++)
{
ucharFLAG=2;
while((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//
{
ucharFLAG++;
delay_us(1);
};
while((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)//DHT11会拉低
{
ucharFLAG++;
delay_us(1);
};
delay_us(28);
ucharcomdata<<=1;
if(HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))ucharcomdata|=0x01;
}
ucharT_data_L_temp = ucharcomdata;
for(i=0;i<8;i++)
{
ucharFLAG=2;
while((HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)
{
ucharFLAG++;
delay_us(1);
};
while((!HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))&&ucharFLAG<100)
{
ucharFLAG++;
delay_us(1);
};
delay_us(28);
ucharcomdata<<=1;
if(HAL_GPIO_ReadPin(DHT11_GPIO_Port,DHT11_Pin))ucharcomdata|=0x01;
}
ucharcheckdata_temp = ucharcomdata;
uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_humidity+ucharRH_data_L_humidity);
if(uchartemp==ucharcheckdata_temp)
{
ucharT_data_H = ucharT_data_H_temp;
ucharT_data_L = ucharT_data_L_temp;
ucharRH_data_H = ucharRH_data_H_humidity;
ucharRH_data_L = ucharRH_data_L_humidity;
ucharcheckdata = ucharcheckdata_temp;
}
}
else //没用成功读取,返回0
{
ucharT_data_H = 0;
ucharT_data_L = 0;
ucharRH_data_H = 0;
ucharRH_data_L = 0;
}
}
3.main程序
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "dht11.h"
#include "delay.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *p)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xff);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
printf("this is init\r\n");
delay_init(168);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
DHT11_TEST();
printf("\r\n Tem =%d\n ",ucharT_data_H);
printf("\r\n Hum =%d\n ",ucharRH_data_H);
HAL_Delay(2000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
4.sysytick实现微秒级延时
#include "stm32f4xx_hal.h"
#include "stm32f4xx.h"
#include "main.h"
#include "delay.h"
static uint32_t fac_us=0; //us延时倍乘数
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(uint8_t SYSCLK)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK;
}
//延时nus
//nus为要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
}
//延时nms
//nms:要延时的ms数
void delay_ms(uint16_t nms)
{
uint32_t i;
for(i=0;i<nms;i++) delay_us(1000);
}
#ifndef _DELAY_H
#define _DELAY_H
#include "stm32f4xx_hal.h"
#include "main.h"
void delay_init(uint8_t SYSCLK);
void delay_ms(uint16_t nms);
void delay_us(uint32_t nus);
#endif
5.DHT11的初始化
void D2_IN_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = DHT11_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}
void D2_OUT_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = DHT11_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}