基于STM32F1系列与阿里云的物联网节点

一、硬件准备
一块STM32F1系列的核心板或开发板

DHT11温湿度传感器一个

BH1750光照强度传感器一个

ESP8266-01S模块一个

TFT彩屏一个(SPI接口)

二、软件准备
准备阿里云物联网平台的开发

准备阿里云物联网Web可视化页面的开发

以及Keil5开发环境和串口调式助手

三、阿里云物联网平台
笔者在写过一篇关于阿里云平台的文章,传送门如下:

(2条消息) ESP8266-01S+MQTT+阿里云数据传输_二筒rrrr的博客-CSDN博客

四、STM32的开发
我们需要准备DHT11传感器与BH1750传感器的代码,废话不多说直接上代码

DHT11.c

 
 
#include "bsp_DHT11.h"
 
 
 
xDHT11_TypeDef       xDHT11;             // 声明全局结构体, 用于记录信息
 
 
 
static GPIO_TypeDef *DHT11_GPIOx = 0;    // 引脚端口
static uint32_t      DHT11_PINx  = 0;    // 引脚编号
 
 
 
#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)             // 读取引脚的电平
 
 
 
#if DELAY_MODE_TIM2
#include "stm32f10x_tim.h"
#include "misc.h"
// 定时器初始化配置
// 不使用中断
void Tim2_Init(uint16_t psc, uint16_t arr, FunctionalState NewState)
{
    TIM_TimeBaseInitTypeDef tim;                         // 结构体
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 开启TIM时钟
    TIM_DeInit(TIM2);
    tim.TIM_ClockDivision = TIM_CKD_DIV1;                // 采样分频
    tim.TIM_CounterMode   = TIM_CounterMode_Up;          // 向上计数
    tim.TIM_Period        = arr;                         // 自动重装载寄存器的值
    tim.TIM_Prescaler     = psc - 1;                     // 时钟预分频
    TIM_TimeBaseInit(TIM2, &tim);                        // 初始化结构体
    TIM2->SR = (uint16_t)~((uint16_t)0x01);              // 清除更新标志 
    TIM2->CNT = 0;                                       // 清0计数器
    TIM_Cmd(TIM2, NewState);                             // 是否开始工作
}
 
 
 
static void delay_us(uint32_t us)
{
    Tim2_Init(72, us, ENABLE);
    while ((TIM2->SR & 1) == 0);
    TIM_Cmd(TIM2, DISABLE);
}
 
 
 
static void delay_ms(uint32_t ms)
{
    for (uint16_t i = 0; i < ms; i++)
    {
        Tim2_Init(72, 990, ENABLE);
        while ((TIM2->SR & 1) == 0);
        TIM_Cmd(TIM2, DISABLE);
    }
}
#else
 
// 本地US粗略延时函数,减少移植时对外部文件依赖;
static void delay_us(uint32_t us)
{
    uint16_t i = 0;
    while (us--)
    {
        i = 7;
        while (i--);
    }
}
 
// 本地MS粗略延时函数,减少移植时对外部文件依赖;
static void delay_ms(uint32_t ms)
{
    uint32_t i = 0;
    while (ms--)
    {
        i = 12000;
        while (i--);
    }
}
#endif // 结束延时
 
 
 
static void DHT11_Mode_IPU(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
 
    GPIO_InitStructure.GPIO_Pin = DHT11_PINx ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_GPIOx, &GPIO_InitStructure);
}
 
static void DHT11_Mode_Out_PP(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PINx ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_GPIOx, &GPIO_InitStructure);
}
 
