STM32学习笔记(一)微项目 使用STM32F103实现温湿度的采集

项目描述:DHT11温湿度传感器采集温湿度数据分别显示在LED屏幕和通过串口打印出来。

外设:DHT11温湿度传感器、CH340 USB转TTL、OLED0.96 显示屏。

控制芯片:STM32F103C8T6核心板。

集成环境:keil5

 一、DHT11传感器采集温度的原理

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。
以上就是我们通常使用接线方式,stm32和dht11进行数据通信和同步,就是通过DATA串行接口(单线双向)。
知道了相应的数据格式,我们等下在分析数据的时候,就容易处理很多.
接下来,就是不可避免的要理解它们通讯的时序图,只有理解之后,我们才能写好代码。废话不多说 直接上图。
第一阶段:主机至少拉低18ms。
第二阶段:主机拉高20-40us。
一二阶段  stm32f103 的GPIO 应该是处于输出模式。
第三阶段:DHT开始拉低电平回应,这个过程持续80us左右,不一定就是80us整整。
第四阶段:DHT拉高电平 持续80us。
接下来就是开始传输40bits的数据。
一二阶段  stm32f103 的GPIO 应该是处于输入模式。
过程大概就是这样子。在这个过程时, 要特别注意的GPIO 输入输出模式的改变。
过程就类似于回合制游戏一样 我打你一下,你打我一下,还是挺好理解的。
知道原理之后 直接开始写代码。
1、配置输入输出模式。
GPIO_InitTypeDef 			 	GPIO_InitStructure;         //GPIO结构体														
void DHT11_OutputMode(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	    //开启GPIOB的时钟
    //GPIO Configure
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		//GPIO模式,赋值为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	            //GPIO引脚,使能PB12引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//GPIO速度,赋值为50MHz
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将赋值后的构体变量传递给GPIO_Init函数
															//函数内部会自动根据结构体的参数配置相应寄存器											
}
void DHT11_InputMode(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	    //开启GPIOB的时钟
    //GPIO Configure
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		    //GPIO模式,赋值为输入浮空模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	            //GPIO引脚,使能PB12引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//GPIO速度,赋值为50MHz
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将赋值后的构体变量传递给GPIO_Init函数														
}
2、开始通讯
uint32_t DHT11_Start(void)
{
    uint32_t i = 0;

    //开启输出模式
		DHT11_OutputMode();
    //给PB12引脚赋值 主机拉低
	  GPIO_ResetBits(GPIOB,GPIO_Pin_12);
    //等待20ms
    Delay_ms(20);
    //给PB12引脚赋值 主机拉高
    GPIO_SetBits(GPIOB,GPIO_Pin_12);
    //等待30us
    Delay_us(30);

    //开启输入模式
    DHT11_InputMode();
		
    i = 0;
    while(i < 100)
    {
		//检测PB12引脚的低电平		
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)== 0)
        break;
       
        Delay_us(1);
        i++;
    }
		
    //检测DHT11响应是否超时
    if(i >= 100)
    {
        return 1;
    }
		//Led_Disp(1,0);      //led1亮
		
    i = 0;
    while(i <= 100)
    {
        //检测PB12引脚的高电平		
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) == 1)
				break;
        Delay_us(1);
        i++;
    }
		
    //检测DHT11响应是否超时
    if(i >= 100)
    {
        return 1;
    }
		
		
    i = 0;
    while(i < 100)
    {
		//检测PB12引脚的低电平		
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)== 0)
        break;
       
        Delay_us(1);
        i++;
    }
		
    //检测DHT11响应是否超时
    if(i >= 100)
    {
        return 1;
    }
		
    //响应成功
    return 0;

}
3、响应成功之后,开始接受数据。
uint8_t dht11_read_byte(void)
{
	uint8_t d=0;
	
	uint32_t i=0;
	
	//?????????
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==1);
	
	for(i=0; i<8; i++)
	{
		//?????
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==0);
		
		//??40us
		Delay_us(40);
		
		//??PG9?????,???????bit1
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==1)
		{
			d|=1<<(7-i);
			
			//?????????
			while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==1);		
		}
	}
		
	return d;
}

uint32_t dht11_read_data(uint8_t *pbuf)
{
	uint32_t i=0;
	uint8_t  check_sum=0;
	
	//唤醒DHT11
	while(DHT11_Start()==1);
	
	//读取数据
	for(i=0; i<5; i++)
	{
		pbuf[i] = dht11_read_byte();
	
	}

	//????????
	check_sum = pbuf[0]+pbuf[1]+pbuf[2]+pbuf[3];
	
	//校验和
	if(check_sum != pbuf[4])
		return 1;
	
	return 0;
}

不过,写代码的时候,一定要确保自己的延时函数一定要精确,后续发生数据读取不出来的问题很多都是因为延时函数不准确。

二、串口通信

这个我讲起来就很多啦,我们直接上链接

STM32-串口通信(串口的接收和发送)_stm32串口接收数据-CSDN博客

这个博主就讲得差不多。约定好一定的波特率
其实就是对USART的配置、数据传输函数、USART中断服务函数。
下面就是一个简单的示例:
void Usart_Init(void)
{
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟

    /*GPIOA Configue*/
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//PA.9
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出50HZ
		GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;//波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//不使用校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流
    USART_InitStructure.USART_Mode = USART_Mode_Tx;//Tx_modes
    USART_Init(USART1, &USART_InitStructure);//将结构体变量交给USART_Init,配置USART1
      /* Enable USART */
    USART_Cmd(USART1, ENABLE);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接收数据的中断
    /* Configure the NVIC Preemption Priority Bits */  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    /* Enable the USART1 Interrupt */
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定NVIC线路的抢占优先级为1
	  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定NVIC线路的响应优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

}
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		Usart_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量
		Usart_RxFlag = 1;										//置接收标志位变量为1
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}

其他的就是数据传输函数 下面也是一个简单的示例,其他函数都是建立在它的基础上。

void Usart_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

三、OLED显示

这个就不太好说了,直接上代码

void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

测试整合

main.c

#include "stm32f10x.h"
#include "stdio.h"
#include "Delay.h"
#include "Dht11.h"
#include "usart.h"
#include "OLED.h"
int main(void)
{
  uint8_t tempArray[5] = {0};//温湿度数据
	OLED_Init();
	Usart_Init();
    while(1)
    {
        Delay_ms(50);//间隔500ms
        if(dht11_read_data(tempArray) == 0)//读取数据
        {
            //温度
            OLED_ShowString(1,1,"temp:");
            OLED_ShowNum(2,1,tempArray[2],2);
            //湿度
            OLED_ShowString(3,1,"humi:");
            OLED_ShowNum(4,1,tempArray[0],2);
            
        }
        Delay_ms(500);
		Usart_Printf("temp:%d\t\n",tempArray[2]);			
        Usart_Printf("humi:%d\t\n",tempArray[0]);
    }
}

总结

总结就是理解DHT11采集温湿度的原理,就很好做出来。

是挺抽象的,我的文章

最后是接线情况

DHT11_DATA------------->PB12

DHT11_GND------------->GND

DHT11_VCC------------->VCC

TXD------------->PA10

RXD------------->PA9

USB_TTL_GND------------->GND

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

37.2°C i love

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值