STM32嵌入式开发笔记——自动测量系统STM32F103C8+ESP8266+DHT22

STM32嵌入式开发笔记——自动测量系统STM32F103C8+ESP8266+DHT22

最近接触了一些STM32嵌入式开发的工作。本人在此方面完全是门外汉,所以通过博客来记录自己的工作内容,也希望专业人士看到后能够给出一些指点。

该项目是基于STM32系列芯片的自动测温测湿系统,主要作为初学者的学习项目。项目开发采用的是基于HAL库的STM32CubeIDE,初衷是可以快速上手,并且代码方便移植到不同型号的芯片。开发中需要用到的调试软件包括XCOM串口调试助手、网络调试助手。

一、 系统功能与原理

  • DHT22温湿度传感器以3s为周期采集数据,并生成对应的温湿度信息。
  • 温湿度信息(字符串)通过USB虚拟串口发送至PC,温湿度数据通过ESP8266wifi模块发送至PC。
  • ESP8266建立Wifi连接的模式有两种。
    • a) ESP8266作为主机打开wifi,PC加入该wifi与ESP8266建立连接。
    • b) ESP8266和PC都作为客户端加入同一个WiFi,然后ESP8266找到PC的地址建立连接。
  • USB虚拟串口可将调试信息发送至PC,并从PC接收信息直接转发到ESP8266,因此可以在PC中直接发送AT指令。
    图1. Stm32温湿度测量系统原理图。
    图1. Stm32温湿度测量系统原理图

二、 系统硬件需求

工作指标工作指标备注
Stm32F103C8T6开发板3.3V供电,主频72MHz需要支持至少一个usart接口用于连接WiFi模块。需要虚拟USB接口以输入调试指令和输出调试信息。一个GPIO引脚用于连接温度传感器。
ESP8266-01 WiFi模块Vcc需要稳定3.3V供电,峰值电流可达500mA,需确保电源模块的稳定性和功率指标。空闲时电流20mA。01型号需要在EN和RST引脚接10k上拉电阻。可用改进型号(ESP8266-01S)替代,则不需要额外接EN和RST引脚。
DHT22温度传感器(AM2302)Vcc 3.3-6V供电,经测试3.3v可正常工作。工作电流1mA。采样周期>2s,每次采样后返回的是上一次采样的数据。Vcc和Gnd切勿接反,否则烧毁。

三、 系统模块介绍与设置

Stm32F103C8T6开发板

该开发板自带LED,由PA1引脚通过GPIO控制。DHT22使用PA6引脚进行单总线传输。PA1和PA6的GPIO需要单独设置。开启ST-Link调试需要在SYS->Debug中选择Serial wire,CubeMx将自动设置PA13、PA14引脚。

其他用到的引脚:DHT22所需的自定义微秒级延时将用到TIM4计时器,开启后自动配置PD0,PD1引脚。开发板自带的USB接口,使用PA11,PA12引脚。开发板自带的ESP8266WIFI模块接口,对应USART2,使用PA2、PA3引脚。开发板的原理图与其他功能详见附件。

为了使各个模块更加独立,本系统开发时勾选了CubeMx下
project manager/Code generator/generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral。设置以后每个外设都将生成独立的源码,方便管理。

图2. Stm32CubeMx中Stm32F103C8的引脚和Debug设置图2. Stm32CubeMx中Stm32F103C8的引脚和Debug设置

虚拟USB串口

虚拟USB串口用于单片机与PC的通信。配置完成后,可由PC上的串口通信助手打开与单片机的连接,并收发调试信息。

CubeMx配置

虚拟USB串口功能需要打开ConnectivityUSB Device(FS),并在Middleware中打开USB虚拟串口:Communication Device Class (Virtual Port Com)

虚拟USB串口设置

图3. 虚拟USB串口设置。

配置成功后,将stm32开发版的microUSB接口连接PC后,可以在串口调试助手中看到相应的COM并可以打开连接。

