上节的代码我就在这里简单汇总下
#define DHT11_BUS_HIGH (DHT11_GPIOx->BSRR = (uint32_t)DHT11_PINx) // DAT引脚 置高电平
#define DHT11_BUS_LOW (DHT11_GPIOx->BSRR = ((uint32_t)DHT11_PINx) << 16)// DAT引脚 置低电平
#define DHT11_BUS_READ ((DHT11_GPIOx->IDR & DHT11_PINx) ? 1: 0) // 读取引脚的电平
static void DHT11_Mode_IPU(void); //GPIO输入模式--上拉模式(电平保持高位)
static void DHT11_Mode_Out_PP(void); //GPIO输入模式--推挽输出
void DHT11_Init(GPIO_TypeDef *GPIOx, uint32_t PINx)
{
DHT11_GPIOx = GPIOx;
DHT11_PINx = PINx;
// 时钟使能:引脚端口;用判断端口的方式使能时钟线, 减少移植时的工作
if (GPIOx == GPIOA) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
if (GPIOx == GPIOB) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
if (GPIOx == GPIOC) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
if (GPIOx == GPIOD) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
if (GPIOx == GPIOE) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
if (GPIOx == GPIOF) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
if (GPIOx == GPIOG) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
DHT11_Mode_IPU(); // 1
DHT11_BUS_HIGH; // 2
}
步骤一中说了, 上电后保持高电平,输入状态。
这一步中说了,IO设置为低电平,输出,并且>18ms,之后就是高电平。下面就是对应的代码,在getdata()函数中。
// 1:主机产生开始信号
DHT11_Mode_Out_PP(); // 输出模式
DHT11_BUS_LOW; // 主机拉低电平
TIM6_delay_ms(25); // 延时18~30ms
// 2:主机拉高等待
DHT11_BUS_HIGH; // 总线拉高
这一步中,我的理解是这个高电平或者低电平都是DHT11自己处理的,所以不用管他。但是需要将自己的IO设置为输入状态。因为DHT11要发送信号出来了。
// 1:主机产生开始信号
DHT11_Mode_Out_PP(); // 输出模式
DHT11_BUS_LOW; // 主机拉低电平
TIM6_delay_ms(25); // 延时18~30ms
// 2:主机拉高等待
DHT11_BUS_HIGH; // 总线拉高
TIM6_delay_us(50); // 延时20~40us,这里设置50,是因为要直接进入下一个时序(电平状态),以方便检测
// 3: 从机产生响应和准备信号
DHT11_Mode_IPU(); // 主机设为输入 判断从机响应信号
if (DHT11_BUS_READ == 0) // 判断从机是否产生响应信号_低电平, 如不响应则跳出
{
while (DHT11_BUS_READ == 0); // 等待响应信号结束:低电平持续约80us
while (DHT11_BUS_READ == 1); // 等待标置信号结束:高电平持续约80us
在这一步中,不管是数据“0”还是“1”,它都是先发送50us的低电平(响应信号)。后面的信号才能区分出来是0还是1。
readByte()代码:
这段函数的思维是一次读取8位,然后用while来等待50us的低电平,等低电平过了后,延时>28us的时间,我是40us,temp左移一位,相当于在temp
二进制数的最低位添加一个0。判断现在的电平还是不是高电平,如果是,就说明这次数据就是1,等待数据1的高电平结束,使用temp |= 1;
将temp
变量的最低位设置为1。如果不是,因为左移的原因,就不用管,它就是0。
static uint8_t readByte(void)
{
uint8_t temp = 0;
uint8_t i = 0;
for ( i = 0; i < 8; i++)
{
while (DHT11_BUS_READ == 0); // 每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束
TIM6_delay_us(40); // 延时x us 这个延时需要大于数据0持续的时间即可
temp <<= 1;
if (DHT11_BUS_READ == 1) // x us后仍为高电平表示数据“1”
{
while (DHT11_BUS_READ == Bit_SET); // 等待数据1的高电平结束
temp |= 1; // 位置“1“
}
}
return temp;
}
它会发送40位数据,那么就要用不同的变量来接收这些数据。
if (DHT11_BUS_READ == 0) // 判断从机是否产生响应信号_低电平, 如不响应则跳出
{
while (DHT11_BUS_READ == 0); // 等待响应信号结束:低电平持续约80us
while (DHT11_BUS_READ == 1); // 等待标置信号结束:高电平持续约80us
// 4: 从机连续输出5字节数据
humiInt = readByte(); // 湿度的整数部分// 开始接收数据
humiDec = readByte(); // 湿度的小数部分
TempInt = readByte(); // 温度的整数部分
TempDec = readByte(); // 温度的小数部分
sum = readByte(); // 校验和
DHT11_Mode_Out_PP(); // 读取结束,引脚改为输出模式
DHT11_BUS_HIGH; // 主机拉高
// 5: 检查读取的数据是否正确
if (sum == (humiInt + humiDec + TempInt + TempDec))
{
xDHT11.Humidity = humiInt + humiDec;
xDHT11.Temperature = (float)TempInt + (float)TempDec / 10;
return SUCCESS; // 校检正确, 返回:SUCCESS=1
}
return ERROR; // 校检错误,返回:ERROR=0
}
读取结束后要把引脚改回输出模式,将主机拉高