基于STM32_DHT11单总线温湿度传感器驱动

news2024/11/23 11:19:12

基于STM32_DHT11单总线温湿度传感器驱动


文章目录

  • 基于STM32_DHT11单总线温湿度传感器驱动
  • 前言
  • 一、DHT11?
  • 二、原理
    • 1.时序
      • 1.主机复位信号和 DHT11 响应信号
      • 2.信号‘0’的表示
      • 3.信号‘1’的表示
      • 4.整个数据信号收发流程
    • 2.数据结构
  • 三、驱动
    • 1 .h文件:
    • 2 .c文件:


前言

趁热打铁,接上篇DS18B20的介绍再出一篇关于单总线传感器DHT11的介绍,相比之下,DHT11的时序和操作更简单易懂。


一、DHT11?

DHT11 是广州奥松有限公司生产的一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。 DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。 DHT11 功耗很低, 5V 电源电压下,工作平均最大电流 0.5mA。性能指标和特性如下:
■ 工作电压范围: 3.5V-5.5V
■ 工作电流 :平均 0.5mA
■ 湿度测量范围:20-90%RH
■ 温度测量范围:0-50℃
■ 湿度分辨率 : 1%RH 8 位
■ 温度分辨率 : 1℃ 8 位
■ 采样周期 : 1S
■ 单总线结构
■ 与 TTL 兼容(5V)
在这里插入图片描述
DHT11 数字湿温度传感器连接方法极为简单。第一脚接电源正,第四脚接电源地端。数据端为第二脚。可直接接主机(单片机)的 I/O 口。为提高稳定性,建议在数据端和电源正之间接一只 4.7K 的上拉电阻。第三脚为空脚,此管脚悬空不用。

二、原理

1.时序

1.主机复位信号和 DHT11 响应信号

在这里插入图片描述
主机拉低信号维持18ms后释放总线(变为高电平),20-40us后,DHT11会将总线拉低维持40-50微秒再将总线拉高维持40-50微秒,主机在对应时间段内依次检测到低电平信号和高电平信号,表示有从机在位,否则从机不在位。

2.信号‘0’的表示

以12-14微秒的低电平作为始,随之而来的高电平信号维持26-28微秒后拉低,此时序表示信号 0
在这里插入图片描述

3.信号‘1’的表示

同样以12-14微秒的低电平作为始,随之而来的高电平信号维持116-118微秒后拉低,此时序表示信号 1
在这里插入图片描述
总结来讲就是看高电平时间维持的长短,可使用while循环等待电平信号是否为低,循环依次延时1us,计数器+1,如果跳出循环则判断计数器数值,小于30则为0,大于30且小于120则为1,大于120则为错误。

4.整个数据信号收发流程

在这里插入图片描述

2.数据结构

DHT11数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由5Byte( 40Bit)组成。数据分小数部分和整数部分,具体格式在下面说明。
         一次完整的数据传输为40bit,高位先出。
         数据格式: 8bit湿度整数数据+8bit湿度小数数据
                +8bit温度整数数据+8bit温度小数数据
                +8bit校验和
         校验和数据为前四个字节相加。
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。如果,某次从传感器中读取如下5Byte数据:
        在这里插入图片描述
由以上数据就可得到湿度和温度的值,计算方法:
        humi (湿度)= byte4 . byte3=45.0 (%RH)
        temp (温度)= byte2 . byte1=28.0 ( ℃ )
        jiaoyan(校验)= byte4+ byte3+ byte2+ byte1=73(=humi+temp)(校验正确)
注意: DHT11一次通讯时间最大3ms,主机连续采样间隔建议不小于100ms。

三、驱动

1 .h文件:

#ifndef __DHT11_H
#define __DHT11_H

#include "stdio.h"	
#include "Config.h"  			//包含初始化GPIO,初始化时钟
#include "SysTick.h"

/*******************************************
 *DHT11 devier
 ********************************************/
#define DHT11_IS_READY      (1u)
#define DHT11_NOT_READY     (0u) 
 
#define DHT11_DQ_IN()       {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}//PG9 输入模式
#define DHT11_DQ_OUT()      {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}//PG9 输出模式

#define DHT11_DQ_PORT       GPIOG
#define DHT11_DQ_PIN        GPIO_Pin_9