修改usbd_cdc_if.c,重写USB串口的接收函数CDC_Receive_FS,使其将收到的数据转发给huart2串口(ESP8266)。USB串口的发送函数CDC_Transmit_FS保持默认。

在usbd_cdc_if.c中重写USB串口的接收函数CDC_Receive_FS

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
	usbRxLen=Len[0];
	for (int i=0;i<usbRxLen;i++){
		UserRxBufferFS[i]=Buf[i];
	}
	CDC_Transmit_FS(UserRxBufferFS,usbRxLen);
	HAL_UART_Transmit_DMA(&huart2 ,(uint8_t*)UserRxBufferFS,usbRxLen); //send to usart2
	// generated code:
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

单片机重启后有时候需要重新插拔USB接口,才能让windows系统重新检测串口连接状态,在调试时非常不方便。因此我们可以写一个模拟USB重新插拔的函数,代替手动插拔的操作。其原理是通过拉低再拉高D+引脚的电平,模拟插拔的动作。注意:模拟插拔的函数必须放在USB初始化之前,因为该函数内将USB相关引脚配置为GPIO模式,而USB初始化函数会在之后进行其他配置。

在usb_device.c中实现USB_Plug_Init

void USB_Plug_Init(void){
	GPIO_InitTypeDef GPIO_InitStruct;
	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOA_CLK_ENABLE();
	/*Configure GPIO pin Output Level */
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
	/*Configure GPIO pin : PA12 */
	GPIO_InitStruct.Pin = GPIO_PIN_12;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_PULLDOWN;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	//pull up PA12 then pull down again to simulate usb unplug and plug
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12,GPIO_PIN_RESET);
	HAL_Delay(65);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET);
	HAL_Delay(65);
}

在MX_USB_DEVICE_Init的开头调用USB_Plug_Init

void MX_USB_DEVICE_Init(void)
{
  /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
	USB_Plug_Init();
  /* USER CODE END USB_DEVICE_Init_PreTreatment */
  /* Init Device Library, add supported class and start the library. */
....
}

ESP8266模块

ESP8266模块用于建立与PC的无线连接。本系统中配置了两种连接模式。一种是ESP8266以AP热点模式启动,作为服务器建立自身的wifi。PC作为客户端连入该wifi后,可由网络调试助手连接服务器并发送数据,ESP8266可以收到多个客户端发送的数据,以及向指定的客户端发送数据。第二种方式是ESP8266以Station模式启动,作为客户端加入其他wifi。当PC和ESP8266位于同意wifi下时,利用网络调试助手打开TCP Server后,ESP8266可以通过PC的ip地址连接PC开设的服务器,并于服务器进行收发通讯。在该模式下ESP8266将打开透传模式,将所有USART2串口收到的数据直接发送到服务器。

通过修改main.c中ESP初始化函数的参数,可以选择芯片启动后的两种连接模式。用户也可以通过USB串口发送AT指令来手动配置ESP8266。

接线

本系统采用ESP-01型号,如图3所示,EN和RST引脚需要接10k上拉电阻。由于Stm32开发板与ESP模块需要互传数据,因此收发引脚应对应相接。Stm32开发板的RXD引脚应接ESP的TXD引脚,TXD引脚接ESP的RXD引脚。由于ESP8266在发送大量数据时可能会产生较大的工作电流和功耗,因此建议采用外部稳压3.3v电源单独供电。本文采用STM32开发板的3.3v引脚进行供电,经测试可以收发少量数据。
本文采用的Stm32F103C8开发板上提供了ESP8266的专用接口,按照开发板原理图对应接线即可。该接口占用PA2和PA3引脚,默认对应USART2串口。

ESP-01模块的接线示意图
图4. ESP-01模块的接线示意图。

CubeMX配置

