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读取数据函数再发送到主机端:

