STM32电容触摸按键检测

news2024/9/21 16:39:09

STM32电容触摸按键检测

  • 电容触摸按键
    • 简介
    • 检测原理
  • CubeMX配置
  • 代码展示&讲解
    • TPAD.c
    • TPAD.h

本期内容我们将学习电容触摸按键的检测原理。以及代码实现思路

电容触摸按键

简介

在这里插入图片描述

  • 电容触摸按键依赖的是电容的充放电
  • 相对于机械按键更加耐用,不容易受外界环境干扰
  • 在我们的开发板(正点原子的STM32F411RCT6 NANO板)上长这样:
    在这里插入图片描述

检测原理

前面将电容触摸按键依赖的是电容的充放电,然而我们看开发板上的元件摆放图,发现没有电容在TPAD位置:

在这里插入图片描述

这个要从PCB的层叠结构说起;

  • 我们的NANo板使用的是双层PCB结构,大概结构就是有两个铜箔中间夹着一个基材,
  • 铜箔构成PCB的顶层和底层,负责PCB的走线,完成信号的传递,
  • 铜箔外侧有丝印,阻焊等等,同时也负责元器件的摆放。
  • 铜箔内部有基材,通常以RF-4为多,这个部分占PCB的大部分厚度,为此PCB的基本外观的硬度
  • 具体结构大体如下(这个手画的有点抽象):
    在这里插入图片描述

有了这个概念,我们就可以进行理论推导了:

首先,电容的充电公式如下:Vc=E (1-e (-t/R*C))
其中:

  • Vc为电容两端的电压,其随时间变化
  • E为电容充电的最大值,即充满电的的电压
  • e为自然对数的底数
  • t为时间
  • R为电容充电回路的电阻
  • C为电容的容值

通过以上公式,我们可以得到曲线
在这里插入图片描述

再看原理图:

在这里插入图片描述
在这里插入图片描述

  • 通过通过电阻R32和前面板材PCB铜箔之间的电容1(这里叫Cs),构成充放电回路,STM_ADC充当开关,控制PB1的充放电
  • 当手指触摸板材时,铜箔与手指之间的电容2(这里叫Cx)加入充电回路,电容增加,充电速度变慢,换句话说,电容达到相同电压的时间变成,利用这一区别,我们就能实现电容触摸按键的检测
    在这里插入图片描述
  • 图中Cth表示单片机GPIO识别为高电平的最小值。当Vc小于Vth时,识别为低电平,反之识别为高电平。越过Vth的过程可以被单片机识别为上升沿。可通过定时器的输入捕获功能捕捉
    具体配置过程如下:
  1. TPAD引脚设置为推挽输出,输出低电平,使电容放电
  2. TPAD引脚设置为浮空输入,电容开始充电
  3. 开启TPAD输入捕获功能,捕获上升沿(即电压达到Vth的时刻)
  4. 等待充电过程,直至捕获成功
  5. 计算充电时间。

CubeMX配置

由于电容触摸按键检测过程中TPAD引脚模式会频繁切换,所以我们再CubeMX中不进行配置,直接从代码中自行编写配置过程。

代码展示&讲解

代码源码来自正点原子,经稍加修改便于移植和理解

TPAD.c

#include "tpad.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK NANO STM32F4开发板
//TPAD驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2019/4/23
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2019-2029
//All rights reserved									  
// 	
TIM_HandleTypeDef TIM3_Handler;         //定时器3句柄 

#define TPAD_ARR_MAX_VAL  0XFFFF		//最大的ARR值(TIM3是16位定时器)	  
vu16 tpad_default_val=0;				没有手指按下获取的充电时间(这里指Vc从0到Vth的充电时间)

//触摸按键初始化函数,获得没有手指按下时的充电时间平均值
//psc:Timer3时钟(100MHz)分频系数,越小,计数器加1时间间隔越短,灵敏度越高.
//返回值:0,初始化成功;1,初始化失败
uint8_t TPAD_Init(uint8_t psc)
{
	uint16_t buf[10];      //存放十次没有手指按下时的充电时间
	uint16_t temp;         //临时变量
	uint8_t j,i;           //计数变量
	TIM3_CH4_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//设置时钟分频系数
	for(i=0;i<10;i++)//连续读取10次没有触摸时的充电时间
	{				 
		buf[i]=TPAD_Get_Val();    //将读取的充电时间存入数组
//		HAL_Delay(10);	          //延时,实测好像没有什么用,不过源码中有
	}
    
    //对读取的时间进行排序,去除两个最大值和两个最小值,算平均数    
	for(i=0;i<9;i++)//排序    
	{
		for(j=i+1;j<10;j++)
		{
			if(buf[i]>buf[j])//升序排列
			{
				temp=buf[i];
				buf[i]=buf[j];
				buf[j]=temp;
			}
		}
	}
	temp=0;
	for(i=2;i<8;i++)temp+=buf[i];//取中间的6个数据进行平均
	tpad_default_val=temp/6;
	printf("tpad_default_val:%d\r\n",tpad_default_val);	  //获取到十次手指未按下时读取时间的平均值
	if(tpad_default_val>(vu16)TPAD_ARR_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!
	return 0;		     	    					   
}
//复位一次
//释放电容电量,并清除定时器的计数值
void TPAD_Reset(void)
{
    GPIO_InitTypeDef GPIO_Initure;
	
    GPIO_Initure.Pin=GPIO_PIN_1;            //PB1
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLDOWN;        //下拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
    
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	//PB1输出0,放电
    HAL_Delay(5);      //等待放电完成
    __HAL_TIM_CLEAR_FLAG(&TIM3_Handler,TIM_FLAG_CC4|TIM_FLAG_UPDATE);   //清除标志位
    __HAL_TIM_SET_COUNTER(&TIM3_Handler,0); //计数器值归0
    
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;  	//推挽复用
    GPIO_Initure.Pull=GPIO_NOPULL;          //不带上下拉
	GPIO_Initure.Alternate=GPIO_AF2_TIM3;   //PB1复用为TIM3通道4
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);         
}

//得到定时器捕获值
//如果超时,则直接返回定时器的计数值.
//返回值:捕获值/计数值(超时的情况下返回)
uint16_t TPAD_Get_Val(void)   
{
    TPAD_Reset();     //TPAD引脚放电,设置为定时器输入捕获上升沿触发模式
    while(__HAL_TIM_GET_FLAG(&TIM3_Handler,TIM_FLAG_CC4)==RESET) //等待TPAD引脚捕获到上升沿
    {
        if(__HAL_TIM_GET_COUNTER(&TIM3_Handler)>TPAD_ARR_MAX_VAL-500)   //一直未检测到上升沿,且时间过大时返回cnt
        {                                                               //此时过大的cnt返回,会被当成最大值去除掉
          return __HAL_TIM_GET_COUNTER(&TIM3_Handler);//超时了,直接返回CNT的值  
        }
          
    };
    return HAL_TIM_ReadCapturedValue(&TIM3_Handler,TIM_CHANNEL_4);      //捕获到上升沿,返回正常充电时间
}


//读取n次,取最大值
//n:连续获取的次数
//返回值:n次读数里面读到的最大读数值
uint16_t TPAD_Get_MaxVal(uint8_t n)
{ 
	uint16_t temp=0; 
	uint16_t res=0;      //最大值结果
	uint8_t lcntnum=n*2/3;//至少2/3*n的有效个触摸,才算有效
	uint8_t okcnt=0;
	while(n--)
	{
		temp=TPAD_Get_Val();//得到一次值
		if(temp>(tpad_default_val*5/4))okcnt++;//至少大于默认值的5/4才算有效
		if(temp>res)res=temp;
	}
	if(okcnt>=lcntnum)return res;//至少2/3的概率,要大于默认值的5/4才算有效
	else return 0;
}  

//扫描触摸按键
//mode:0,不支持连续触发(按下一次必须松开才能按下一次);1,支持连续触发(可以一直按下)
//返回值:0,没有按下;1,有按下;	
#define TPAD_GATE_VAL 	30	//触摸的门限值,也就是必须大于tpad_default_val+TPAD_GATE_VAL,才认为是有效触摸.
uint8_t TPAD_Scan(uint8_t mode)
{
	static uint8_t keyen=0;	//0,可以开始检测;>0,还不能开始检测	 
	uint8_t res=0;
	uint8_t sample=3;		//默认采样次数为3次	 
	uint16_t rval;
	if(mode)
	{
		sample=6;		//支持连按的时候,设置采样次数为6次
		keyen=0;		//支持连按	  
	}
	rval=TPAD_Get_MaxVal(sample);  //采集的最大值
	if(rval>(tpad_default_val+TPAD_GATE_VAL))//大于tpad_default_val+TPAD_GATE_VAL,有效
	{							 
		if(keyen==0)res=1;	//keyen==0,有效 
		//printf("r:%d\r\n",rval);		     	    					   
		keyen=3;				//至少要再过3次之后才能按键有效   
	} 
	if(keyen)keyen--;		   							   		     	    					   
	return res;
}	

//定时器3通道4输入捕获配置
//arr:自动重装值(TIM2是16位的!!)
//psc:时钟预分频数
void TIM3_CH4_Cap_Init(uint32_t arr,uint16_t psc)
{  
    TIM_IC_InitTypeDef TIM3_CH4Config;  
    
    TIM3_Handler.Instance=TIM3;                          //通用定时器3
    TIM3_Handler.Init.Prescaler=psc;                     //分频系数
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器
    TIM3_Handler.Init.Period=arr;                        //自动装载值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//分频因子
    HAL_TIM_IC_Init(&TIM3_Handler);
    
    TIM3_CH4Config.ICPolarity=TIM_ICPOLARITY_RISING;    //上升沿捕获
    TIM3_CH4Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到TI1上
    TIM3_CH4Config.ICPrescaler=TIM_ICPSC_DIV1;          //配置输入分频,不分频
    TIM3_CH4Config.ICFilter=0;                          //配置输入滤波器,不滤波
    HAL_TIM_IC_ConfigChannel(&TIM3_Handler,&TIM3_CH4Config,TIM_CHANNEL_4);//配置TIM3通道4
    HAL_TIM_IC_Start(&TIM3_Handler,TIM_CHANNEL_4);      //开始捕获TIM3的通道4
}

//定时器3底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_IC_Init()调用
//htim:定时器3句柄
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();			//开启GPIOB时钟
	
    GPIO_Initure.Pin=GPIO_PIN_1;            //PB1
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;  	 //推挽复用
    GPIO_Initure.Pull=GPIO_NOPULL;          //不带上下拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
	GPIO_Initure.Alternate=GPIO_AF2_TIM3;   //PB1复用为TIM3通道4
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}

TPAD.h

#ifndef __TPAD_H
#define __TPAD_H

#include "main.h"
#include "usart.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK NANO STM32F4开发板
//TPAD驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2019/4/23
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2019-2029
//All rights reserved									  
// 

//空载的时候(没有手按下),计数器需要的时间
//这个值应该在每次开机的时候被初始化一次
typedef __IO uint16_t vu16;
extern vu16 tpad_default_val;
							   	    
void TPAD_Reset(void);
uint16_t  TPAD_Get_Val(void);
uint16_t TPAD_Get_MaxVal(uint8_t n);
uint8_t   TPAD_Init(uint8_t systick);
uint8_t   TPAD_Scan(uint8_t mode);
void TIM3_CH4_Cap_Init(uint32_t arr,uint16_t psc);
#endif


不得不说,正点原子写的代码还是太妙了,所以我们在这里斗胆来窥探一下:

  1. void TPAD_Reset(void);
    在这里插入图片描述

这里负责的是TPAD引脚的复位,完成TPAD引脚的放电,并设置为输入捕获功能,这是进行充电时间读取前的必备操作

  1. uint16_t TPAD_Get_Val(void);
    在这里插入图片描述

这里读取的是电容充电时间,不论是否超时都会被读取

  1. uint16_t TPAD_Get_MaxVal(uint8_t n);
    在这里插入图片描述

这里是读取有手指按下的最大充电时间,当没有手指按下时,返回0
这里的5/4只是个判断标准,确保是有手指按下的情况
2/3则是用来判误触,静电等情况

  1. uint8_t TPAD_Init(uint8_t systick);
//触摸按键初始化函数,获得没有手指按下时的充电时间平均值
//psc:Timer3时钟(100MHz)分频系数,越小,计数器加1时间间隔越短,灵敏度越高.
//返回值:0,初始化成功;1,初始化失败
uint8_t TPAD_Init(uint8_t psc)
{
	uint16_t buf[10];      //存放十次没有手指按下时的充电时间
	uint16_t temp;         //临时变量
	uint8_t j,i;           //计数变量
	TIM3_CH4_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//设置时钟分频系数
	for(i=0;i<10;i++)//连续读取10次没有触摸时的充电时间
	{				 
		buf[i]=TPAD_Get_Val();    //将读取的充电时间存入数组
//		HAL_Delay(10);	          //延时,实测好像没有什么用,不过源码中有
	}
    
    //对读取的时间进行排序,去除两个最大值和两个最小值,算平均数    
	for(i=0;i<9;i++)//排序    
	{
		for(j=i+1;j<10;j++)
		{
			if(buf[i]>buf[j])//升序排列
			{
				temp=buf[i];
				buf[i]=buf[j];
				buf[j]=temp;
			}
		}
	}
	temp=0;
	for(i=2;i<8;i++)temp+=buf[i];//取中间的6个数据进行平均
	tpad_default_val=temp/6;
	printf("tpad_default_val:%d\r\n",tpad_default_val);	  //获取到十次手指未按下时读取时间的平均值
	if(tpad_default_val>(vu16)TPAD_ARR_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!
	return 0;		     	    					   
}

这里则是完成TPAD模块的初始化。读取10次手指未按下时的充电时间
排序去除最大值和最小值是为了减小未捕获上升沿,或过快捕获到上升沿导致的误差

  1. uint8_t TPAD_Scan(uint8_t mode);

在这里插入图片描述

  • 扫描函数可以分为支持连续触发和单次触发
  • 单次触发:第一次触发时会将res置1,后返回表示按键按下,后续计入函数,如果手指一直不拿开,会导致res无法置1(keyen一直等于3),返回0表示按键为按下
  • 连续触发:无论那一次触发进入函数,keyen都会被置1,后返回1表示按键按下。

以上就是本期的全部内容,emmm

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

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

相关文章

mybatis注解方式动态标签时有特殊符号,出现元素内容必须由格式正确的字符数据或标记组成

原始代码demo Select("SELECT COUNT(1) FROM AAAA WHERE name #{nage} AND age< 4") public Integer sumXxxxx(String nage, String age);现需求改为nage可以为空&#xff0c;因此使用了动态拼接 Select("<script> SELECT COUNT(1) FROM AAAA WHERE …

ruoyi-plus使用Statistic统计组件升级element-plus

原本使用的就是gitee上lionli的ruoyi-plus版本的代码。但是在使用过程中作首页数据看板时想使用elementui的Statistic统计组件。结果在浏览器控制台报错找不到组件el-statistic 于是查看elementui的历史版本&#xff0c;发现是在新版中才有这个组件&#xff0c;旧版本是没这个组…

常见树种(贵州省):012茶、花椒、八角、肉桂、杜仲、厚朴、枸杞、忍冬

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、茶 灌…

读像火箭科学家一样思考笔记07_探月思维

1. 挑战“不可能”的科学与企业 1.1. 互联网 1.1.1. 和电网一样具有革命性&#xff0c;一旦你插上电源&#xff0c;就能让自己的生活充满活力 1.1.2. 互联网的接入可以帮助人们摆脱贫困&#xff0c;拯救生命 1.1.3. 互联网还可以提供与天气相关的信息 1.2. 用廉价、可靠的…

实例讲解:在3dMax中如何使用python脚本?

如果你是Python或Maxscript的新手&#xff0c;你现在可以跟着这篇文章开始做一些代码了&#xff0c;本文将让我们从非常基本的东西开始学习。 如何在3dmax中获取选定的节点并打印出它们的名称&#xff1f;所有场景对象如何&#xff1f;我们直接看代码&#xff1a; import MaxP…

常见树种(贵州省):014槭树、梧桐、鹅掌楸、檫木、梓木、油桐、泡桐、川楝、麻楝

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、色木槭…

Windows如何截取屏幕图片以及动态图

在制作PPT或是其他演示文稿或是说明文档的时候&#xff0c; 常常需要截取网页或是屏幕的截图&#xff0c;在Windows中有多种方式可以实现截取屏幕。 Windows 截取屏幕图片的方式 在Windows 中截取屏幕中某个区块的方式有&#xff1a; 方式1. 最原始的方式&#xff1a; 点击 …

旋转的数组

分享今天看到的一个题目&#xff0c;不同思路解法 题目 思路1&#xff1a;时间复杂度0(N*k&#xff09; void rotate(int *a,int N,int k)//N为数组元素个数 { while(k--) { int tema[N-1]; for(int rightN-2;right>0;right--) { a[right1]a[right]; } a[0]tem; …

飞翔的小鸟——Java

一、创建文件、包、类、插入图片文件 二、app包 1、Gameapp类&#xff08;运行游戏&#xff09; package app;import main.GameFrame;public class Gameapp {public static void main(String[] args) {//游戏的入口new GameFrame();} } 三、main包 1、Barrier&#xff08…

【华为OD题库-037】跳房子2-java

题目 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏游戏。参与者需要分多个回合按顺序跳到第1格直到房子的最后一格&#xff0c;然后获得一次选房子的机会&#xff0c;直到所有房子被选完&#xff0c;房子最多的人获胜。 跳房子的过程中&#xff0c;如果有踩…

React中如何解决点击<Tree>节点前面三角区域不触发onClick事件

React中如何解决点击节点前面三角区域不触发onClick事件&#xff0c;如何区别‘左边’和‘右边’区域点击逻辑呢&#xff1f;&#xff08;Tree引用开源组件TDesign&#xff09; 只需要在onClick里面加限制一下就行&#xff1a; <TreeexpandMutexactivabletransitiondata{t…

Word/PPT/PDF怎么免费转为JPG图片?

1、打开金鸣表格文字识别网站。 2、点击导航条上的“软件下载” 3、安装并打开金鸣表格文字识别软件。 4、点击顶部导航栏的“文件转图片”。 5、选择需要转换成图片的文件&#xff08;支持Word/PPT/PDF&#xff09;. 6、点“打开”程序将自动分页转换为图片。

【SpringBoot3+Vue3】五【完】【实战篇】-前端(配合后端)

目录 一、环境准备 1、创建Vue工程 2、安装依赖 2.1 安装项目所需要的vue依赖 2.2 安装element-plus依赖 2.2.1 安装 2.2.2 项目导入element-plus 2.3 安装axios依赖 2.4 安装sass依赖 3、目录调整 3.1 删除部分默认目录下文件 3.1.1 src/components下自动生成的…

外贸自建站服务器怎么选?网站搭建的工具?

外贸自建站服务器用哪个好&#xff1f;如何选海洋建站的服务器&#xff1f; 外贸自建站是企业拓展海外市场的重要手段之一。而在这个过程中&#xff0c;选择一个适合的服务器对于网站的稳定运行和优化至关重要。海洋建站将为您介绍如何选择适合的外贸自建站服务器。 外贸自建…

干货 | 携程酒店基于血缘元数据的数据流程优化实践

作者简介 九号&#xff0c;携程数据技术专家&#xff0c;关注数据仓库架构、数据湖、流式计算、数据治理。 一、背景 元数据MetaData狭义的解释是用来描述数据的数据&#xff0c;广义的来看&#xff0c;除了业务逻辑直接读写处理的那些业务数据&#xff0c;所有其它用来维持整个…

C语言之字符串函数

C语言之字符串函数 文章目录 C语言之字符串函数1. strlen的使用和模拟实现1.1 strlen的使用1.2 strlen的模拟实现 2. strcpy的使用和模拟实现2.1 strcpy的使用2.2 strncpy的使用2.3 strcpy的模拟实现 3. strcat的使用和模拟实现3.1 strcat的使用3.2 strncat3.3 strcat的模拟实现…

深度学习中的Dropout正则化:原理、代码实现与实际应用——pytorch框架下如何使用dropout正则化

目录 引言 一、导入包 二、dropout网络定义 三、创建模型&#xff0c;定义损失函数和优化器 四、加载数据 五、训练train 六、测试 引言 dropout正则化的原理相对简单但非常有效。它在训练神经网络时&#xff0c;以一定的概率&#xff08;通常是在0.2到0.5之间&#xff…

基于C#实现赫夫曼树

赫夫曼树又称最优二叉树&#xff0c;也就是带权路径最短的树&#xff0c;对于赫夫曼树&#xff0c;我想大家对它是非常的熟悉&#xff0c;也知道它的应用场景&#xff0c;但是有没有自己亲手写过&#xff0c;这个我就不清楚了&#xff0c;不管以前写没写&#xff0c;这一篇我们…

GeoTrust SSL数字安全证书介绍

一、GeoTrust OV证书的介绍 GeoTrust OV证书是由GeoTrust公司提供的SSL证书&#xff0c;它是一种支持OpenSSL的数字证书&#xff0c;具有更高的安全性和可信度。GeoTrust是全球领先的网络安全解决方案提供商&#xff0c;为各类用户提供SSL证书和信任管理服务。GeoTrust OV证书…

Docker+ Jenkins+Maven+git自动化部署

环境&#xff1a;Centos7 JDK1.8 Maven3.3.9 Git 2.40 Docker 20.10.17 准备工作&#xff1a; 安装Docker Centos7默认的yum安装的docker是1.13&#xff0c;版本太低&#xff0c;很多镜像都要Docker版本要求&#xff0c;升级Docker版本。 卸载已安装Docker: yum …