STM32 通用定时器

news2024/11/28 12:54:09

一、概述

        STM32内部集成了多个定时/计数器,根据型号不同,STM32系列芯片最多包含8个定时/计数器。其中,TIM6、TIM7为基本定时器,TIM2~TIM5为通用定时器,TIM1、TIM8为高级控制定时器。

1.定时器的类型

  • 基本定时器
  • 通用定时器
  • 高级控制定时器
  • 窗口看门狗定时器
  • 独立看门狗定时器
  • 系统滴答定时器

2.计数模式

  • 向上计数模式:计数器从0计数到自动加载值(ARR),并产生向上溢出事件。

  • 向下计数模式:计数器从自动加载值(ARR)向下计数到0,并产生向下溢出事件。 

  • 中央对齐模式:计数器从0开始计数到自动加载值-1,产生向上溢出事件,然后向下计数到1,产生向下溢出事件,最后再从0开始重新计数。

3.主要功能 

  • 基本定时功能
  •  输出比较
  • 输入捕获
  • 编码器接口模式
  • 单脉冲模式
  • 死区控制和刹车功能

        注:本文将介绍前四种常见的功能。 

4.通用定时器的结构

        STM32通用定时器主要包括1个外部触发引脚(TIMx_ETR),4个输入/输出通道(TIMx_CH1、 TIMx_CH2、TIMx_CH3和TIMx_CH4),1个内部时钟1个触发控制器1个时钟单元(由预分频器PSC、自动重装载寄存器ARR和计数器CNT组成)。如图所示:

5.时钟源

        定时/计数器时钟可由下列时钟源(如上图所示)提供: 

  • 内部时钟(CK_INT)
  • 外部时钟模式1(TIMx_CH1~4)
  • 外部时钟模式2(TIMx_ETR)
  • 内部触发输入(ITR,使用一个定时器作为另一个定时器的预分频器)

        当时钟源来自内部时,可实现定时功能;当时钟源来自外部时,可实现计数功能。

6.功能寄存器

        略。

二、基本定时功能

        下面介绍TIM定时器最基础的功能:基本定时功能。这种功能常用于周期性事件的触发。使用流程和步骤如下:

  1. 选择时钟源
  2. 配置时基单元
  3. 配置NVIC
  4. 编写中断服务函数
void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
    //开启TIM2的时钟
	
	TIM_InternalClockConfig(TIM2);		
    //选择时钟源为内部时钟,若不调用此函数,TIM2默认也为内部时钟
	
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;			
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						
    //清除定时器更新标志位,TIM_TimeBaseInit函数末尾,手动产生了更新事件,
    //若不清除此标志位,则开启中断后,会立刻进入一次中断,
    //如果不介意此问题,则不清除此标志位也可。
																
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//设置NVIC分组

	/*配置NVIC*/
	NVIC_InitTypeDef NVIC_InitStructure;						
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//设置抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//设置响应优先级为1
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM2, ENABLE);			                            //使能TIM2,运行TIM2
}




void TIM2_IRQHandler(void)                                      //定时器中断函数
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{    
            
        //此处编写要周期实现的功能
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

 三、输出比较功能

         当定时器的计数器值(CNT)与捕获比较寄存器(CCR)的值相等时,产生比较事件,并根据配置对输出管脚进行相应的操作,如翻转或置位。其应用场景如下:

  • PWM(脉宽调制)信号的产生:输出占空比可调的PWM信号,用于电机控制、LED调光等。
  • 定时脉冲:在特定时间产生一个脉冲信号,用于精确事件触发。
  • DAC触发:精确触发模拟信号输出。

        下面介绍产生PWM波的使用流程:

  1.  配置GPIO,用于输出PWM,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输出比较模式
void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                 //复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);								
	//受外设控制的引脚,均需要配置为复用模式
	

	TIM_InternalClockConfig(TIM2);		
    //选择时钟源为内部时钟,若不调用此函数,TIM默认也为内部时钟
	

	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                 
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;               
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            

	
	/*配置输出比较模式*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;	
	TIM_OCStructInit(&TIM_OCInitStructure);                         
    //结构体初始化,若结构体没有完整赋值,则最好执行此函数,给结构体所有成员都赋一个默认值,避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               
    //输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       
    //输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   
    //输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								
    //初始的CCR值,也可以不定为0,直接定为想要的占空比所需的CCR值
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);                        


	TIM_Cmd(TIM2, ENABLE);			
    //使能TIM2,运行TIM2
}


TIM_SetCompare3(TIM2, Compare);		            
//设置CCR的值,设置CCR几的值取决于使用哪个引脚,PA2对应的是CCR3
//该行代码用于改变占空比,一般放在主函数或者中断服务函数

 另外,PWM的三项重要数据的计算方法如下:

  1. 占空比:CCR/(ARR+1)
  2. 分辨率:1/(ARR+1)
  3. 频率:CK_PSC/(PSC+1)/(ARR+1),CK_PSC一般为72MHz

四、输入捕获功能 

        输入捕获模式用于测量外部信号的时间特性,例如周期、频率、脉宽等。它通过将外部输入信号的某个边沿(上升沿或下降沿)捕获并保存计数器的值,从而实现时间测量。 

        下面介绍通过输入捕获功能实现频率测量的步骤:

  1.  配置GPIO,用于接收需要测频率的信号,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输入捕获功能
  5. 编写频率计算函数
void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				    //上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);			
	

	TIM_InternalClockConfig(TIM3);		
    //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

	
	/*配置输入捕获功能*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				
    //选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		
    //极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			
    //捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	
    //输入信号交叉,选择直通,不交叉
	TIM_ICInit(TIM3, &TIM_ICInitStructure);	
	

	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					
    //触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					
    //从模式选择复位,即TI1产生上升沿时,会触发CNT归零
	

	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,运行TIM3
}





uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		
    //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

         频率测量方法有两种,一种是适用于测量高频信号的测频法,一种是适用于测量低频信号的测周法。其原理如下图所示:

 五、编码器模式

        编码器接口模式用于解码旋转编码器的信号。它可以直接连接增量型旋转编码器的A相和B相信号,并解码出编码器的旋转方向和位置。 每个高级定时器和通用定时器都拥有1个编码器接口,两个输入引脚借用了输入捕获的通道1和通道2。

        下面介绍使用编码器模式来测量电机转速的步骤,硬件上将带编码器的电机的编码输出连接到STM32的PA6和PA7,具体如下:

  1. 配置GPIO,用于接收正交编码,根据引脚定义表配置
  2. 配置时基单元
  3. 配置输入捕获模式
  4. 配置编码器模式
  5. 配置另一个定时器,编写速度计算函数
void Encoder_Init(void)
{
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

    /*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           //浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          //配置PA6和PA7引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	  
  
    /*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;                //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //使用TIM3
	

    /*配置输入捕获模式*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							
	TIM_ICStructInit(&TIM_ICInitStructure);																																		
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				
	TIM_ICInitStructure.TIM_ICFilter = 0x10;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInit(TIM3, &TIM_ICInitStructure);	
						
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				
	TIM_ICInitStructure.TIM_ICFilter = 0x10;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInit(TIM3, &TIM_ICInitStructure);							
	
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);
	//配置编码器模式以及两个输入通道是否反相

	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	
	TIM_SetCounter(TIM3, 0);
	
	TIM_Cmd(TIM3, ENABLE);			                                //使能TIM3
}


//使用二、基本定时功能,在中断服务函数中编写计算速度的代码。
//这里需要另外配置一个定时器,相关代码参考第二点,这里不再赘述。
//先计算电机转一圈,STM32收到的n个编码;这取决于电机本身的参数。
//再每隔T时间输出STM32一共接收到的N个编码;则N/n即这段时间T里电机转过的圈数。
//最后用N/n/T即可求出转速。其中:

    int n=xxx                     //根据电机参数计算
    int N=TIM_GetCounter(TIM3);   //STM32接收到的编码数
    TIM_SetCounter(TIM3, 0);      //拿到T时间内的编码数后,计数清零,重新计数
    float Speed=N/n/T;            //T为定时周期
    
    

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

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

相关文章

C/C++ 中的未定义行为(Undefined Behavior, UB)

0. 简介 在 C/C 编程中,理解未定义行为(UB)及其相关概念至关重要。本文将对未定义行为进行详细解析,并通过实例展示其影响与处理方法。 1. 概念辨析 在 C/C 中,未定义行为容易与以下两个概念混淆: 1.1 …

【Spring】Spring MVC的项目准备和连接建立

文章目录 1. 什么是 Spring Web MVC1.1 MVC 定义1.2 什么是 Spring MVC 2. 学习 Spring MVC2.1 项目准备2.2 建立连接 1. 什么是 Spring Web MVC Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从已开是就包含在 Spring 框架中。它的正式名称“Spring We…

【pytorch】张量求导

笔者看到了这篇文章,可以很好的解释张量的求导问题: 看到了上面这张图,可以说很好的表示了前向和反向的过程了。 补充几个细节 之前看李沐的d2l,一直不懂为什么矩阵计算时的一些奇奇怪怪的规定,比如为什么一个行向量…

github项目——gpt-pilot自动创建应用

今天扯一扯在github上看到的一个项目gpt-pilot,声称“首个AI程序员”。本来打算玩一下,结果需要配置大语言模型的API,并且只支持OpenAI和claude(Qwen呢)。有没有玩过的老哥说一下好不好用!!(对了…

【Postman】接口测试工具使用

干就完啦 Postman发送get请求案例1: Postman发送post请求案例2 Postman发送其他请求 学习目标:能够使用Postman发送get/post/put/delete请求并获取响应结果 Postman发送get请求 首先postman是一款接口调试工具,支持win,mac以及l…

Python | Leetcode Python题解之第456题132模式

题目: 题解: class Solution:def find132pattern(self, nums: List[int]) -> bool:candidate_i, candidate_j [-nums[0]], [-nums[0]]for v in nums[1:]:idx_i bisect.bisect_right(candidate_i, -v)idx_j bisect.bisect_left(candidate_j, -v)if…

Pandas -----------------------基础知识(六)

目录 数据类型 查看类型 类型转换 无法转换的值返回NaN 无法转换的值返回原值 datetime类型 datetime类型数据列作为df索引 Python中的timedelta类型 Pandas中的timedelta类型 pd.to_timedelta函数转换timedelta类型 timedelta类型数据作为df索引 分组groupby 分箱…

开发环境简单介绍

目录 开发环境keil的安装和使用 keil的介绍 keil的安装 keil的简单使用 STC-ISP的安装 STC-ISP简单介绍 开发环境测试 总结 开发环境keil的安装和使用 keil的介绍 Keil uVision5是一个集成开发环境(IDE),用于对嵌入式系统中的微控制器…

vue-scrollto实现页面组件锚点定位

文章目录 前言背景操作指南安装及配置步骤vue组件中使用 参考文章 前言 博主介绍:✌目前全网粉丝3W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容:Java后端、大数据…

Java | Leetcode Java题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; class Solution {public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {Map<Integer, Integer> countAB new HashMap<Integer, Integer>();for (int u : A) {for (int v : B) {countAB.put(u v, countAB.getOrDefa…

多模态—文字生成图片

DALL-E是一个用于文字生成图片的模型&#xff0c;这也是一个很好思路的模型。该模型的训练分为两个阶段&#xff1a; 第一阶段&#xff1a;图片经过编码器编码为图片向量&#xff0c;当然我们应该注意这个过程存在无损压缩&#xff08;图片假设200*200&#xff0c;如果用one-h…

VBA中类的解读及应用第十六讲:让文本框在激活时改变颜色(中)

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

数据链路层(以太网简介)

一.以太网数据帧结构&#xff1a; 目的地址&#xff0c;源地址&#xff0c;类型这三个被称为帧头&#xff0c;数据则被称为载荷&#xff0c;CRC则被称为帧尾&#xff08;校验和&#xff09; 二.数据帧结构分析 1.目的地址和源地址 i.地址解释 这两个地址指的是mac地址&#x…

【AIGC】2022-NIPS-视频扩散模型

2022-NIPS-Video Diffusion Models 视频扩散模型摘要1. 引言2. 背景3. 视频扩散模型3.1. 重建引导采样以改进条件生成 4. 实验4.1. 无条件视频建模4.2. 视频预测4.3. 文本条件视频生成4.3.1 视频与图像建模的联合训练4.3.2 无分类器指导的效果4.3.3 更长序列的自回归视频扩展 5…

数通 2

一 网络层 数据传输中最大支持1518字节&#xff0c;所以超过这个一次传不过去&#xff0c;就要分开传&#xff0c;就像快递标记1/2, 2/2 。说明你有两包 下图例子解释了 identification 用于标识一台设备发送的数据 片偏移&#xff0c;就是 你 好 吗 三个分片谁先到达不一定&…

C语言 | Leetcode C语言题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; int next(int* nums, int numsSize, int cur) {return ((cur nums[cur]) % numsSize numsSize) % numsSize; // 保证返回值在 [0,n) 中 }bool circularArrayLoop(int* nums, int numsSize) {for (int i 0; i < numsSize; i) {if (!n…

vue-live2d看板娘集成方案设计使用教程

文章目录 前言v1.1.x版本&#xff1a;vue集成看板娘&#xff08;暂不使用&#xff0c;在v1.2.x已替换&#xff09;集成看板娘实现看板娘拖拽效果方案资源备份存储 当前最新调研&#xff1a;2024.10.2开源方案1&#xff1a;OhMyLive2D&#xff08;推荐&#xff09;开源方案2&…

小米 MIX FOLD工程固件 更换字库修复分区 资源预览与刷写说明

小米 MIX FOLD机型代号 :cetus 该手机搭载骁龙888旗舰处理器 。对于一些因为字库问题损坏导致的故障,更换字库后要先刷写对应的工程底层修复固件。绑定cpu后在写入miui量产固件。 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2💝💝💝-----此…

合肥企业参访:走进联想合肥智能制造基地参观学习

跟随华研标杆游学高丽华高老师去到联想参观游学 联想合肥智能制造基地成立于2011年&#xff0c;是联想集团全球蕞大的PC研发和制造基地&#xff0c;也是智能制造示范基地。基地占地约500亩&#xff0c;拥有全球PC制造业蕞大的单体厂房以及业界主板、整机生产线。在这里&#xf…

fiddler抓包17_简单接口测试(Composer请求编辑)

课程大纲 ① 进入“Composer”&#xff08;请求编辑&#xff09;界面&#xff1a; Fiddler右侧标签菜单选择“Composer”&#xff0c;中文“请求编辑” 。 ② 编辑、发送请求&#xff1a; 填写接口请求信息&#xff08;或从左侧列表直接拖拽填充&#xff09;&#xff0c;点击“…