#define DHT11_DQ_LOW        GPIO_ResetBits(DHT11_DQ_PORT,DHT11_DQ_PIN) 
#define DHT11_DQ_HIGH       GPIO_SetBits(DHT11_DQ_PORT,DHT11_DQ_PIN) 

#define DHT11_DQ_STATUS     GPIO_ReadInputDataBit(DHT11_DQ_PORT,DHT11_DQ_PIN)		//读取DQ状态
 
#define DHT11_delay_us(a)   SysCtlDelayus(a)			      //延时函数us
#define DHT11_delay_ms(a)   SysCtlDelayus(a * 1000)			//延时函数ms

#define DHT11_ACK_TIMEOUT           (uint8_t)100	        //等待DHT11响应信号延时
#define DHT11_BIT_START_TIMEOUT     (uint8_t)100	        //1bit起始信号延时时间
#define DHT11_BIT_STATUS_TIMEOUT    (uint8_t)130	        //表示 0 的延时时间

typedef enum
{
  DHT11_1 = 0,
  DHT11_2,
  DHT11_3,
  DHT11_4,
  DHT11_Num_Counter
}DHT11_Num;

typedef struct
{
  uint8_t DHT11_IndexNumber;
  float DHT11_Temp;
  float DHT11_Humi;
}DHT11_Data_Type;

extern uint8_t DHT11_Init(void);
extern void DHT11_Reset(void);
extern uint8_t DHT11_GetTemp_Main(void);
extern float DHT11_Get_Temperature(uint8_t Index);
extern float DHT11_Get_Humidity(uint8_t Index);

#endif
## 1.时序

.c文件:

#ifndef __DHT11_H
#define __DHT11_H

#include "stdio.h"	
#include "Config.h"  			//包含初始化GPIO,初始化时钟
#include "SysTick.h"

/*******************************************
 *DHT11 devier
 ********************************************/
#define DHT11_IS_READY      (1u)
#define DHT11_NOT_READY     (0u) 
 
#define DHT11_DQ_IN()       {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}//PG9 输入模式
#define DHT11_DQ_OUT()      {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}//PG9 输出模式

#define DHT11_DQ_PORT       GPIOG
#define DHT11_DQ_PIN        GPIO_Pin_9

#define DHT11_DQ_LOW        GPIO_ResetBits(DHT11_DQ_PORT,DHT11_DQ_PIN) 
#define DHT11_DQ_HIGH       GPIO_SetBits(DHT11_DQ_PORT,DHT11_DQ_PIN) 

#define DHT11_DQ_STATUS     GPIO_ReadInputDataBit(DHT11_DQ_PORT,DHT11_DQ_PIN)		//读取DQ状态
 
#define DHT11_delay_us(a)   SysCtlDelayus(a)			      //延时函数us
#define DHT11_delay_ms(a)   SysCtlDelayus(a * 1000)			//延时函数ms

#define DHT11_ACK_TIMEOUT           (uint8_t)100	        //等待DHT11响应信号延时
#define DHT11_BIT_START_TIMEOUT     (uint8_t)100	        //1bit起始信号延时时间
#define DHT11_BIT_STATUS_TIMEOUT    (uint8_t)130	        //表示 0 的延时时间

typedef enum
{
  DHT11_1 = 0,
  DHT11_2,
  DHT11_3,
  DHT11_4,
  DHT11_Num_Counter
}DHT11_Num;

typedef struct
{
  uint8_t DHT11_IndexNumber;
  float DHT11_Temp;
  float DHT11_Humi;
}DHT11_Data_Type;

extern uint8_t DHT11_Init(void);
extern void DHT11_Reset(void);
extern uint8_t DHT11_GetTemp_Main(void);
extern float DHT11_Get_Temperature(uint8_t Index);
extern float DHT11_Get_Humidity(uint8_t Index);

#endif

2 .c文件:

 #include "DHT11.h"
 
 //#define DHT11_MORE_THAN_ONE 

DHT11_Data_Type DHT11_Temp[DHT11_Num_Counter] = 
  {
    {DHT11_1,0.0,0.0},
    {DHT11_2,0.0,0.0},
    {DHT11_3,0.0,0.0},
    {DHT11_4,0.0,0.0}   
  };

static uint8_t DHT11_Read_Bit(void);
static uint8_t DHT11_Read_Byte(void);
static uint8_t DHT11_Check(void);

/************************************************************************************
*@fuction	:DHT11_Init
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
extern uint8_t DHT11_Init(void)
{
    DHT11_Reset();
    if(DHT11_Check() == DHT11_IS_READY)
    {
      return DHT11_IS_READY;
    }
}

/************************************************************************************
*@fuction	:DHT11_Reset
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
void DHT11_Reset(void)
{
    //DHT11 复位时序:DQ输出模式 DQ = 0(20us), DQ = 1(30us.
    DHT11_DQ_OUT();
    DHT11_DQ_LOW;
    DHT11_delay_ms(20);
    DHT11_DQ_HIGH;
    DHT11_delay_us(30);
}

/************************************************************************************
*@fuction	:DHT11_Check
*@brief		:
*@param		:--
*@return	:1-device ok/0-device error
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
uint8_t DHT11_Check(void)
{
    uint8_t wait_time = 0;
    uint8_t Ready_Dev = 0;
		
	//DQ输入模式
    DHT11_DQ_IN();
	//等待DQ脚被DHT11拉低
    while((DHT11_DQ_STATUS) && (wait_time < DHT11_ACK_TIMEOUT))
    {
        wait_time++;
        DHT11_delay_us(1);
    };
    if(wait_time >= DHT11_ACK_TIMEOUT)
    {
         //如果等待时间超时,则退出等待
         return (uint8_t)DHT11_NOT_READY;
    }
    else
    {
         wait_time = 0;
         while((!DHT11_DQ_STATUS) && (wait_time < DHT11_ACK_TIMEOUT))
         {
              wait_time++;
              DHT11_delay_us(1);
         };
         if(wait_time >= DHT11_ACK_TIMEOUT)
         {
              //如果等待时间超时,则退出等待
              return (uint8_t)DHT11_NOT_READY;
         }
         else
         {
              //如果未超时,则说明设备存在
              return (uint8_t)DHT11_IS_READY;              
         }
    }
}

/************************************************************************************
*@fuction	:DHT11_Read_Bit
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
static uint8_t DHT11_Read_Bit(void) 			 //read one bit
{
   uint8_t wait_time = 0;
   uint8_t Bit_Status = 0;
   
 
    DHT11_DQ_IN();
    //等待DQ脚被DHT11拉低
    while((DHT11_DQ_STATUS) && (wait_time < 100))
    {
        wait_time++;
        DHT11_delay_us(1);
    }
    if(wait_time >= 100)
    {
         //如果等待时间超时,则出现错误,退出等待
         Bit_Status = 0xFF;
    }
    else
    {
         wait_time = 0;
        //等待DQ脚被DHT11抬高
         while((!DHT11_DQ_STATUS) && (wait_time < DHT11_BIT_START_TIMEOUT))
         {
              wait_time++;
              DHT11_delay_us(1);
         }
         if(wait_time >= DHT11_BIT_START_TIMEOUT)
         {
              //如果等待时间超时,则退出等待
              Bit_Status = 0xFF;
         }
         else
         {
              //如果未超时,则说明1bit起始信号正常,
              wait_time = 0;
              //进入高电平时长判断
              while((DHT11_DQ_STATUS) && (wait_time < DHT11_BIT_STATUS_TIMEOUT))
              {
                wait_time++;
                DHT11_delay_us(1);
              }
              //如果高电平保持时间小于50us则为0,大于50us则为1
              if(wait_time <= 50)
              {                 
                  Bit_Status = 0;
              }
              else if((50 < wait_time) && (wait_time <= 150))
              {
                  Bit_Status = 1;              
              }
              else
              {
                  Bit_Status = 0xFF;
              }
         }
    }

  return Bit_Status;
}
/************************************************************************************
*@fuction	:DHT11_Read_Byte
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
static uint8_t DHT11_Read_Byte(void)
{
    uint8_t i = 0 ,Bit_Status = 0,aByte = 0;
    //DQ为输入模式
    DHT11_DQ_IN();
    for (i = 0; i < 8; i++)
    {
        //高位先出
        Bit_Status = DHT11_Read_Bit();
        aByte = (aByte << 1) | Bit_Status;
    }
    
    return aByte;
}

/************************************************************************************
*@fuction	:DHT11_CheckSumFun
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
uint8_t DHT11_CheckSumFun(uint8_t* MsgArr, uint8_t Lengh)
 {
   uint8_t CheckSum = 0;
   uint8_t i = 0;
   
   if((MsgArr == NULL_Ptr) && (Lengh == NULL))
   {
     return CheckSum;
   }
   
   for(i = 0; i < Lengh; i++)
   {
     CheckSum = (CheckSum + MsgArr[i]) % 256;
   }
   
   return CheckSum;
 }

/************************************************************************************
*@fuction	:SplicingFloat
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
float SpliceFloat(uint8_t Integer, uint8_t Decimal)
{
   float Temp = Integer;
 
   if(Decimal & 0x80) 
   {
      Temp = -1 - Temp;
   }
   Temp += (Decimal & 0x0f) * 0.1;
   
   return Temp;
}
 
/************************************************************************************
*@fuction	:DHT11_GetTemp_Main
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
extern uint8_t DHT11_GetTemp_Main(void)
{
    uint8_t i = 0,temp[5];
  
    DHT11_Reset();
    if(DHT11_Check() == DHT11_IS_READY)
    {
      for(i = 0;i < 5;i++)
      {
         temp[i] = DHT11_Read_Byte();
      }
      if(DHT11_CheckSumFun(temp,4) == temp[4])
      {
         DHT11_Temp[0].DHT11_Humi = SpliceFloat(temp[0],temp[1]);
         DHT11_Temp[0].DHT11_Temp = SpliceFloat(temp[2],temp[3]);
        
         return E_OK;
      }
    }
}

/************************************************************************************
*@fuction	:DHT11_Get_Temp
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
extern float DHT11_Get_Temperature(uint8_t Index)
{
    return DHT11_Temp[Index].DHT11_Temp;
}

/************************************************************************************
*@fuction	:DHT11_Get_Temp
*@brief		:
*@param		:--
*@return	:void
*@author	:_Awen
*@date		:2022-12-04
************************************************************************************/
extern float DHT11_Get_Humidity(uint8_t Index)
{
    return DHT11_Temp[Index].DHT11_Humi;
}

