preamble
How do you drive a sensor that is new to you? Don't look at me, and don't beat a dead horse on the internet! What you need to do, first of all, is to specify the name of your sensor, in this case, we want to use the DHT11 temperature and humidity sensor
Prior knowledge may be required
-
Simple OLED Driving Principle
-
Simple knowledge of IIC communication
-
Basic manual searching skills
-
Relatively solid foundation in C
It's okay if you don't, I'll elaborate!
A possible device scheme
A DHT11 that passes the manufacturer's test samples (ask the seller for the manufacturer's test samples when you buy the DHT11).
An OLED screen that can communicate using the IIC protocol through a manufacturer's test sample (ask the seller for a manufacturer's test sample when you purchase the OLED).
An STM32F103C8T6 core board (BTW, preferably from ST manufacturers, domestic can be, will follow up on how to modify the cfg file to pass the onboard checksum check)
One possible development environment configuration
Just choose one of the following three configurations
Normal working STM32CubeMX + Keil5
Normal working STM32CubeMX + STM32CubeIDE
PlatformIO + VSCode (author's choice)
Throughout we use the third environment configuration, other environment configurations please modify according to your own experience at your discretion!
Kaizen manual
You can get the manual that came with the DHT11 when you purchased it (what, it didn't come with it? Look carefully to see if the merchant has provided a link, and ask for it if they haven't, or, just go online and search for the manual), which we open and focus on:
- How do I start it up? (The author here got screwed for a day because he didn't read the startup method carefully resulting in a delay of the wrong length)
- How do I read the data?
- How to parse the resulting data (how the data is formatted)
The author consulted the manual at hand and was able to extract the following information:
This is the startup method indicated in the manual (answer to question 1):
This is how the manual gives how the DHT11 passes back data (to answer question 2)
This is the data format given in the manual:
That means we need a unit data frame of five bytes in size. The first four data frames are loaded with the real-time monitoring data delivered by the DHT11 sensor, and the last bit is a checksum to ensure that the data we receive is legitimate!
We are here in the most general case of development and can not be too concerned about the electrical characteristics of this device. But if there is a more specific situation in the working condition, pleaseAlways make sure that the operating environment allows the device to function properly!
Code design: top-down
Why do we say top-down? Because when we think about a project, we often first need to think about the module its basic steps, what kind of functionality it wants to achieve. This is not much to do with the underlying layer, is an abstraction of the underlying layer. So from the architectural level we call this top-down.
After we've found the information we need for the manual, it's time to dissect it piece by piece, design the code framework and implement it!
Let's think about how we use a sensor - that is, it performs a possible initialization, subsequently starts the sensor when we need it, then receives the data obtained by the sensor, and finally uses the data obtained by the sensor. For the user side, the necessary function-method interface we need to expose is exactly that:
-
Initialize the DHT11 interface
-
and the interface to get the data received by DHT11.
Ready to go.
But it doesn't end there: we want to write library drivers that use pins we don't know, expect formats the user doesn't know, and the state of the sensors whose abstraction is encapsulated needs to be known by the user. This means that we actually need to provide such parameters that can be set by the user. This is also something we need to think about in our code design!
- User-specified DHT11 GPIO interface
- The data structure that the user uses to get the specification of the data.
Reading in conjunction with the manual, we find that this sensor has integers and decimals for humidity and integers and decimals for temperature on its accuracy. This also hints at our design:
typedef struct __DHT11_DataPack{
uint8_t int_temperature; // Integer humidity
uint8_t float_temperature; // decimal humidity
uint8_t int_humanity; // Integer humidity
uint8_t float_humanity; // decimal humidity
}DHT11_DataPack;
We don't need to expose the checksum bit, because the checksum is not done by the user, but by our library developer! So exposing this interface is better than exposing a corresponding error code (which we'll talk about later)
The pins connected to the microcontroller to communicate with this sensor only need to be a common gpio pin. Therefore, we can design the user configuration structure of DHT11 in this simple way
typedef struct __DHT11_Config{
GPIO_TypeDef* GPIO_Who;
GPIO_Pin_Type GPIO_Pin;
}DHT11_Config;
Of course the response error code should not be forgotten
// Status error code
typedef enum __DHT11_Error{
DHT11_NO_ERROR, // no error
DHT11_NOT_INIT, // not initialized
DHT11_ERROR_DEVICE_NOT_FOUND, // device not found
DHT11_FAILED_READ_DATA, // Unable to read data
DHT11_FAILED_PASS_CHECK // Failed checksum check.
}DHT11_Error.
Now that we have the necessary structural abstractions in place, it's time to think about the format of the function signatures that will dictate the architectural communication between the user and the libraries we develop.
// Initialize the device, its behavior is determined based on LAZY_INIT
uint8_t init_DHT11_Device(DHT11_Config* config);
// Get the data package, the pack referral is empty!
void getDHT11_DataPack(DHT11_Config* config, DHT11_DataPack* pack);
// Get the current state
DHT11_Error error_GetDHT11_Status();
As of now we can think of three such function signatures. The approximate thought process is:
After the user calls the first initialization function, you can use the second function to get the information obtained by the sensor, for the upper layer wants to see the state only need to call the three interfaces can be.
Below, we'll realize the details.
Code implementation: bottom-up
Timing Module
Let's bring the manual's dead instructions to life below, and think carefully about the fact that we first need a function that actually does the initialization of the sensor, a function that is very much dedicated to initialization-related work and does nothing else.
static void __DHT11_Only_Init(DHT11_Config* config)
Why is the static modifier, because the function will be restricted to the file scope before available, will not be found by the outside world, familiar with C + + + and other object-oriented language comrades will immediately find that this is not private it, I think it is similar.
The advantage of this is that we can use it to remind ourselves that
In terms of architectural design, this is Interface - Implement relationship, the user side only knows that init_DHT11_Device can do our work, but doesn't know that the actual work is done by __DHT11_Only_Init as the core.
Reading the manual, we find an annoying thing that the HAL library is no delay_us by default, which is depressing, how do we know what kind of way the user is actually going to do the delay at the microsecond level? Is it using the system timer? Is it using one of the STM32's internal TIM timers? Or do we just use an empty instruction fetch to hang the pipeline? We don't even know?
For things that are completely unknown to the users of our libraries, we have chosen to provide new interfaces for the users to accomplish! It is also important to ensure a certain level of user-friendliness, to make sure that the user still maintains the whole system working as well as possible after setting up that step incorrectly.
So, we still need to work hard (really hard!) Add another interface, this one needs to let the user set their own clock
// Clock source
typedef TIM_HandleTypeDef DHT11_ClockSource;
// Otherwise, the system clock will be used, resulting in inaccurate delays!
void setDHT11_DelayClockSource(DHT11_ClockSource* source).
Can it not be Typedef?
Yes, feel free to do that, but you need to expose a header file for that. This would increase the complexity of the project, and we're just using a clock interface. Here's an engineering trick: if we're only using the name of a structure, it's best to use its pointer and declare it up front. This reduces the reference complexity of the project!
Now, we need to build the code from low to high. The difficulty we are currently experiencing is that there is no us level delay.
Regarding the Delay module, we know that either we use a timer or we manually hang the pipeline ourselves. Let's think of it this way, if for some reason there is no way to start a timer, we will use a manual pipeline for the delay. Conversely, if the user specifies a timer resource, we use the timer provided by the user for timing, and how the timer works is not our concern! That's for the user to worry about!
// Clock to be used
TIM_HandleTypeDef* using_clock = NULL;
// No timer is used by default, which means NULL.
// Use manual Delay, very unrecommended!
static void __DHT11_NO_CLOCK_SET(uint16_t us){
uint32_t Tdata = us* SystemCoreClock / 8U / 1000000U;
do{__NOP();}while(Tdata--);
return ;
}
// own rubbed HAL_DELAY_US, need to turn on a timer, timer resource use
// setDHT11_DelayClockSource is set in advance
static void __DHT11_MAKE_US_DELAY(uint16_t us){
// No clock is specified by the user!
if(!using_clock) return __DHT11_NO_CLOCK_SET(us); // User specified clock!
// User specified clock
uint16_t differ=0xffff-us-5;
HAL_TIM_Base_Start(using_clock); // user-specified clock.
__HAL_TIM_SetCounter(using_clock,differ).
while(differ<0xffff-5){
differ = __HAL_TIM_GetCounter(using_clock);
}
HAL_TIM_Base_Stop(using_clock);
}
// Timer resource setting
void setDHT11_DelayClockSource(DHT11_ClockSource* source)
{
using_clock = source;
}
Prescaler is set to 72 -1 = 71, please check yourself about how the timer is set!
initialization
After we are done with the microsecond delay function we can start the initialization.
Post the manual here
It's a job of programming on demand.
We find that the gpio is both input and output, meaning that we need to dynamically adjust the properties of the gpio interface, so we can abstract an internal function to complete the adjustment of the gpio interface
// Setting up the portIOmodal_implfunction (math.)
static void __DHT11_SetPackInternal(DHT11_Config* config, uint32_t mode)
{
GPIO_InitTypeDef init;
= mode;
= config->GPIO_Pin;
if(mode == GPIO_MODE_INPUT)
= GPIO_PULLUP; // Input then pull-up
else
= GPIO_SPEED_FREQ_HIGH; // Output Settings Quick Output
HAL_GPIO_Init(config->GPIO_Who, &init);
}
// Setting up the portIOparadigm
static void __setDHT11_PinMode(DHT11_Config* config, DHT11_Mode mode)
{
switch (mode)
{
case DHT11_Mode_Read:
__DHT11_SetPackInternal(config, GPIO_MODE_INPUT);
break;
case DHT11_Mode_Write:
__DHT11_SetPackInternal(config, GPIO_MODE_OUTPUT_PP);
break;
default:
break;
}
}
Layer by layer, we actually call the __DHT11_SetPackInternal function, and then do a little semantic wrapping around it, which can be done or not, in order to ensure good abstraction. This way, we can easily set the GPIO properties in one line.
The following is a refinement of the initialization interface
// Error settings
static DHT11_Error global_error = DHT11_NOT_INIT;
// ...
// Initialize only without checking
static void __DHT11_Only_Init(DHT11_Config* config){
__setDHT11_PinMode(config, DHT11_Mode_Write); // Write port
HAL_GPIO_WritePin(config->GPIO_Who, config->GPIO_Pin, GPIO_PIN_RESET); // pull down level
HAL_Delay(20); // Host wait at least 18ms.
HAL_GPIO_WritePin(config->GPIO_Who, config->GPIO_Pin, GPIO_PIN_SET); // pull high level
__DHT11_MAKE_US_DELAY(30); // host hang for 30us
__set_global_error(DHT11_NO_ERROR); // Initialization complete, set zero error
}
Initialization is done, we can do the following check. We want to initiate a request, we must do an initialization to notify the sensor to start communication notification after the end of the notification we also need to check whether there is really a response, only really come to the response we can further read the sensor. So we also need to write a check function as follows:
static uint8_t __DHT11_Check_If_DHT11_Work(DHT11_Config* config){
// set to read
_setDHT11_PinMode(config, DHT11_Mode_Read); // Here we need to determine if the reading is done.
// Here we need to determine whether the read is low or not, this is according to the manual: "When the bus is low, the sensor is ready to send a response signal" and write the code
uint8_t status = HAL_GPIO_ReadPin(config->GPIO_Who, config->GPIO_Pin) == GPIO_PIN_RESET; if(!
if(!status){
__set_global_error(DHT11_FAILED_READ_DATA);
}
return status; }
}
So, a user interface initialization function with error capability becomes simple:
uint8_t init_DHT11_Device(DHT11_Config* config)
{
__DHT11_Only_Init(config).
return __DHT11_Check_If_DHT11_Work(config); // return if it is ready to transmit or not
}
data transmission
Let's move on to the graph that was just posted it also illustrates how data is transmitted. I think it's pretty simple it's based on how long the high level is held to reflect the bits transmitted per unit of time. So, we need to write an internal function that reads the bits
So from here we just need a delay of about thirty microseconds to see if it's high or not to decide if the bit passed is high at this point or not
// Read one bit
static inline uint8_t __DHT11_read_one_bit(DHT11_Config* config)
{
if(! __dht11_pass_retry(config, GPIO_PIN_RESET)){
__set_global_error(DHT11_FAILED_READ_DATA);
}
// According to the timing diagram, the DHT sends back a high level signal that lasts 26us~28us for 0, and 70us for 1.
__DHT11_MAKE_US_DELAY(30); // According to the timing diagram, the DHT sends back a high level signal to maintain 26us~28us for 0 and 70us for 1.
uint8_t res = HAL_GPIO_ReadPin(config->GPIO_Who, config->GPIO_Pin);
// Still doing the check to see if the end bit was passed properly
if(! __dht11_pass_retry(config, GPIO_PIN_SET)){
__set_global_error(DHT11_FAILED_READ_DATA); }
}
return res; }
}
So at this point in time we just need to loop this thing eight times to receive a byte.
// Read a byte,Yes, it is.__DHT11_read_one_bitcyclic encapsulation
static uint8_t __DHT11_read_one_byte(DHT11_Config* config)
{
uint8_t required_ret_byte = 0;
for(uint8_t i = 0; i < 8; i++){
required_ret_byte <<= 1;
required_ret_byte |= __DHT11_read_one_bit(config);
}
return required_ret_byte;
}
It's not enough just to receive the data, we also need to do the data validation mechanism.
// data validation
static inline uint8_t __DHT11_Check_Policy(){
return dataFrame[0]+dataFrame[1]+dataFrame[2]+dataFrame[3]==dataFrame[4];
}
inline: In more traditional C, inline instructs the compiler to inline a function, which simply means to paste the assembly code of the function directly into the caller to reduce stack overhead. But it's 2024, and compilers have been doing this intelligently for a long time, but it's not guaranteed that all compilers are that intelligent. So we chose to add it so that it's always inline. Comrades interested in what this keyword does now can look it up for themselves. (Hint: the semantics change dramatically)
Now, we can go ahead and implement a complete request logic:
__DHT11_Only_Init(config); // Notify DHT11 to open communication once.
if(__DHT11_Check_If_DHT11_Work(config)) // Open successfully, start communication.
{
// Next, DHT11 pulls low for a period of time and then pulls high for a period of time
// This miss can be eliminated in the next few loops, so just set the error without backtracking
__dht11_pass_retry(config, GPIO_PIN_RESET); // monitor the bus to see if it's working as expected
__dht11_pass_retry(config, GPIO_PIN_SET); // monitor the bus to see if it is working as expected
// Fill in the data frame
for(int i = 0; i < 5;i++)
dataFrame[i] = __DHT11_read_one_byte(config);
if(__DHT11_Check_Policy()){ // Checksum bit
pack->int_humanity = dataFrame[0];
pack->float_humanity = dataFrame[1];
pack->int_temperature = dataFrame[2];
pack->float_temperature = dataFrame[3];
}else{
__set_global_error(DHT11_FAILED_PASS_CHECK);
}
}
Let's put the code together and see:
#include "stm32f1xx_hal.h"
// Maximum number of attempts
#define RETRY_MAX_TIME (100)
// protocol packet length, DHT11 defaults to 5 bytes
#define DHT11_BUFFER_SIZE (5)
// Lazy_Init determines whether or not to pull the DHT11 up early.
// defined to be 1 and not initialized until it's time to use it.
// init_DHT11_Device will take no real action and the function will always succeed!
// Instead, enable the init_DHT11_Device function!
#define LAZY_INIT 0
// Clock resources
typedef TIM_HandleTypeDef DHT11_ClockSource.
// Specified port
typedef uint16_t GPIO_Pin_Type; // Specified port.
typedef struct __DHT11_Config{
GPIO_TypeDef* GPIO_Who; typedef uint16_t GPIO_Pin_Type; // Specified port.
GPIO_Pin_Type GPIO_Pin;
}DHT11_Config.
// Status error code
typedef enum __DHT11_Error{
DHT11_NO_ERROR, // No error.
DHT11_NOT_INIT, // not initialized
DHT11_ERROR_DEVICE_NOT_FOUND, // device not found
DHT11_FAILED_READ_DATA, // Unable to read data
DHT11_FAILED_PASS_CHECK // Failed checksum check.
}DHT11_Error.
// DHT11 data structure
typedef struct __DHT11_DataPack{
uint8_t int_temperature; // DHT11 data structure.
uint8_t int_temperature; uint8_t float_temperature; // DHT11 data structure.
uint8_t int_humanity; uint8_t float_temperature; uint8_t float_humanity
uint8_t float_humanity; uint8_t int_humanity; uint8_t float_humanity
}DHT11_DataPack.
// Interface description
// Initialize the device, its behavior is determined based on LAZY_INIT
uint8_t init_DHT11_Device(DHT11_Config* config); // Set the clock resource for the DHT11 delay function.
// Set the clock resources for the DHT11 delay function, which must be called before the DHT11 is used.
// Otherwise, the system clock will be used, resulting in inaccurate delays!
void setDHT11_DelayClockSource(DHT11_ClockSource* source).
// Get the data pack, the pack referral is empty!
void getDHT11_DataPack(DHT11_Config* config, DHT11_DataPack* pack); // Get the current state.
// Get the current state
DHT11_Error error_GetDHT11_Status();
#include ""
#include ""
typedef enum __DHT11_Mode{
DHT11_Mode_Write,
DHT11_Mode_Read
}DHT11_Mode;
// misconfiguration
static DHT11_Error global_error = DHT11_NOT_INIT;
// buffer
static uint8_t dataFrame[DHT11_BUFFER_SIZE];
// Clock used
TIM_HandleTypeDef* using_clock = NULL;
// Design methodology:will thisCThe document itself is seen as a module,possess
// static Modified functions are all private private function
// Setting Global Errors
static inline void __set_global_error(DHT11_Error error){
global_error = error;
}
// Using the System Clock,highly unrecommended!
static void __DHT11_NO_CLOCK_SET(uint16_t us){
uint32_t Tdata = us* SystemCoreClock / 8U / 1000000U;
do{__NOP();}while(Tdata--);
return ;
}
// I rubbed it myself.HAL_DELAY_US,Need to turn on a timer,Timer Resource Usage
// setDHT11_DelayClockSourceSet up in advance
static void __DHT11_MAKE_US_DELAY(uint16_t us){
if(!using_clock) return __DHT11_NO_CLOCK_SET(us);
uint16_t differ=0xffff-us-5;
HAL_TIM_Base_Start(using_clock);
__HAL_TIM_SetCounter(using_clock,differ);
while(differ<0xffff-5){
differ = __HAL_TIM_GetCounter(using_clock);
}
HAL_TIM_Base_Stop(using_clock);
}
// Setting up the portIOmodal_implfunction (math.)
static void __DHT11_SetPackInternal(DHT11_Config* config, uint32_t mode)
{
GPIO_InitTypeDef init;
= mode;
= config->GPIO_Pin;
if(mode == GPIO_MODE_INPUT)
= GPIO_PULLUP; // Input then pull-up
else
= GPIO_SPEED_FREQ_HIGH; // Output Settings Quick Output
HAL_GPIO_Init(config->GPIO_Who, &init);
}
// Setting up the portIOparadigm
static void __setDHT11_PinMode(DHT11_Config* config, DHT11_Mode mode)
{
switch (mode)
{
case DHT11_Mode_Read:
__DHT11_SetPackInternal(config, GPIO_MODE_INPUT);
break;
case DHT11_Mode_Write:
__DHT11_SetPackInternal(config, GPIO_MODE_OUTPUT_PP);
break;
default:
break;
}
}
// This one is viewedretrysuccessful,Want to make the library more rigorous,Consider checking the return value!
static uint8_t __dht11_pass_retry(DHT11_Config* config, uint8_t required_check_bits)
{
uint8_t retry_times = 0;
__setDHT11_PinMode(config, DHT11_Mode_Read);
while( HAL_GPIO_ReadPin(config->GPIO_Who, config->GPIO_Pin)
== required_check_bits &&
retry_times <= RETRY_MAX_TIME)
{
__DHT11_MAKE_US_DELAY(1);
retry_times++;
}
return retry_times < RETRY_MAX_TIME;
}
// Check if the working status is normal
static uint8_t __DHT11_Check_If_DHT11_Work(DHT11_Config* config){
__setDHT11_PinMode(config, DHT11_Mode_Read);
uint8_t status = HAL_GPIO_ReadPin(config->GPIO_Who, config->GPIO_Pin) == GPIO_PIN_RESET;
if(!status){
__set_global_error(DHT11_FAILED_READ_DATA);
}
return status;
}
// Read a bit
static inline uint8_t __DHT11_read_one_bit(DHT11_Config* config)
{
if(!__dht11_pass_retry(config, GPIO_PIN_RESET)){
__set_global_error(DHT11_FAILED_READ_DATA);
}
// According to the timing diagram,DHTSend back a high level signal to maintain26us~28usindicate0, uphold70usindicate1
__DHT11_MAKE_US_DELAY(30);
uint8_t res = HAL_GPIO_ReadPin(config->GPIO_Who, config->GPIO_Pin);
// Or do you do a check to see if there's a normal pass to end a bit?
if(!__dht11_pass_retry(config, GPIO_PIN_SET)){
__set_global_error(DHT11_FAILED_READ_DATA);
}
return res;
}
// Read a byte,Yes, it is.__DHT11_read_one_bitcyclic encapsulation
static uint8_t __DHT11_read_one_byte(DHT11_Config* config)
{
uint8_t required_ret_byte = 0;
for(uint8_t i = 0; i < 8; i++){
required_ret_byte <<= 1;
required_ret_byte |= __DHT11_read_one_bit(config);
}
return required_ret_byte;
}
// data validation
static inline uint8_t __DHT11_Check_Policy(){
return dataFrame[0]+dataFrame[1]+dataFrame[2]+dataFrame[3]==dataFrame[4];
}
// Initialize only without checking
static void __DHT11_Only_Init(DHT11_Config* config){
__setDHT11_PinMode(config, DHT11_Mode_Write);
HAL_GPIO_WritePin(config->GPIO_Who, config->GPIO_Pin, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(config->GPIO_Who, config->GPIO_Pin, GPIO_PIN_SET);
__DHT11_MAKE_US_DELAY(30);
__set_global_error(DHT11_NO_ERROR);
}
uint8_t init_DHT11_Device(DHT11_Config* config)
{
#if LAZY_INIT
return 1;
#else
__DHT11_Only_Init(config);
return __DHT11_Check_If_DHT11_Work(config);
#endif
}
void setDHT11_DelayClockSource(DHT11_ClockSource* source)
{
using_clock = source;
}
DHT11_Error error_GetDHT11_Status()
{
return global_error;
}
void getDHT11_DataPack(DHT11_Config* config, DHT11_DataPack* pack)
{
__DHT11_Only_Init(config);
if(__DHT11_Check_If_DHT11_Work(config))
{
// accept,DHT11Pull low level for a period of time and then pull high level for a period of time
// This mistake can be eliminated in the next couple of loops,So just set the error and don't backtrack.
__dht11_pass_retry(config, GPIO_PIN_RESET);
__dht11_pass_retry(config, GPIO_PIN_SET);
for(int i = 0; i < 5;i++)
dataFrame[i] = __DHT11_read_one_byte(config);
if(__DHT11_Check_Policy()){
pack->int_humanity = dataFrame[0];
pack->float_humanity = dataFrame[1];
pack->int_temperature = dataFrame[2];
pack->float_temperature = dataFrame[3];
}else{
__set_global_error(DHT11_FAILED_PASS_CHECK);
}
}
}
Testing:
After we configure PA15 as the target pin and TIM1 as the timer resource in CubeMX, we write the following code
/* USER CODE BEGIN Header */
/* USER CODE BEGIN Header */
******************************************************************************
* @file .
* @brief : Main program body
******************************************************************************
* @attention
* @attention
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved. </center></h2>
* This software component is licensed by STMicroelectronics.
* This software component is licensed by ST under BSD 3-Clause license, * the "License"; Your "License"; * the "Software".
* the "License"; You may not use this file except in compliance with the * License.
* License. You may obtain a copy of the License at.
* /licenses/BSD-3-Clause
You may obtain a copy of the License at: * /licenses/BSD-3-Clause
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------ */
#include ""
#include ""
#include ""
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include ""
#include "dht11_try.h"
/* USER CODE END Includes */ #include "" #include "dht11_try.h
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */
/* USER CODE BEGIN PD */ /* USER CODE END PTD */ /* Private define */
DHT11_Data_TypeDef DH.
/* USER CODE END PD */ /* Private define */ /* USER CODE BEGIN PD */ DHT11_Data_TypeDef DH; DHT11_Data_TypeDef
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* user code end pm */ /* private macro */ /* user code
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* Private function prototypes */
/* USER CODE BEGIN PFP */ /* Private function prototypes */ void SystemClock_Config(void)
/* USER CODE END PFP */
/* Private user code --------------------------------------------------------- */
/* user code begin 0 */ /* user code end pfp */ /* private user code */
/* user code end 0 */ /* private user code */ /* private user code
/* USER CODE BEGIN 0 */ /* USER CODE END 0 */
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init().
/* USER CODE BEGIN Init */ /* Reset of all peripherals, Initializes the Flash interface and the Systick.
/* USER CODE END Init */ /* HAL_Init()
/* Configure the system clock */
SystemClock_Config(); /* USER CODE END /* USER CODE END
/* USER CODE BEGIN SysInit */ /* Configure the system clock */
/* USER CODE END SysInit */
/* USER CODE END SysInit */ /* Initialize all configured peripherals */
MX_GPIO_Init().
MX_GPIO_Init(); MX_TIM1_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END SysInit
/* USER CODE BEGIN 2 */
// Set timer resources
setDHT11_DelayClockSource(&htim1); // Configure the port.
// Configure the port
DHT11_Config config.
config.GPIO_Pin = DHT11_Pin; // Configure the port. config.
config.GPIO_Who = DHT11_GPIO_Port; config.
init_DHT11_Device(&config).
// Data packet designation
DHT11_DataPack pack.
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
/* USER CODE END WHILE */ while (1)
/* USER CODE END WHILE */
// Get the data pack
getDHT11_DataPack(&config, &pack); // Here I use debugging view.
// Here I use the debugging view, you can also specify your own serial port to print!
HAL_Delay(500).
HAL_Delay(500); /* USER CODE BEGIN 3 */
HAL_Delay(500); /* USER CODE BEGIN 3 */ }
HAL_Delay(500); /* USER CODE END 3 */ }
}
// ... omitted
Configured and burned in PIO, after set to debug mode:
You can see that it's working properly, and the room thermostat on hand shows that it's currently 31 degrees Celsius, which is relatively accurate.
Questions
Would like to try PIO + VSCode development:
Sir, this way: STM32 development environment configuration records - on PlatformIO + VSCode + CubeMX integrated environment configuration_platformio stm32-CSDN blog
I bought a domestic chip, and then when I burned the prompt Error expected 1 of 1 0x1ba01477, that is, STLink can not be burned into the program!
Congrats on getting a bootleg ST chip.VsCodedo sth (for sb)STM32F103C8T6Chip failed to download program_info : auto-selecting first available session tran-CSDNblog (loanword)It's your solution. Be a good boy and change the cfg file, remember to back up the old one.
Sources
All source code:
MCU_Libs/DHT11 at main · Charliechen114514/MCU_Libs ()