static 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_Out_PP();
    DHT11_BUS_HIGH;
}
 
 
// 从DHT11读取一个字节,MSB先行
// 1:每bit以50us低电平标置开始,
// 2:以26~28us的高电平表示“0”
// 3:以70us高电平表示“1”
// 4: 通过检测从高电平开后 x us后的电平即可区别这两个状态
static uint8_t readByte(void)
{
    uint8_t  temp = 0;
 
    for (uint8_t i = 0; i < 8; i++)
    {
        while (DHT11_BUS_READ == 0);             // 每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束
        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;
}
 
 
/******************************************************************************
 * 函  数: DHT11_GetData
 * 功  能: 从DHT11中读取数据
 * 参  数: 1:
 * 说  明: 调用后,获取到的数据,保存到结构体xDHT11中
 *          温度值:xDHT11.Temperature (有效范围:0~50℃)
 *          湿度值: xDHT11.Humidity    (有效范围:20%~90%)
 * 返回值: 0-失败; 1-正常
 ******************************************************************************/
uint8_t DHT11_GetData(GPIO_TypeDef *GPIOx, uint32_t PINx)
{
    static uint8_t humiInt = 0;        // 湿度的整数部分
    static uint8_t humiDec = 0;        // 湿度的小数部分
    static uint8_t TempInt = 0;        // 温度的整数部分
    static uint8_t TempDec = 0;        // 温度的小数部分
    static uint8_t sum = 0;            // 校验和
 
    DHT11_Init(GPIOx, PINx);
 
    // 1:主机产生开始信号
    DHT11_Mode_Out_PP();               // 输出模式
    DHT11_BUS_LOW;                     // 主机拉低电平
    delay_ms(25);                      // 延时18~30ms
    // 2:主机拉高等待
    DHT11_BUS_HIGH;                    // 总线拉高
    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
        // 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
    }
    return ERROR;                      // 通信错误,返回:ERROR=0
}
 
 
 
 
/******************************************************************************
 * 函  数: DHT11_GetTemperature
 * 功  能: 从DHT11中读取温度值
 * 参  数: GPIO_TypeDef *GPIOx: GPIO端口号,取值范围:GPIOA ~ GPIOG
 *          uint32_t     PINx  : 引脚编号,  取值范围:GPIO_Pin_0 ~ GPIO_Pin_15
 * 说  明: 温度值有效范围:0~50℃; 精度±2°C; 小数部份无效
 * 返回值: 0-失败,非0值-湿度值
 ******************************************************************************/
float DHT11_GetTemperature(GPIO_TypeDef *GPIOx, uint32_t PINx)

    DHT11_GetData(GPIOx, PINx);
    return xDHT11.Temperature;
}
 
 
/******************************************************************************
 * 函  数: DHT11_GetHumidity
 * 功  能: 从DHT11中读取湿度值
 * 参  数: GPIO_TypeDef *GPIOx: GPIO端口号,取值范围:GPIOA ~ GPIOG
 *          uint32_t     PINx  : 引脚编号,  取值范围:GPIO_Pin_0 ~ GPIO_Pin_15
 * 说  明: 湿度值有效范围:20%~90%; 精度±5%; 小数部分无效
 * 返回值: 0-失败,非0值-湿度值
 ******************************************************************************/
float DHT11_GetHumidity(GPIO_TypeDef *GPIOx, uint32_t PINx)
{  
    DHT11_GetData(GPIOx, PINx);
    return xDHT11.Humidity;
}

DHT11.h

#ifndef __BSP_DHT11_H
#define __BSP_DHT11_H
/***********************************************************************************************************************************
 **【文件名称】  bsp_DHT11.h
 **【功能测试】  DHT11-温湿度获取
 **【购买链接】  魔女科技   https://demoboard.taobao.com
 **【更新分享】  Q群文件夹
 ***********************************************************************************************************************************
 **【文件名称】  bsp_DHT11.h
 **【功能描述】  定义引脚、定义全局结构体、声明全局函数
 **
 **【适用平台】  STM32F103 + 标准库v3.5 + keil5
 **
 **【实验操作】  1-模块接线,VCC  接 3.3V 或 5V
 **                          DATA 接 PC3
 **                          GND  接 GND
 **
 **【划 重 点】  1-电压范围:3.3V~5.0V,工作电流:0.2~1mA; 待机电流:150uA
 **              2-DHT11反应有点慢:上电后1S后进入稳定状态; 
 **              3-采样周期,不同店家的DHT11,参数不同,一般是1次/秒, 有部分可以数百毫秒间隔采集一次; 当小于采样周期进行重复采集数据,会发生错误;
 **              5-DHT11时序要求较高,us级别,当使用delay粗略延时,同一份代码同一个模块,在不同keil里跑也可能发生错误(代码优化设置对while延时的影响)。建议使用TIM的精准延时;
 **              4-注意判断使用的DHT11器件,是单独的DHT11元件,还是完整的DHT11模块
 **                单独的DHT11元件:蓝色塑料主体、三个引脚,不带上拉电阻;适合低成本工程方案使用,建议测试时引脚使用推挽输出模式;
 **                完整的DTH11模块:蓝色塑料主体、三个引脚、PCB底板、上拉电阻、电源指示LED,适合方案搭建;
 **              6-精度误差:DHT11分旧版和新版,10元内的绝大部分是旧版,新版约12元左右;
 **                          旧版数据稳定,但只有整数部分,新版有小数部分,但数据跳动稍大;
 **                          温度0-50°C; 精度±2°C; 小数部份无效
 **                          湿度20-80%; 精度±5%; 小数部分无效
 **
 **
 **【文件移植】  步骤1-复制文件:可复制bsp_DHT11.c和bsp_DHT11h两个文件,或复制DHT11文件夹,保存到所需工程目录文件夹下;
 **              步骤2-添加文件:在keil工各程左侧文件管理器中,双击某文件夹,以添加bsp_DHT11.c文件;
 **              步骤3-添加路径:点击魔术棒工具按钮,在“c/c++"选项页中,点击”Include Path"后面的按键,以添加文件存放所在路径(是文件夹,不是文件);
 **         步骤4-添加引脚:在所需DHT11功能的代码文件头部,添加:#include "bsp_DHT11.h";
 **
 **
 **【函数使用】  函数2-DHT11_GetData(GPIOx, GPIO_Pin_x);           // 获取数据, 获取的数据存放于结构体xDHT11中,具体使用方法,可参考示例代码
 **
 **
 **【更新记录】  2022-05-13  增加TIM2精准延时,以便于移植
 **              2021-12-23  修改初始化函数,使文件移植更方便: DHT11_Init(GPIOx, GPIO_Pin_x);
 **              2021-10-26  修改c和h文件文件格式
 **              2021-10-26  修改DHT11_GetData()函数
 **              2021-05-20  创建文件
 **
***********************************************************************************************************************************/
#include <stm32f10x.h>
#include <stdio.h>
 
 
 
/*****************************************************************************
 ** 移植修改
****************************************************************************/
#define   DELAY_MODE_TIM2   1     // 通信时序中的延时方式:0_使用while方式的粗略延时; 1_使用TIM2进行计时; 注意计时器是否和其它设备有冲突
                                  // 0_使用粗略延时:注意本代码是在勾选下列参数状态下调试的:Options / C++ /One ELF Section per Function ;
                                  // 1_使用TIM2计时:首选推荐; 注意同一工程中是否有其它功能使用TIM2,以免发生冲突;
 
 
 
/*****************************************************************************
 ** 声明 全局变量
****************************************************************************/
typedef struct
{
    float          Temperature;   // 温度值:在调用DHT11_GetTemp()函数后,获取到的温度值;
    float          Humidity;      // 湿度值:在调用DHT11_GetTemp()函数后,获取到的温度值;
} xDHT11_TypeDef;
extern xDHT11_TypeDef  xDHT11;    // 声明全局结构体, 用于存放读取的结果值
 
 
 
/*****************************************************************************
 ** 声明  全局函数
****************************************************************************/
uint8_t DHT11_GetData(GPIO_TypeDef *GPIOx, uint32_t PINx);
float DHT11_GetTemperature(GPIO_TypeDef *GPIOx, uint32_t PINx);
float DHT11_GetHumidity(GPIO_TypeDef *GPIOx, uint32_t PINx);
 
#endif 
 
 
 
 

BH1750.C

/**************************************************************************
 * 文件名:bh1750.c
 * 功 能 :光强度传感模块
 *
 * 作 者 :魔女科技团队
****************************************************************************/
#include "bsp_BH1750.h"
 
#define SDA_1   (BH1750_SDA_GPIOx -> BSRR = BH1750_SDA_PINx)
#define SDA_0   (BH1750_SDA_GPIOx -> BRR  = BH1750_SDA_PINx)
 
#define SCL_1   (BH1750_SCL_GPIOx -> BSRR = BH1750_SCL_PINx)
#define SCL_0   (BH1750_SCL_GPIOx -> BRR  = BH1750_SCL_PINx)
 
 
 
 
static uint8_t   Buffer[8];       // 接收数据缓存区
 
 
 
 
static void delay_us(uint32_t times)
{
    times = times * 7;   //  10us内用7;
    while (--times)
        __nop();
}
 
 
static void delay_ms(uint32_t ms)
{
    ms = ms * 6500;
    for (u32 i = 0; i < ms; i++); // 72MHz系统时钟下,多少个空循环约耗时1ms
}
 
 
 
 
// 产生IIC开始信号
void BH1750_Start()
{
    SDA_1 ;              // 拉高数据线
    SCL_1;               // 拉高时钟线
    delay_us(5);         // 延时
    SDA_0;               // 产生下降沿
    delay_us(5);         // 延时
    SCL_0;               // 拉低时钟线
}
 
// 产生IIC停止信号
void BH1750_Stop()
{
    SDA_0;               // 拉低数据线
    SCL_1;               // 拉高时钟线
    delay_us(5);         // 延时
    SDA_1;               // 产生上升沿
    delay_us(5);         // 延时
}
 
// 发送应答信号, 参数:ack (0:ACK 1:NAK)
static void BH1750_SendACK(uint8_t ack)
{
    GPIO_InitTypeDef GPIO_InitStruct;
 
    GPIO_InitStruct.GPIO_Pin   = BH1750_SDA_PINx ;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
 
    if (ack == 1)    // 写应答信号
        SDA_1;
    else 
        SDA_0;
 
    SCL_1;           // 拉高时钟线
    delay_us(5);     // 延时
    SCL_0;           // 拉低时钟线
    delay_us(5);     // 延时
}
 
// 接收应答信号
static uint8_t BH1750_RecvACK()
{
    uint8_t value = 0;
    GPIO_InitTypeDef GPIO_InitStruct;
 
    GPIO_InitStruct.GPIO_Pin = BH1750_SDA_PINx ;    // 配置为输入模式
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;      // 上拉输入
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
 
    SCL_1;                                          // 拉高时钟线
    delay_us(5);                                    // 延时
    if (BH1750_SDA_GPIOx->IDR & BH1750_SDA_PINx)    // 读应答信号  (GPIO_ReadInputDataBit(GPIOA, sda) == 1)
        value = 1 ;
    else
        value = 0 ;
    SCL_0;                                          // 拉低时钟线
    delay_us(5);                                    // 延时
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;   // 配置为输出模式
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
    return value;
}
 
// 向IIC总线发送一个字节数据
static void BH1750_SendByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)     //8位计数器
    {
        if (0X80 & data)
            SDA_1;
        else
            SDA_0;
        data <<= 1;
        SCL_1;             // 拉高时钟线
        delay_us(5);       // 延时
        SCL_0;             // 拉低时钟线
        delay_us(5);       // 延时
    }
    BH1750_RecvACK();      // 接收应答信号
}
 
// 向IIC总线接收一个字节
static uint8_t BH1750_RecvByte()
{
    uint8_t i;
    uint8_t dat = 0;
    uint8_t bit;
 
    GPIO_InitTypeDef GPIO_InitStruct;              
    GPIO_InitStruct.GPIO_Pin = BH1750_SDA_PINx ;     // 配置为输入模式
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;       // 上拉输入 
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
 
    SDA_1;                                           // 上拉,准备读取数据,
    for (i = 0; i < 8; i++)                          // 8位计数器
    {
        dat <<= 1;
        SCL_1;                                       // 拉高时钟线
        delay_us(5);                                 // 延时
 
        if (BH1750_SDA_GPIOx->IDR & BH1750_SDA_PINx) 
            bit = 0X01;
        else
            bit = 0x00;
        dat |= bit;                                  // 读数据
        SCL_0;                                       // 拉低时钟线
        delay_us(5);                                 // 延时
    }
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;    // 配置为输出模式
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
    return dat;
}
 
static void BH1750_WriteOrder(uint8_t REG_Address)
{
    BH1750_Start();                  //起始信号
    BH1750_SendByte(BH1750_ADDR);    //发送设备地址+写信号
    BH1750_SendByte(REG_Address);    //内部寄存器地址,请参考中文pdf22页
//  BH1750_SendByte(REG_data);       //内部寄存器数据,请参考中文pdf22页
    BH1750_Stop();                   //发送停止信号
}
 
//初始化BH1750,根据需要请参考pdf进行修改
void BH1750_Init()
{
    /*** 特别地说明:引脚需要在h文件中修改 ***/
        
    GPIO_InitTypeDef GPIO_InitStruct;
    // 使能SDA引脚端口时钟
    if (BH1750_SDA_GPIOx == GPIOA)  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    if (BH1750_SDA_GPIOx == GPIOB)  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    if (BH1750_SDA_GPIOx == GPIOC)  RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    if (BH1750_SDA_GPIOx == GPIOD)  RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
    if (BH1750_SDA_GPIOx == GPIOE)  RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
    if (BH1750_SDA_GPIOx == GPIOF)  RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
    if (BH1750_SDA_GPIOx == GPIOG)  RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
    // 使能SCL引脚端口时钟
    if (BH1750_SCL_GPIOx == GPIOA)  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    if (BH1750_SCL_GPIOx == GPIOB)  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    if (BH1750_SCL_GPIOx == GPIOC)  RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    if (BH1750_SCL_GPIOx == GPIOD)  RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
    if (BH1750_SCL_GPIOx == GPIOE)  RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
    if (BH1750_SCL_GPIOx == GPIOF)  RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
    if (BH1750_SCL_GPIOx == GPIOG)  RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
 
    // 配置SDA引脚工作模式
    GPIO_InitStruct.GPIO_Pin   = BH1750_SDA_PINx;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_GPIOx, &GPIO_InitStruct);
    // 配置SCL引脚工作模式
    GPIO_InitStruct.GPIO_Pin   = BH1750_SCL_PINx;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SCL_GPIOx, &GPIO_InitStruct);
 
    BH1750_WriteOrder(0x01);
    delay_ms(180);                // 延时180ms
}
 
//连续读出BH1750内部数据
static void BH1750_ReadData(void)
{
    BH1750_Start();                        // 起始信号
    BH1750_SendByte(BH1750_ADDR + 1);      // 发送设备地址+读信号
 
    for (uint8_t i = 0; i < 3; i++)        // 连续读取6个地址数据,存储buffer中
    {
        Buffer[i] = BH1750_RecvByte();     // buffer[0]存储0x32地址中的数据
        if (i == 3)
        {
            BH1750_SendACK(1);             // 最后一个数据需要回NOACK
        }
        else
        {
            BH1750_SendACK(0);             // 回应ACK
        }
    }
    BH1750_Stop();                         // 停止信号
    delay_ms(5);
}
 
 
 
float BH1750_GetData(void)
{
    uint32_t dataTem;                      // 结果值
    static uint8_t flagInit = 0;
    if(flagInit==0)
    {
        BH1750_WriteOrder(0x01);           // power on
        BH1750_WriteOrder(0x10);           // H- resolution mode
        delay_ms(180);                     // 延时180ms
        flagInit=1;
    }        
    BH1750_ReadData();                     // 读取原始数据,存储在Buffer中
    dataTem = Buffer[0];
    dataTem = (dataTem << 8) + Buffer[1];  // 转换成结果光强度值
    return dataTem / 1.2;
}

BH1750.h

#ifndef __BSP_BH1750_H__
#define __BSP_BH1750_H__
#include <stm32f10x.h>
#include <stdio.h>
 
 
 
//移植参数区 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// SDA
#define    BH1750_SDA_GPIOx   GPIOB         // 模拟IIC
#define    BH1750_SDA_PINx    GPIO_Pin_0
// SCL
#define    BH1750_SCL_GPIOx   GPIOB         // 模拟IIC
#define    BH1750_SCL_PINx    GPIO_Pin_1
// IIC器件地址
#define    BH1750_ADDR   0x46               // 定义器件在IIC总线中的从地址, 根据ADDR引脚不同修改:当ADDR引脚接GND或空置时地址为0x46,接3.3V时地址为0xB8
//END 移植 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 
 
/*****************************************************************************
 ** 声明  全局函数
****************************************************************************/
void     BH1750_Init(void);           // 初始化BH1750
float    BH1750_GetData(void);                                      // 读取BH1750数据
 
 
 
 
#endif
 
 

主函数代码

/**==================================================================================================================
 **【文件名称】  main.c
 **【功能测试】  DHT11-温湿度获取
 **==================================================================================================================
 **【适用平台】 STM32F103 + KEIL5.27 + 标准库v3.5 + DHT11
 **
 **【实验操作】  1-模块接线,VCC  接 3.3V 或 5V
 **                          DATA 接 PC3
 **                          GND  接 GND
 **              2-烧录代码至开发板,并重新上电或复位,使新程序运行;
 **              3-打开电脑串口上位机,115200-N-8-1, 即可观察到采集的数据;
 **              4-注意:DHT11的上电稳定时间约:1~6秒,采集间隔时长约: 0.5~2秒, 不同厂家不同芯片,有所差异;
 **
 **【划 重 点】  1-电压范围:3.3V~5.0V,工作电流:0.2~1mA; 待机电流:150uA
 **              2-DHT11反应有点慢:上电后1S后进入稳定状态;
 **              3-采样周期,不同店家的DHT11,参数不同,一般是1次/秒, 有部分可以数百毫秒间隔采集一次; 当小于采样周期进行重复采集数据,会发生错误;
 **              5-DHT11时序要求较高,us级别,当使用delay粗略延时,同一份代码同一个模块,在不同keil里跑也可能发生错误(代码优化设置对while延时的影响)。建议使用TIM的精准延时;
 **              4-注意判断使用的DHT11器件,是单独的DHT11元件,还是完整的DHT11模块
 **                单独的DHT11元件:蓝色塑料主体、三个引脚,不带上拉电阻;适合低成本工程方案使用,建议测试时引脚使用推挽输出模式;
 **                完整的DTH11模块:蓝色塑料主体、三个引脚、PCB底板、上拉电阻、电源指示LED,适合方案搭建;
 **              6-精度误差:DHT11分旧版和新版,10元内的绝大部分是旧版,新版约12元左右;
 **                          旧版数据稳定,但只有整数部分,新版有小数部分,但数据跳动稍大;
 **                          温度0-50°C; 精度±2°C; 小数部份无效
 **                          湿度20-80%; 精度±5%; 小数部分无效
 **
 **
 **【文件移植】  步骤1-复制文件:可复制bsp_DHT11.c和bsp_DHT11h两个文件,或复制DHT11文件夹,保存到所需工程目录文件夹下;
 **              步骤2-添加文件:在keil工各程左侧文件管理器中,双击某文件夹,以添加bsp_DHT11.c文件;
 **              步骤3-添加路径:点击魔术棒工具按钮,在“c/c++"选项页中,点击”Include Path"后面的按键,以添加文件存放所在路径(是文件夹,不是文件);
 **         步骤4-添加引脚:在所需DHT11功能的代码文件头部,添加:#include "bsp_DHT11.h";
 **
 **
 **【函数使用】  函数2-DHT11_GetData(GPIOx, GPIO_Pin_x);           // 获取数据, 获取的数据存放于结构体xDHT11中,具体使用方法,可参考示例代码
 **
 **
 **【备注说明】  代码版权归魔女科技所有,请勿商用,谢谢!
 **              https://demoboard.taobao.com
====================================================================================================================*/
#include <stm32f10x.h>            // 头文件引用(标准库); 内核、芯片外设....;(stm32f10x.conf.h, 对标准库头文件进行调用)     
#include "stm32f10x_conf.h"       // 头文件引用(标准库); 内核、芯片外设....;(stm32f10x.conf.h, 对标准库头文件进行调用) 
#include "stm32f10x_tim.h"
#include "bsp_led.h"              // LED指示灯
#include "bsp_usart.h"            // USART1、2、3,UART4、5
#include "bsp_lcd_ST7735.h"       // 1.8寸显示屏驱动 
#include "bsp_DHT11.h"            // DHT11
#include "bsp_BH1750.h"
#include "stdio.h"
#include "Timer.h"
 
 
 
char   strTem[100];               // 用于临时存储字符串
uint16_t Temperature=0;
uint16_t Humidity=0;
float Light=0.0;
char Temp_aly[300];        
 
 
 
// ms延时函数,减少移植时对外部文件依赖;
static void delay_ms(u32 ms)
{
    ms = ms * 6500;
    for (u32 i = 0; i < ms; i++); // 72MHz系统时钟下,多少个空循环约耗时1ms
}
 
 
 
// 主函数
int main(void)
{
    USART1_Init(115200);                                        // 串口初始化:USART1(115200-N-8-1), 且工程已把printf重定向至USART1输出
    USART2_Init(115200);
      BH1750_Init();
    Led_Init();                                                 // LED 初始化
    LED_RED_ON;                                                 // 点亮红灯   
    LCD_Init();                                                // 初始化显示屏-驱动芯片ST7735
    Timer_Init();
    
    
      USART2_SendString("AT+RST\r\n");
      delay_ms(8000);    
    
        USART2_SendString("AT+CWMODE=3\r\n");
        delay_ms(8000);
      printf("%s\r\n",xUSART.USART2ReceivedBuffer);        
 
      USART2_SendString("AT+CWJAP=\"PCG1\",\"862139..\"\r\n");
        delay_ms(8000);
      printf("%s\r\n",xUSART.USART2ReceivedBuffer);
 
      USART2_SendString("AT+MQTTUSERCFG=0,1,\"NULL\",\"ESP8266_Room&a1F60XocNHb\",\"3BD08EC181CD30E09BB01C6B8E4FA0ACB63EF07F\",0,0,\"\"\r\n");
        delay_ms(5000);
      printf("%s\r\n",xUSART.USART2ReceivedBuffer);                    
      USART2_SendString("AT+MQTTCLIENTID=0,\"002211|securemode=3\\,signmethod=hmacsha1\\,timestamp=112200|\"\r\n");
        delay_ms(5000);
      printf("%s\r\n",xUSART.USART2ReceivedBuffer);                
        
      USART2_SendString("AT+MQTTCONN=0,\"a1F60XocNHb.iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883,1\r\n");
        delay_ms(10000);
      printf("%s\r\n",xUSART.USART2ReceivedBuffer);        
        
    USART2_SendString("ATE0\r\n");
        delay_ms(2000);
    
    LCD_Chinese16ForFile(25,34,5,BLUE,BLACK);
    LCD_Chinese16ForFile(25+16,34,7,BLUE,BLACK);
 
    
      LCD_Chinese16ForFile(25,50,6,BLUE,BLACK);
    LCD_Chinese16ForFile(25+16,50,7,BLUE,BLACK);
    
      LCD_Chinese16ForFile(25,70,8,BLUE,BLACK);
    LCD_Chinese16ForFile(25+16,70,9,BLUE,BLACK);    
      LCD_Chinese16ForFile(25+2*16,70,10,BLUE,BLACK);
    LCD_Chinese16ForFile(25+3*16,70,7,BLUE,BLACK);        
 
 
 
        if(xUSART.USART2InitFlag)
            {
                xUSART.USART2InitFlag = 0;
                memset(xUSART.USART2ReceivedBuffer,0,xUSART.USART2ReceivedNum);
                xUSART.USART2ReceivedNum = 0;
            }            
            
            
    while (1)                                                   // while函数死循环,不能让main函数运行结束,否则会产生硬件错误
    {
            Temperature=DHT11_GetTemperature(GPIOE,GPIO_Pin_3);
            Humidity=DHT11_GetHumidity(GPIOE,GPIO_Pin_3);
            Light=BH1750_GetData();
            
            sprintf(strTem,":%d^C",Temperature);
            LCD_String(25+16*2,34,strTem,16,BLUE,BLACK);
 
            sprintf(strTem,":%d%%",Humidity);
            LCD_String(25+16*2,50,strTem,16,BLUE,BLACK);        
    
            sprintf(strTem,":%.0fLux   ",Light);
            LCD_String(25+16*4,70,strTem,16,BLUE,BLACK);                
            printf("当前温度为:%d,当前湿度为:%d,当前光照强度为:%.0f\r\n",Temperature,Humidity,Light);
      
            sprintf(Temp_aly,"AT+MQTTPUB=0,\"/sys/a1F60XocNHb/ESP8266_Room/thing/event/property/post\",\"{\\\"method\\\":\\\"thing.service.property.set\\\"\\\,\\\"id\\\":\\\"2012934117\\\"\\\,\\\"params\\\":{\\\"TEMP\\\":%d\\\,\\\"Light\\\":%d\\\,\\\"Huit\\\":%d}\\\,\\\"version\\\":\\\"1.0.0\\\"}\",1,0\r\n",Temperature,Humidity,(uint16_t)(Light));
            USART2_SendString(Temp_aly);
      delay_ms(300);
    }
}
 
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
 
        LED_RED_TOGGLE;
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);        
    }
}
 
// 注意:每个代码文件的末尾,要保留一个空行

在主函数中一定要特别注意字符串的处理

TFT彩屏的驱动代码这里就不作展示了,在网上都能随便找到

五、实物演示
阿里云界面


TFT彩屏页面展示


至此所有工作全部完成,实现预期效果

在此特别感谢淘宝魔女开发板团队的代码,可移植性很强
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值