惜今 · 2022年12月09日 · 广东

【GD32F427开发板试用】使用GD32F427RKT6 驱动DHT11

实物
图片.png

一硬件搭建

先根据模块上面的丝印对应接好杜邦线,模块上VDD接3.3V。模块上的GND接开发板上面的GND,DATA引脚接单片机的GPIO脚,我这接了PC9.
图片.png

二、模块熟悉

由于本人很懒,买回来的模块几年了没用过,一直吃灰,感谢极术给的这次测评的机会。让我吃灰的模块不在吃灰。由于买回来没用过。先去嘉立创查一下规格书。先熟悉一下这个器件的一些特性和时序。
图片.png
数据格式
图片.png
图片.png
图片.png
时序图
图片.png
1、从时序图来看,我们只需要先把PC9拉高然后拉低大于18ms然后再拉高,激活模块,就可以等待模块相应低电平,在等待响应高电平,就可以接收40bit数据。然后再拉高PC9引脚就可以读取到湿度和温度数据。
学习一个新的东西的最快方式是看他的使用示例,并且驱动起来
图片.png

三、软件部分

从单总线格式定义的延时来看,我们需妖在GD32F427里面准备ms延时和us延时。

毫秒延时我们直接使用系统已经搭建好的滴答时钟就可以了

/* configure systick */
 systick_config();

us时钟需要我们自己搭建,
在这里我使用通用定时器1来搭建us。
搭建us函数需要用到延时和中断和时钟的分配。

1、熟悉时钟

从GD32F427xx的数据手册,看到最高时钟为200MHz
图片.png

查看一下时钟树,找到time1的时钟路线,从下图看time1的时钟CK_TIMERx
挂在APB1总线上。我们只需要吧CK_TIMERx配置成1MHz就可以了计数器为1.或者1us产生一次中断。
图片.png

我们先看一下整个系统软件配置的时钟是多少。进入startup_gd32f407_427.s汇编启动文件查看一下系统时钟的配置,下面我复制了一部分代码。下面SystemInit就是配置系统时钟的函数。

/* reset Handler */
Reset_Handler   PROC
                EXPORT  Reset_Handler                     [WEAK]
                IMPORT  SystemInit
                IMPORT  __main
                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__main
                BX      R0
                ENDP

时钟的配置,由SystemInit函数调用system_clock_config,system_clock_config执行在调用system_clock_200m_25m_hxtal,最后完成200M时钟的配置,具体的时钟配置函数和内部代码逻辑如下:
下面是SystemInit函数

void SystemInit (void)
{
        /* FPU settings */
    #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
        SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
    #endif
        /* Reset the RCU clock configuration to the default reset state */
        /* Set IRC16MEN bit */
        RCU_CTL |= RCU_CTL_IRC16MEN;
        while(0U == (RCU_CTL & RCU_CTL_IRC16MSTB)){
        }
        RCU_MODIFY(0x50);

        RCU_CFG0 &= ~RCU_CFG0_SCS;

        /* Reset HXTALEN, CKMEN and PLLEN bits */
        RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);

        /* Reset HSEBYP bit */
        RCU_CTL &= ~(RCU_CTL_HXTALBPS);

        /* Reset CFG0 register */
        RCU_CFG0 = 0x00000000U;

        /* wait until IRC16M is selected as system clock */
        while(0 != (RCU_CFG0 & RCU_SCSS_IRC16M)){
        }

        /* Reset PLLCFGR register */
        RCU_PLL = 0x24003010U;

        /* Disable all interrupts */
        RCU_INT = 0x00000000U;

        /* Configure the System clock source, PLL Multiplier and Divider factors, 
            AHB/APBx prescalers and Flash settings */
        system_clock_config();
}

system_clock_config函数:

static void system_clock_config(void)
{
    #ifdef __SYSTEM_CLOCK_IRC16M
        system_clock_16m_irc16m();
    #elif defined (__SYSTEM_CLOCK_HXTAL)
        system_clock_hxtal();
    #elif defined (__SYSTEM_CLOCK_120M_PLL_IRC16M)
        system_clock_120m_irc16m();
    #elif defined (__SYSTEM_CLOCK_120M_PLL_8M_HXTAL)
        system_clock_120m_8m_hxtal();
    #elif defined (__SYSTEM_CLOCK_120M_PLL_25M_HXTAL)
        system_clock_120m_25m_hxtal();
    #elif defined (__SYSTEM_CLOCK_168M_PLL_IRC16M)
        system_clock_168m_irc16m();
    #elif defined (__SYSTEM_CLOCK_168M_PLL_8M_HXTAL)
        system_clock_168m_8m_hxtal();
    #elif defined (__SYSTEM_CLOCK_168M_PLL_25M_HXTAL)
        system_clock_168m_25m_hxtal();
    #elif defined (__SYSTEM_CLOCK_200M_PLL_IRC16M)
        system_clock_200m_irc16m();
    #elif defined (__SYSTEM_CLOCK_200M_PLL_8M_HXTAL)
        system_clock_200m_8m_hxtal();
    #elif defined (__SYSTEM_CLOCK_200M_PLL_25M_HXTAL)
        system_clock_200m_25m_hxtal();
    #elif defined (__SYSTEM_CLOCK_240M_PLL_IRC16M)
        system_clock_240m_irc16m();
    #elif defined (__SYSTEM_CLOCK_240M_PLL_8M_HXTAL)
        system_clock_240m_8m_hxtal();
    #elif defined (__SYSTEM_CLOCK_240M_PLL_25M_HXTAL)
        system_clock_240m_25m_hxtal();
    #endif /* __SYSTEM_CLOCK_IRC16M */   
}

知道了最大时钟,我们可以配置定时器了

void bsp_timer_init(void)
{
    timer_parameter_struct timer_initpara;//定时器结构体
    rcu_periph_clock_enable(RCU_TIMER1);//开启定时器时钟
    //因为APB1是AHB的4分频,因此定时器时钟CK_TIMERx = CK_AHB = 200m
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);//配置定时器时钟等于CK_AHB
    timer_struct_para_init(&timer_initpara);//将定时器结构体内参数配置成默认参数
   timer_deinit(TIMER1); //复位定时器
    
    /* TIMER1 configuration */
    //200M/200/1000 = 1k
    timer_initpara.prescaler         = 5-1;//预分频
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE; //边缘对齐
    timer_initpara.counterdirection  = TIMER_COUNTER_UP; //向上计数方式
    timer_initpara.period            = 40-1; //计数值
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0; //每次溢出都产生更新是件
    timer_init(TIMER1,&timer_initpara);
    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);//使能自动重加载
        timer_interrupt_enable(TIMER1,TIMER_INT_UP);//使能溢出中断
        nvic_irq_enable(TIMER1_IRQn, 0, 1);//配置中断优先级
    /* TIMER1 enable */
    timer_enable(TIMER1);//使能定时器
}

#ifndef __DHT11_H__
#define __DHT11_H__

#include "gd32f4xx.h"
#include "systick.h"
#include "stdbool.h"
#include "time.h"

#define DHT11_HIGH  1
#define DHT11_LOW   0

#define DHT_PORT_RCU    RCU_GPIOC        //时钟
#define DHT_PORT    GPIOC                        //端口
#define DHT_PORT_PIN                                GPIO_PIN_9        //引脚号


typedef struct
{
    uint8_t  humi_int;        //湿度的整数部分
    uint8_t  humi_deci;         //湿度的小数部分
    uint8_t  temp_int;         //温度的整数部分
    uint8_t  temp_deci;         //温度的小数部分
    uint8_t  check_sum;         //校验和
}DHT11_Data_TypeDef;


#define DHT11_DATA_OUT(a)    if (a)    \
                                   gpio_bit_write(DHT_PORT, DHT_PORT_PIN, true);\
                                   else        \
                                  gpio_bit_write(DHT_PORT, DHT_PORT_PIN, false)

#define  DHT11_DATA_IN()      gpio_input_bit_get(DHT_PORT,DHT_PORT_PIN)
                                                                     
#define DHT_VOLTAGE(VOLTAGE) (VOLTAGE == true ?  gpio_bit_write(DHT_PORT, DHT_PORT_PIN, true) : gpio_bit_write(DHT_PORT, DHT_PORT_PIN, false))
    
void DHT11_Mode_Out_PP(void);
uint8_t Read_DHT11(DHT11_Data_TypeDef *DHT11_Data);
#endif


#include "dht11.h"



void DHT11_Mode_Out_PP(void)
{
        /* enable the LEDs GPIO clock */
    rcu_periph_clock_enable(DHT_PORT_RCU);
        /* configure LED1 GPIO port */
    gpio_mode_set(DHT_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, DHT_PORT_PIN);
    gpio_output_options_set(DHT_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, DHT_PORT_PIN);
        
}


void DHT11_Mode_IPU(void)
{
            /* enable the User Key GPIO clock */
        rcu_periph_clock_enable(DHT_PORT_RCU);
    /* configure key pin as input */
    gpio_mode_set(DHT_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, DHT_PORT_PIN);        //上拉输入
}

/* 
 * 从DHT11读取一个字节,MSB先行
 */
static uint8_t Read_Byte(void)
{
    uint8_t i, temp=0;

    for(i=0;i<8;i++)    
    {     
        /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/  
        while(DHT11_DATA_IN()==RESET);

        /*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
         *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 
         */
        delay_1us(40); //延时x us 这个延时需要大于数据0持续的时间即可             

        if(DHT11_DATA_IN()==SET)/* x us后仍为高电平表示数据“1” */
        {
            /* 等待数据1的高电平结束 */
            while(DHT11_DATA_IN()==SET);

            temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行 
        }
        else     // x us后为低电平表示数据“0”
        {               
            temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
        }
    }
    return temp;
}


/*
 * 一次完整的数据传输为40bit,高位先出
 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
 */
uint8_t Read_DHT11(DHT11_Data_TypeDef *DHT11_Data)
{  
  uint16_t count;
    /*输出模式*/
    DHT11_Mode_Out_PP();
    /*主机拉低*/
    DHT11_DATA_OUT(DHT11_LOW);
    /*延时18ms*/
    delay_1ms(20);
//    delay_1us(20000);
    /*总线拉高 主机延时30us*/
    DHT11_DATA_OUT(DHT11_HIGH); 

    delay_1us(30);   //延时30us

    /*主机设为输入 判断从机响应信号*/ 
    DHT11_Mode_IPU();

    /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
    if(DHT11_DATA_IN()==RESET)     
    {
    count=0;
        /*轮询直到从机发出 的80us 低电平 响应信号结束*/  
        while(DHT11_DATA_IN()==RESET)
    {
      count++;
      if(count>1000)  return 0;
      delay_1us(10); 
    }    
    
    count=0;
        /*轮询直到从机发出的 80us 高电平 标置信号结束*/
        while(DHT11_DATA_IN()==SET)
    {
      count++;
      if(count>1000)  return 0;
      delay_1us(10); 
    }  
        /*开始接收数据*/   
        DHT11_Data->humi_int= Read_Byte();

        DHT11_Data->humi_deci= Read_Byte();

        DHT11_Data->temp_int= Read_Byte();

        DHT11_Data->temp_deci= Read_Byte();

        DHT11_Data->check_sum= Read_Byte();

        /*读取结束,引脚改为输出模式*/
        DHT11_Mode_Out_PP();
        /*主机拉高*/
        DHT11_DATA_OUT(DHT11_HIGH);

        /*检查读取的数据是否正确*/
        if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
            return 1;
        else 
            return 0;
    }
    else
    {        
        return 0;
    }   
}

执行函数

/*!
    \file    main.c
    \brief   GPIO running led demo

    \version 2020-09-04, V2.0.0, demo for GD32F4xx
*/

/*
    Copyright (c) 2020, GigaDevice Semiconductor Inc.

    Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.
    3. Neither the name of the copyright holder nor the names of its contributors
       may be used to endorse or promote products derived from this software without
       specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#include "gd32f4xx.h"
#include "gd32f427r_start.h"
#include "systick.h"
#include <stdio.h>
#include "dht11.h"
#include "time.h"
#include "drv_usb_hw.h"
#include "usbd_msc_core.h"

DHT11_Data_TypeDef DHT11_Data;

usb_core_driver msc_udisk;

unsigned char SRAM[40 * 1024];

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
    /* configure systick */
    systick_config();

    /* enable the LEDs GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOC);

    /* configure LED1 GPIO port */
    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    /* reset LED1 GPIO pin */
    gpio_bit_reset(GPIOC, GPIO_PIN_6);
        bsp_timer_init();
        DHT11_Mode_Out_PP();
    
        /* configure the GPIO */
    usb_gpio_config();
    /* configure the USB peripheral clock */
    usb_rcu_config();
    /* initialize the USB timer */
    usb_timer_init();

        usbd_init(&msc_udisk,
        #ifdef USE_USB_FS
                                    USB_CORE_ENUM_FS,
        #elif defined(USE_USB_HS)
                                    USB_CORE_ENUM_HS,
        #endif
                                    &msc_desc,
                                    &msc_class);
                usb_intr_config();
    while(1) {
        /* turn on LED1 */
        gpio_bit_set(GPIOC, GPIO_PIN_6);
        delay_1ms(1000);

        /* turn off LED1 */
        gpio_bit_reset(GPIOC, GPIO_PIN_6);
        delay_1ms(1000);
                if( Read_DHT11 ( & DHT11_Data ) == SUCCESS)
                {
                    printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\
                    DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);
                }    
                else{
                    delay_1ms(1);
                }
    }
}
推荐阅读
关注数
10708
内容数
187
中国高性能通用微控制器领域的领跑者兆易创新GD系列芯片技术专栏。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息