【航顺HK32F103A开发板试用】+DHT11检测温湿度发送主机显示

原创2026-09-20 09:01:37浏览3
3
Star

1.产品概述

HK32F103xCxDxE 系列使用ARM® Cortex®-M3 内核,最高工作频率120 MHz。

HK32F103xCxDxE 内置了大容量存储器:最高512Kbyte Flash、64 KbyteSRAM。此外,可通过FSMC 模块外挂最多1 Gbyte 容量的NOR/PSRAM/NAND/PC Card 存储器,其中有256 Mbyte 的空间可以存放指令,并且可以被片内1 Kbyte 指令Cache 缓存。

HK32F103xCxDxE 内置的CRC 模块提供了数据完整性的检查能力。

HK32F103xCxDxE 内置了丰富的通信接口满足多种通信应用场景:5 路USART/UART、3 路SPI(SPI2/3支持I2S 协议)、1 路SDIO、2 路I2C、1 路CAN 2.0 A/B 和1 路FS USB。

HK32F103xCxDxE 内置2 个高级16 位PWM 计时器(共8 路PWM 输出,其中6 路带死区互补输出),4 个通用16 位PWM 计时器(共16 路PWM 输出),2 个16 位的基本定时器。

HK32F103xCxDxE 提供独立的VBAT 电池电源域,当VDD 主电源掉电时,RTC 模块可在VBAT 供电下继续工作;另外,VBAT 电池电源域提供了84 Byte 的备份寄存器。

HK32F103xCxDxE 内置了丰富的模拟电路:3 个12 位ADC(支持最多16 路外部模拟输入通道同时使用,其中2 路弱驱动信号输入通道和1 路5V 高压信号输入通道)、2 个12 位DAC、1 个温度传感器、1个0.8V 内部参考电压源、1 个可编程电压检测器(PVD)、1 个上电/下电复位(POR/PDR)电路和1 个VBAT电源电阻分压器(分压器输出在片内与ADC 相连)。

HK32F103xCxDxE 支持丰富的功耗模式;在最低功耗模式下,芯片的典型漏电电流小于 100nA。 HK32F103xCxDxE 工作于-40°C 至+105°C 的温度范围,供电电压2.0V 至3 .6V,可满足绝大部分应用环境条件的要求。

完整的HK32F103xC、HK32F103xD 和HK32F103xE 系列产品包括从64 脚至100 脚的两种不同封装。根据不同的封装形式,器件中的外设配置有差异。

由于拥有丰富的外设配置,HK32F103xCxDxE MCU 可适用于多种应用场景:

• 工业应用,如可编程控制器、打印机、扫描仪等

• HMI 人机音视频多媒体交互

• 图形显示设备,如广告显示

• 语音识别设备

• 安全监控设备

• 电机驱动和调速控制

• 物联网低功耗传感器终端,如运动手环等

• 无人机飞控、云台控制

• 玩具产品

• 家用电器

• 智能机器人

• 智能手表

2.产品特性

• ARM® Cortex®-M3 内核

◦ 最高时钟频率:120 MHz

◦ 24 位System Tick 定时器

◦ 支持CPU Event 信号输入至MCU 引脚,实现与板级其它Soc CPU 的联动。

• 工作电压范围

◦ 双电源域:

- 主电源VDD 为2.0 V ~ 3.6 V

- 备份电源VBAT 为1.8 V ~ 3.6 V

◦ 当主电源掉电时,RTC 可继续工作在VBAT 电源下。

◦ VBAT 电源域提供84 byte 备份寄存器。

• 工作温度范围:-40°C ~ +105°C

• VDD 典型工作电流

◦ 运行(Run)模式:19.3 mA@120MHz@3 .3V

◦ 睡眠(Sleep)模式:5.6mA@120MHz@3 .3V(唤醒时间:1 个机器时钟周期)

◦ 停机(Stop)模式

- LDO 低功耗模式:89.4 μA@3 .3V(唤醒时间:10µs)

- LDO 全速工作模式:303 μA@3 .3V

◦ 待机(Standby)模式:3.3 μA@3 .3V(唤醒时间:150µs)

• VBAT 典型工作电流(VDD 掉电)

◦ VBAT RTC 开启模式:2.6 μA@3 .3V

◦ VBAT RTC 关闭模式:2.1 μA@3 .3V

• 存储器

◦ 最高512 Kbyte 的Flash 存储器

- 当CPU 主频不高于24 MHz 时,支持0 等待总线周期。

- 具有代码安全保护功能,可分别设置读保护和写保护。

◦ 64Kbyte 片内SRAM

◦ FSMC 模块可外挂1 Gbyte NOR/PSRAM/NAND/PC Card 存储器(其中,256 Mbyte 的空间可以存放指令,可用于片内Cache 缓存)

• 数据安全

◦ CRC32 校验硬件单元

• 时钟

◦ 外部高速时钟(HSE):支持4 ~ 32 MHz,典型8 MHz

◦ 外部低速时钟(LSE):32.768 kHz

◦ 片内高速时钟(HSI):8 MHz/28 MHz/56 MHz 可配置

◦ 片内低速时钟(LSI):40 kHz