配置Usart2引脚,模式设为Asynchronous,并打开global interrupt。由于需要用到DMA收发,还需要手动设置DMA收发通道,如图5所示。
ESP8266所需的USART2串口配置
图5. ESP8266所需的USART2串口配置。

该USART串口除了收发数据外,还需要传输AT指令(尤其是接收)。默认的DMA接收、中断接收和堵塞接收都只支持固定长度的接收,因此在接收不定长的AT指令时,一旦指令长度和预设长度不符,将发生发生错误:

  • 指令长度大于预设长度时,会提前触发接收完成中断进入回调函数,导致指令接收不完整。
  • 指令长度小于预设长度时,无法触发接收完成中断,进而无法进入中断回调函数来处理数据。同时,下一次接收的数据的前一部分将会被当成上一次接收中未完成的部分,导致第二次接收数据不完整。

为避免这一问题,需要实现任意长度的数据接收功能参考博客)。思路:连续接收结束后会进入空闲中断。可以自定义空闲中断的处理方式,连续接收结束后,在空闲中断回调函数中处理缓存中的数据,并重置寄存器。

为了使用空闲中断,在USART2的初始化函数中使能空闲中断: __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE)。之后,在USART2的中断处理中加入空闲中断处理,调用自己定义的回调函数。

usart.c中修改MX_USART2_UART_Init函数

void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //
  /* USER CODE END USART2_Init 2 */
}

在USART2的中断处理中加入空闲中断处理,调用自己定义的回调函数

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

    if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)  // 空闲中断
	{
    	USART2_DMA_RxCpltCallback(); // 调用自定义回调函数
	}
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

此外还需要自己实现中断回调函数,此处命名为USART2_DMA_RxCpltCallback。在DMA接收结束时,会进入空闲中断,此时调用该回调函数以处理接收到的数据。首先重置寄存器,然后将收到的数据发送到usb串口,并清空接收缓存。除此之外,还需要重写收发错误回调函数USART2_UART_ErrorCallback,从而在发生预料之外的错误时重新打开DMA接收功能,否则USART2串口将无法再次接收数据。该函数是__weak函数,在没有重写时将由默认函数代替。

usart.c中定义所需的变量

volatile uint8_t recv_end_flag;              //接收完成标记
volatile uint8_t rx_len;                     //接收数据长度
extern uint8_t USART2_RxBuffer[];			//usart2 receive buffer

usart.c中实现USART2_DMA_RxCpltCallback

void USART2_DMA_RxCpltCallback(){
	//used for receive data of any length
	uint32_t temp;
  	__HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除标志
  	temp = huart2.Instance->SR;  //清除寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
  	temp = huart2.Instance->DR; //读取数据寄存器中的数据
  	HAL_UART_DMAStop(&huart2); //
  	temp  = hdma_usart2_rx.Instance->CNDTR;// 获取DMA中未传输的数据个数  	rx_len =  USART2_RxBUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据数量
  	recv_end_flag = 1;    // 接受完成标志位置1

	CDC_Transmit_FS(USART2_RxBuffer,sizeof(USART2_RxBuffer)); // send data to usb
	memset(USART2_RxBuffer,0,sizeof(USART2_RxBuffer)); // reset buffer
	rx_len=0;//清除计数
	recv_end_flag=0;//清除接收结束标志
	HAL_UART_Receive_DMA(&huart2,USART2_RxBuffer,USART2_RxBUFFER_SIZE);// 启动DMA接收
}

usart.c中重写USART2_UART_ErrorCallback

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart2)
	{
		__HAL_UNLOCK(huart);
		memset(&huart2,0,sizeof(UART_HandleTypeDef) );
		HAL_UART_MspDeInit(&huart2);
		MX_DMA_Init();
		MX_USART2_UART_Init();
		HAL_UART_Receive_DMA(&huart2,(uint8_t*)USART2_RxBuffer,USART2_RxBUFFER_SIZE);
	}
}
ESP8266功能封装

配置USART2串口后,还需要针对ESP8266的功能进行封装,例如初始化、处理AT指令、向外发送数据等。ESP8266.h中还需要指定ESP所用到的UART串口和相应的接收缓存。

ESP8266.h中提供了一系列接口,并在此处定义ESP所用的usart接口组、缓存大小、缓存。移植时需要修改此处的定义。

//ESP8266.h
#ifndef INC_ESP8226_H_
#define INC_ESP8226_H_
#include "stm32f1xx_hal.h"
#define ESP_USART huart2 //define the uart group used by ESP8266
#define ESP_BUFFER_SIZE USART2_RxBUFFER_SIZE // define the buffer size
#define ESP_RxBuffer USART2_RxBuffer
void ESP_Send_cmd(char cmd[],uint32_t waittime, uint32_t delay);//发送AT
void ESP_Send_Data_TT(uint8_t* data, uint16_t len); //透传模式发送任意数据
void ESP_Send_Data_4Bytes(uint8_t* data);//非透传模式发送4字节
void ESP_Init(uint8_t mode);//初始化
void ESP_Init_TT();//打开透传
#endif /* INC_ESP8226_H_ */

在main函数中调用ESP_Init(1),可以将esp配置为station模式。默认连接的wifi和密码需要自己修改,TCP服务器地址(PC)192.168.0.196,端口8080。默认自动开启透传。调用ESP_Init(2)将会配置ESP为AP模式(服务器模式),wifi名:TCP_Server,密码12345678,TCP地址192.168.4.1,端口8080。

调用ESP_Send_cmd可以向ESP8266发送AT指令,并根据参数执行等待和延时。

DHT22温湿度传感器

DHT22温湿度传感器可同时测量温度和湿度数据,精确到小数点后一位。传感器采用单总线模式传输数据,占用Stm32一个GPIO引脚。接收数据时,先由Stm32发送一个激活信号,然后将GPIO引脚设置为接收,收到DHT22回应后,读取40位的数据。前0-15位为温度数值10,16-31位为湿度数据10,32-39位为校验位。前32位数据相加后应与最后8位数据相等。DHT22的测量周期应大于2s(系统中为3s),每次测量时返回的是上一次测量的数据。

接线

DHT22温湿度传感器的接线如图6所示。因为传感器内置上拉电阻,数据引脚可直接连接Stm32引脚,Vcc接3.3v-6v电压。本系统采用stm32的3.3v引脚供电,经测试传感器工作正常。
DHT22接线示意图图6. DHT22接线示意图。

CubeMx配置

DHT22采用单总线方式传输数据,因此CubeMx中只需要配置一个GPIO引脚(图7)。默认GPIO模式设为输出:Output open drain

PA6GPIO设置图7. PA6 GPIO设置。

DHT22功能封装

DHT22相关功能的实现和接口分别在DHT22.cDHT22.h中定义。

DHT22.h定义了引脚和错误代码,以及提供了以下接口:

  • DHT22_Init():初始化DHT引脚,设为输出
  • uint8_t DHT22_Get_Data(uint16_t *temp,uint16_t *humi):从头开始读取数据,包括向DHT发送开始接收数据的信号,获取响应,读取、校验数据
  • void ONE_WIRE_Start():发送开始接收数据的信号
  • uint8_t ONE_WIRE_Response():获取DHT的响应
  • uint64_t DHT22_ReadData(void): 读取40位的原始数据
  • void DHT22_THMsg(uint16_t Temp, uint16_t Humi, char THMsg[30]):根据相应的温度适度数据,生成固定格式的字符串信息供调试使用。格式:Temp:00000000, Humi:00000000\r\n
//DHT22.h
#include "stm32f1xx_hal.h"
// pin
#define DHT_PORT GPIOA
#define DHT_PIN GPIO_PIN_6
// error codes
#define DHT_ERR_RESPONSE_FAILED 1
#define DHT_ERR_READ_BIT_FAILED 2
#define DHT_ERR_DATA_CHKSUM_FAILED 3
// function prototypes
void DHT22_Init(); //initialization function
void ONE_WIRE_Start(); //Start communication
uint8_t ONE_WIRE_Response(); //Sensor response for start command
void DHT22_THMsg(uint16_t Temp, uint16_t Humi, char THMsg[30]); //generate message string
uint8_t DHT22_Get_Data(uint16_t *temp,uint16_t *humi); //function available for user to use
uint64_t DHT22_ReadData(void); // read original data

四、 系统运行示例

此处将展示系统通过模式1方式与PC交换数据的过程。

  • 按图8接线。将PC连接到指定wifi,打开网络调试助手,设置本地主机端口并开启TCP Server。
    系统实物图图8. 系统实物图。

  • 将wifi名称和密码预先写好在ESP8266.c中的对应指令中,编译并烧录到单片机中。

  • 将USB连接到PC,在XCOM中打开串口连接,即可看到单片机发送至PC的调试信息,如图9所示。

  • 与此同时,网络调试助手也能看到单片机成功连接TCP Server的信息,并且每3秒会收到一个4字节的数据,前2字节对应温度数据,后2字节对应湿度(图10)。例如第一条数据中,温度312,16进制即为0138,湿度477,16进制为01DD。数据一致,证明系统运行正常

  • PC作为TCP Server也可以通过网络调试助手发送信息到单片机,单片机会将收到的信息转发到USB串口。

XCOM串口调试助手收到的调试信息。图9. XCOM串口调试助手收到的调试信息。
网络调试助手收到的调试信息。图10. 网络调试助手收到的调试信息。

在XCOM中可以向USB串口发送AT指令,单片机会将AT指令转发至ESP8266。首先发送+++(不需要\r\n字符)关闭透传模式,否则无法接受AT指令。然后发送AT,可以收到返回的信息AT OK,即测试AT指令正确。发送AT+RST可以看到ESP成功重置,如图11所示。调试信息中的乱码是由于关闭透传之后,4字节的温湿度数据会被转发到USB串口,对应的ASCLL码乱码。

按下stm32开发板的复位按钮,能够重置单片机并让程序从头运行。此时,需要在复位后重新打开串口,才能接收到ESP8266的初始化信息。

USB串口发送AT指令。图11. USB串口发送AT指令。

  • 5
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
Hal库是一个用于嵌入式系统的硬件抽象层库,而DHT11是一款数字温湿度传感器。当我们使用Hal库来集成DHT11传感器时,可以实现方便的温湿度监测功能。 首先,我们需要连接DHT11传感器到嵌入式系统中。DHT11传感器通常有三个引脚:VCC(电源正极)、GND(电源负极)和DATA(数据引脚)。我们需要确保正确地连接传感器引脚到系统的相应引脚上。 接下来,我们可以使用Hal库提供的函数来读取DHT11传感器的数据。Hal库通常提供了一组用于与不同类型传感器通信的API函数,这些函数可以在软件层面上与传感器进行交互。为了使用DHT11传感器,我们需要调用Hal库提供的函数来读取传感器的温度和湿度数据。 在调用这些函数之前,我们需要初始化Hal库来确保与传感器的良好通信。初始化可能涉及到配置传感器引脚的输入输出模式,设置传感器的采样精度和其他相关参数。 一旦初始化完成,我们就可以调用Hal库中读取DHT11传感器数据的函数。这些函数通常使用数据采集协议来和传感器通信,并根据传感器返回的数据格式解析出温度和湿度值。 最后,我们可以通过将读取到的温度和湿度数据传输到其他系统模块或进行数据处理等操作来实现更具体的功能,比如数据可视化、自动控制等。 总之,使用Hal库和DHT11传感器可以快速实现温湿度监测功能,只需连接传感器、初始化Hal库并调用相应的函数即可获取传感器数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值