注意:

1.硬件电路中DQ脚外部会加上拉电阻,主机释放总线会被上拉电阻自动上拉,但为保险器件我们将主机释放中线写为主动上拉为高电平
2. .h文件中对延时函数DS18B20_delay_us(a)的定义,关于 SysCtlDelayus(a)实际是ARM汇编的一种延时函数的写法,参见另一篇关于延时函数的博客(汇编延时)https://blog.csdn.net/Yin_w/article/details/130036593?spm=1001.2014.3001.5501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣-434.字符串中的单词数

Idea 利用C中的 stringstream 指定字符分割字符串 class Solution { public:int countSegments(string s) {int cnt 0;stringstream ss(s);string word;while(ss >> word){cnt;}return cnt;} };

大模型分布式训练并行技术(一)-概述

近年来&#xff0c;随着Transformer、MOE架构的提出&#xff0c;使得深度学习模型轻松突破上万亿规模参数&#xff0c;传统的单机单卡模式已经无法满足超大模型进行训练的要求。因此&#xff0c;我们需要基于单机多卡、甚至是多机多卡进行分布式大模型的训练。 而利用AI集群&am…

【Java 进阶篇】JavaScript流程控制语句详解

JavaScript是一门高级编程语言&#xff0c;具备丰富的流程控制语句&#xff0c;用于控制程序的执行流程。在本篇博客中&#xff0c;我们将深入探讨JavaScript的流程控制语句&#xff0c;包括条件语句、循环语句、以及其他一些控制语句。这篇博客将逐步介绍这些概念&#xff0c;…

智慧公厕:打破传统,解决城市痛点@中期科技

近年来&#xff0c;随着城市化进程的加速推进&#xff0c;智慧公厕成为人民生活质量提升的重要组成部分。作为一个富有创新和科技感的解决方案&#xff0c;智慧公厕不仅满足了人们对公共环境的要求&#xff0c;还提供了一系列便利的服务&#xff0c;让人们的生活更加舒适、便捷…

LabVIEW在安装了其它的NI软件之后崩溃了

LabVIEW在安装了其它的NI软件之后崩溃了 在安装了其它的NI软件之后&#xff0c;一些原本安装好的或者新安装的软件由于缺少必要的DLL而崩溃掉了。例如&#xff0c;在这种情况下&#xff0c;Teststand可能会报下面的错误&#xff1a; RetrievingCOM class factory for compone…

基于Springboot的闲置图书共享系统设计与实现(源码+论文+开题报告+PPT+部署)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

PixMIM论文笔记

论文名称&#xff1a;PixMIM: Rethinking Pixel Reconstruction in Masked Image Modeling 发表时间&#xff1a;2023 年 3 月 4 日 作者及组织&#xff1a;上海人工智能实验室、西蒙菲莎大学、香港中文大学 GitHub&#xff1a;https://github.com/open-mmlab/mmselfsup/tree/d…

虚幻阴影整理

虚拟阴影贴图&#xff08;VSM&#xff09;是一种全新的阴影贴图方法&#xff0c;可以提供稳定的高分辨率阴影。通过与虚幻引擎5的Nanite虚拟几何体、Lumen全局光照和反射以及世界分区功能结合使用&#xff0c;它能够实现电影级的品质效果&#xff0c;为大型开放场景提供光照。 …

C#,数值计算——数据建模Plog的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Plog { private int ndat { get; set; } private double[] dat { get; set; } private double[] stau { get; set; } private double[] slogtau { get; set; …

【Redis】使用Java客户端操作Redis

目录 引入jedis依赖连接Redis命令get/setexists/delkeysexpire/ttltype 引入jedis依赖 连接Redis 命令 get/set exists/del keys expire/ttl type

简单三步,让你的二维码焕发新生

在当今数字化时代&#xff0c;二维码已经成为了我们日常生活和工作中不可或缺的一部分。然而&#xff0c;你是否厌倦了那些单调、乏味的二维码&#xff1f;是否希望你的二维码能够更加美观、个性化&#xff1f;接下来&#xff0c;就让我们一起走进二维码的美化之旅。 首先&…

8.Covector Transformation Rules

上一节已知&#xff0c;任意的协向量都可以写成对偶基向量的线性组合&#xff0c;以及如何通过计算基向量穿过的协向量线来获得协向量分量&#xff0c;且看到 协向量分量 以 与向量分量 相反的方式进行变换。 现要在数学上确认协向量变换规则是什么。 第一件事&#xff1a;…

学习记忆——数学篇——案例——代数——不等式——一元二次不等式

重点记忆法 归类记忆法 解一元二次不等式的步骤 1.先化成标准型&#xff1a; a x 2 b x c > 0 ( 或 < 0 ) ax^2bxc>0(或<0) ax2bxc>0(或<0)&#xff0c;且a >0&#xff1b; 2.计算对应方程的判别式 △ △ △&#xff1b; 3.求对应方程的根&#xff1b…

Spring-学习

Spring&#xff1a; 图片出处&#xff1a;b站黑马 ssm学习截图 是一个大家族 &#xff0c;是一套完整的开发生态圈。可以利用这个spring全家桶快速构建企业级开发环境。 Spring Freamwork 是其他框架的基础 Springbot 使用了注解开发 SpringCloud 分布式 云服务 Sprin…

ShopXO download 任意文件读取

漏洞描述 ShopXO存在任意文件读取漏洞&#xff0c;攻击者可利用该漏洞获取敏感信息 漏洞复现 访问url&#xff1a; 构造payload 漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或…

想要精通算法和SQL的成长之路 - 连续的子数组和

想要精通算法和SQL的成长之路 - 连续的子数组和 前言一. 连续的子数组和1.1 最原始的前缀和1.2 前缀和 哈希表 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 连续的子数组和 原题链接 1.1 最原始的前缀和 如果这道题目&#xff0c;用前缀和来算&#xff0c;我们的思路…

基于Java的点歌管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

车载电子电器架构 —— 国产基础软件现在与未来

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

【Vue 2】Props

Prop大小写 Prop的命名规则有camelCase&#xff0c;驼峰命名和kebab-case&#xff0c;短横线分隔。 由于HTML对大小写不敏感&#xff0c;所以浏览器会把大写字母解释为小写字母。 当我们使用camelCase命名prop时&#xff0c;在Dom中的template模板使用该prop就需要换成对应的…

【LeetCode】17. 电话号码的字母组合

1 问题 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” 输出&…