◦ PLL 输出时钟:120MHz(最大值)

◦ GPIO 外部输入时钟:1 ~ 64 MHz

• 复位

◦ 外部管脚复位

◦ 电源复位(POR/PDR)

◦ 软件复位

◦ 看门狗(IWDG和WWDG)复位

◦ 低功耗管理复位

• 可编程电压检测器(PVD)

◦ 8 级检测电压门限可调

◦ 上升沿和下降沿检测可配置

• 通用输入输出端口(GPIO)

◦ 64 脚封装提供51 个GPIO 引脚,100 脚封装提供80 个GPIO 引脚

◦ 所有GPIO 引脚可配置为外部中断输入

◦ 内置可开关的上、下拉电阻

◦ 支持开漏(Open-Drain)输出

◦ 支持施密特(Schmitt)迟滞输入

◦ 输出驱动能力超高、高、中、低四档可选

• 数据通讯接口

◦ 5 路USART/UART(USART1/2/3,UART4/5)

◦ 3 路SPI(SPI2/3 支持I2S 协议)

◦ 2 路I2C

◦ 1 路SDIO

◦ 1 路CAN 2.0 A/2.0B

◦ 1 路全速USB2.0

• 定时器及PWM 发生器

◦ 高级定时器:TIM1/TIM8(带死区互补PWM 输出)

◦ 通用定时器:TIM2/TIM3/TIM4/TIM5

◦ 基本定时器:TIM6/TIM7(支持CPU 中断、DMA 请求和DAC 转换触发)

• 片内模拟电路

◦ 3 个12 位1 MSPS ADC(支持最多16 路外部模拟输入通道同时使用);支持双ADC 模式,采样率最高2 MSPS。

◦ 2 个12 位DAC

◦ 1 个温度传感器

◦ 1 个0.8 V 内部参考电压源

◦ 1 个VBAT 电源电阻分压器(分压器输出在片内与ADC 相连,实现VBAT 电源电压监控)

• DMA 控制器

◦ 2 个独立DMA:DMA1 和DMA2

◦ DMA1 提供7 路通道

◦ DMA2 提供5 路通道

◦ 支持Timer、ADC、SPI、I2C、USART、UART 等多种外设触发

• CPU 调试及跟踪接口

◦ SW-DP 两线调试端口

◦ JTAG 五线调试端口

◦ ARM DWT、FPB、ITM、TPIU 调试追踪模块

◦ 单线异步跟踪数据输出接口(TRACESWO)

◦ 四线同步跟踪数据输出接口(TRACEDO[3:0]、TRACECKO)

◦ 自定义DBGMCU 调试控制器(低功耗模式仿真控制、调试外设时钟控制、调试及跟踪接口分配)

3. 硬件原理图

 

4. 测评内容

本次测评完成温湿度检测并通过UART发送主机显示,使用PB12引脚作为DHT11数据引脚,使用PA9和PA10作为UART串口引脚 /*USART1:PA9(TX)---PA10(RX)*/,接线如图:

 

5. 软件代码

代码主要分为2个模块,一部分完成uart输出数据,一部分完成DHT11温湿度检测:

 

/**

  * @brief  Deinitializes the USART1 peripheral registers to their default reset values.

  * @param  None

  * @retval None

  */

void UART_Config()

{

    RCC_HCLKConfig(RCC_SYSCLK_Div1);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

USART_InitTypeDef UART_InitStruct;

UART_InitStruct.USART_BaudRate = 115200;

UART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

UART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;

UART_InitStruct.USART_StopBits = USART_StopBits_1;

UART_InitStruct.USART_WordLength = USART_WordLength_8b;

UART_InitStruct.USART_Parity = USART_Parity_No;

    

USART_Init(USART1,&UART_InitStruct);

 

    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);

 

USART_Cmd(USART1,ENABLE);

 

}

 

 

/**

  * @brief  Deinitializes the GPIO peripheral registers to their default reset values.

  * @param  None

  * @retval None

  */

void GPIO_Config(void)

{

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStruct;

    

/*USART1:PA9(TX)---PA10(RX)*/

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStruct);  

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOA, &GPIO_InitStruct);  

 

}

 

/**

  * @brief  Retargets the C library printf function to the USART.

  * @param  None

  * @retval None

  */

int fputc(int ch,FILE *f)

{

 

    USART_SendData(USART1, (uint8_t) ch);

    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)

    {

    }

    return ch;

}

DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率 只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。DHT11 只有在接收到开始信号后才触发一次温湿度采集,如果没有接收到主机发送复位信号,DHT11 不主动进行温湿度采集。当数据采集完毕且无开始信号后,DHT11 自动切换到低速模式。

DHT11 的初始化过程同样分为复位信号和响应信号。

首先主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,取中间值 30us,此时复位信号发送完毕。

 

从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集。采集数据后转换到低速模式。

 

 DHT11 发送响应信号

DHT11 检测到复位信号后,触发一次采样,并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了;然后 DHT11 拉高总线 80us,之后开始传输数据。如果检测到响应信号为高电平,则 DHT11 初始化失败,请检查线路是否连接正常。

 

当复位信号发送完毕后,如果检测到总线被拉低,就每隔 1us 计数一次,直至总线拉高,计算低电平时间;当总线被拉高后重新计数检测 80us 的高电平。如果检测到响应信号之后的80us 高电平,就准备开始接收数据。实际上 DHT11 的响应时间并不是标准的 80us,往往存在误差,当响应时间处于 20~100us 之间时就可以认定响应成功。

 

数据传输

DHT11 在拉高总线 80us 后开始传输数据。每 1bit 数据都以 50us 低电平时隙开始,告诉主机开始传输一位数据了。DHT11 以高电平的长短定义数据位是 0 还是 1,当 50us 低电平时隙过后拉高总线,高电平持续 26~28us 表示数据“0”;持续 70us 表示数据“1”。

 

当 最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。

 

区分数据0/1的巧法

还是像检测响应时间那样计算高电平持续时间那就太麻烦了!!!

 

数据“0”的高电平持续 26~28us,数据“1”的高电平持续70us,每一位数据前都有 50us 的起始时隙。如果我们取一个中间值 40us 来区分数据“0”和数据“1”的时隙。

 

当数据位之前的 50us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 70us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 50us 的开始时隙,那么上一位数据肯定是“0”。

 

为什么延时 40us?

由于误差的原因,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长。

当数据“0”时隙大于 26~28us 时,

如果延时太短,无法判断当前处于数据“0”的时隙还是数据“1”的时隙;

如果延时太长,则会错过下一位数据前的开始时隙,导致检测不到后面的数据

因为时序在DHT11读取非常重要所以使用了嘀嗒定时器完成延时,具体代码如下:

dht11.h

#ifndef __DHT11_H

#define __DHT11_H

#include "hk32f103xxxxa.h"

#define dht11_high GPIO_SetBits(GPIOB, GPIO_Pin_12)

#define dht11_low GPIO_ResetBits(GPIOB, GPIO_Pin_12)

#define Read_Data GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)

void DHT11_GPIO_Init_OUT(void);

void DHT11_GPIO_Init_IN(void);

void DHT11_Start(void);

unsigned char DHT11_REC_Byte(void);

void DHT11_REC_Data(void);

#endif

DHT11.c

#include "hk32f103xxxxa.h"

#include  "dht11.h"

 

unsigned int rec_data[4];

 

void delay_us(uint32_t i)

{

    uint32_t temp;

    SysTick->LOAD = SystemCoreClock / 8000000 * i;

    SysTick->VAL = 0x00;

    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

    do {

        temp = SysTick->CTRL;

    } while((temp&0x01)&&!(temp&(1<<16))); SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        SysTick->VAL =0X00;  

}

 

void delay_ms(uint32_t i)

{

    uint32_t temp;

    SysTick->LOAD = SystemCoreClock / 8000* i;

    SysTick->VAL = 0x00;

    // ?????,????

    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

    // ??????

    do {

        temp = SysTick->CTRL;

    } while((temp&0x01)&&!(temp&(1<<16)));

SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       

SysTick->VAL =0X00;  

}

 

 

 

//??stm32??,???

void DH11_GPIO_Init_OUT(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

 

}

 

void DH11_GPIO_Init_IN(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING; //????

GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

 

}

 

void DHT11_Start(void)

{

DH11_GPIO_Init_OUT(); //????

dht11_high; //???

delay_us(30);

dht11_low; //??????18us

delay_ms(20);

dht11_high; //????20~40us

delay_us(30);

DH11_GPIO_Init_IN(); //????

}

 

char DHT11_Rec_Byte(void)

{

unsigned char i = 0;

unsigned char data;

for(i=0;i<8;i++) //1?????1???byte,1???byte?8?bit

{

while( Read_Data == 0); //?1bit??,???????,???????

delay_us(30); //??30us???????0???1,0??26~28us

data <<= 1; //??

if( Read_Data == 1 ) //????30us???????????1

{

data |= 1; //??+1

}

while( Read_Data == 1 ); //???????,???????

}

return data;

}

 

void DHT11_REC_Data(void)

{

unsigned int R_H,R_L,T_H,T_L;

unsigned char RH,RL,TH,TL,CHECK;

DHT11_Start(); //??????

dht11_high; //????

if( Read_Data == 0 ) //??DHT11????

{

while( Read_Data == 0); //???????,???????

while( Read_Data == 1); //???????,???????

R_H = DHT11_Rec_Byte();

R_L = DHT11_Rec_Byte();

T_H = DHT11_Rec_Byte();

T_L = DHT11_Rec_Byte();

CHECK = DHT11_Rec_Byte(); //??5???

dht11_low; //????bit???????,DHT11???? 50us

delay_us(55); //????55us

dht11_high; //??????????????????

if(R_H + R_L + T_H + T_L == CHECK) //??????,??????????????

{

RH = R_H;

RL = R_L;

TH = T_H;

TL = T_L;

}

}

rec_data[0] = RH;

rec_data[1] = RL;

rec_data[2] = TH;

rec_data[3] = TL;

}

最后在主函数中调用DHT11读取数据函数再发送到主机端: