电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
电机控制基础——定时器捕获单输入脉冲原理
分 享
扫描二维码分享
电机控制基础——定时器捕获单输入脉冲原理
电机控制
定时器
输入捕获
码农爱学习
关注
发布时间: 2021-03-01
丨
阅读: 570
上篇介绍了定时器的**输出**功能,本篇介绍定时器的**输入**功能。 # 1 问题引出 在单片机与嵌入式开发中,某些场景需要捕获传感器的高电平(或低电平)信号的持续时间,如红外解码信号、编码器输入信号等。 如下图,以单一的一段高电平输入信号为例,如何测量这段高电平的时间呢? 从直观上理解,就是要不断的检测这个信号,当信号从0变到1时,记录一个时间,再从1变到0时,记录另一个时间,两个时间差就是高电平的持续时间了。那具体要怎么编程呢?这就要用到定时器了。 ![](https://cf03.ickimg.com/bbsimages/202102/51df2b9d950e6beaf73688bb275a22c2.png) # 2 定时器的捕获原理 上篇介绍了定时器的输出功能,本篇是利用定时器的输入功能,来计算脉冲时长。如下图: - 定时器的CNT计数器在不停的计数 - 首先配置定时器的输入通道为上升沿捕获,这样当检测到从0到1的跳变时,CCR1就会先保存当前的CNT值,同时CNT会清零重新开始计数 - 然后将定时器的输入通道为下降沿捕获,当检测从1到0的跳变时,CCR2就会先保存当前的CNT值 - 在这期间,CNT的计数值可能会溢出,这不影响,记录下溢出的次数,并重新开始计数即可 - 最终,t2-t1的高电平时间,就可以通过N次的溢出时间加CCR2保存的时间来计算获得了 ![](https://cf03.ickimg.com/bbsimages/202102/ffc98c710c68fb68a89f25edbb2812dd.png) # 3 定时器常用的寄存器 上篇介绍了定时器输出PWM时用到的几个寄存器(CR、CCMR、CNT、PSC、ARR、CCR等),这里再介绍几个捕获信号时需要用到的几个寄存器: ## 3.1 捕获/比较模式寄存器CCMR1 CCMR寄存器上篇已有介绍,只是上篇仅介绍了输出模式下的功能,本篇再介绍一下它在输入模式下的功能: 这些通道可用于**输入(捕获模式)**或**输出(比较模式)模式**。通道方向通过配置相应的 CCxS 位进行定义。此寄存器的所有其它位在输入模式和输出模式下的功能均不同。对于任一给定位 - OCxx 用于说明通道配置为输出时该位对应的功能 - ICxx 则用于说明通道配置为输入时 该位对应的功能 因此,必须注意同一个位在输入阶段和输出阶段具有不同的含义。 ![](https://cf03.ickimg.com/bbsimages/202102/ad686b3488c1e0b006abae83e858bffd.png) 这里仅先介绍**输入模式**下的功能: - 位 15:12 **IC2F**:输入捕获 2 滤波器 (Input capture 2 filter) - 位 11:10 **IC2PSC[1:0]**:输入捕获 2 预分频器 (Input capture 2 prescaler) - 位 9:8 **CC2S**:捕获/比较 2 选择 (Capture/compare 2 selection) 用法参照下面的CC1S通道1 - 位 7:4 **IC1F**:输入捕获 1 滤波器 (Input capture 1 filter) 数字滤波器由事件计数器组成,每 N 个事件才视为一个有效边沿: - 0000:无滤波器 - 0001~1111:其它频率的滤波器 - 位 3:2 **IC1PSC**:输入捕获 1 预分频器 (Input capture 1 prescaler) 此位域定义 CC1 输入 (IC1) 的预分频比。只要 CC1E=0(TIMx_CCER 寄存器),预分频器便立即复位。 - 00:无预分频器,捕获输入上每检测到一个边沿便执行捕获 - 01~11:每发生 2 (4、8)个事件便执行一次捕获 - 位 1:0 **CC1S**:捕获/比较 1 选择 (Capture/Compare 1 selection),此位域定义通道方向(输入/输出)以及所使用的输入。 - 00:CC1 通道配置为输出 - 01:CC1 通道配置为输入,IC1 映射到 TI1 上 - 10:CC1 通道配置为输入,IC1 映射到 TI2 上 - 11:CC1 通道配置为输入,IC1 映射到 TRC 上。此模式仅在通过 TS 位(TIMx_SMCR 寄存器)选择内部触发输入时有效 注: 仅当通道关闭时(TIMx_CCER 中的 CC1E = 0),才可向 CC1S 位写入数据。 ## 3.2 捕获/比较使能寄存器CCER 我们要用到这个寄存器的最低 2 位, CC1E 和 CC1P。 ![](https://cf03.ickimg.com/bbsimages/202102/70406097b88142a9a313489cf13e82ed.png) - 位 15、11、7、3 **CCxNP**:捕获 /比较x 输出极性 (Capture/Comparex output Polarity)。 - CCx 通道配置为输出: CCxNP 必须保持清零。 - CCx 通道配置为输入:此位与 CCxP 配合使用,用以定义 TI1FP1/TI2FP1 的极性。请参见 CCxP 说明。 - 位 14、10、6、2 保留,必须保持复位值。 - 位 13、9、5、1 **CCxP**:捕获 /比较x 输出极性 (Capture/Comparex output Polarity)。 - CCx 通道配置为**输出**: - 0:OCx 高电平有效 - 1:OCx低电平有效 - CCx 通道配置为**输入**: CCxNP/CCxP 位可针对触发或捕获操作选择 TI1FP1 和 TI2FP1 的极性。 - 00:非反相/上升沿触发 电路对 TIxFP1 上升沿敏感 (在复位模式、外部时钟模式或触发模式下执行捕获或触发操作), TIxFP1 未反相 (在门控模式或编码器模式下执行触发操作)。 - 01:反相/下降沿触发 电路对 TIxFP1 下降沿敏感 (在复位模式、外部时钟模式或触发模式下执行捕获或触发操作), TIxFP1 反相 (在门控模式或编码器模式下执行触发操作)。 - 10:保留,不使用此配置。 - 11:非反相/上升沿和下降沿均触发 电路对 TIxFP1 上升沿和下降沿都敏感(在复位模式、外部时钟模式或触发模式下执行捕获或触发操作),TIxFP1 未反相(在门控模式下执行触发操作)。编码器模式下不得使用此配置。 - 位 12、8、4、0 **CCxE**:捕获 /比较 x 输出使能 (Capture/Comparex output enable)。 - CCx 通道配置为**输出**: - 0:关闭––OCx 未激活 - 1:开启––在相应输出引脚上输出 OCx信号 - CCx 通道配置为**输入**: 此位决定了是否可以实际将计数器值捕获到输入捕获/比较寄存器 1 (TIMx_CCR1) 中。 - 0:禁止捕获 - 1:使能捕获 ## 3.3 DMA/中断使能寄存器DIER 我们需要用到中断来处理捕获数据,所以必须开启通道 1 的捕获比较中断,即 **CC1IE **设置为 1 。 ![](https://cf03.ickimg.com/bbsimages/202102/c0ff098b68b88de6740ee6b1b83c4c50.png) - 位 15、13、7、5 保留,必须保持复位值。 - 位 14 **TDE**:触发 DMA 请求使能 (Trigger DMA request enable) - 位 12~位9 **CCxDE**:捕获/比较x DMA 请求使能 (Capture/Compare 1 DMA request enable) - 位 8 **UDE**:更新 DMA 请求使能 (Up
date DMA request enable) - 位 6 **TIE**:触发信号(TRGI)中断使能 (Trigger interrupt enable) - 位 4~位1 **CCxIE**:捕获/比较x 中断使能 (Capture/Compare 1 interrupt enable) - 位 0 **UIE**:更新中断使能 (IC1Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); } else if (TIM_ICInitStruct->TIM_Channel == TIM_Channel_2) { /@@*省略...*/ } } ``` ```c static void TI1_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection,uint16_t TIM_ICFilter) { uint16_t tmpccmr1 = 0, tmpccer = 0; /@@* 关闭通道1: 复位CC1E位 */ TIMx->CCER &= (uint16_t)~TIM_CCER_CC1E; tmpccmr1 = TIMx->CCMR1; tmpccer = TIMx->CCER; /@@* 通过设置CC1S选择为输入模式, 并配置滤波器 */ tmpccmr1 &= ((uint16_t)~TIM_CCMR1_CC1S) & ((uint16_t)~TIM_CCMR1_IC1F); tmpccmr1 |= (uint16_t)(TIM_ICSelection | (uint16_t)(TIM_ICFilter << (uint16_t)4)); /@@* 选择CC1P极性并设置CC1E位 */ tmpccer &= (uint16_t)~(TIM_CCER_CC1P | TIM_CCER_CC1NP); tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC1E); /@@* 写数据到 TIMx 的CCMR1 和 CCER 寄存器 */ TIMx->CCMR1 = tmpccmr1; TIMx->CCER = tmpccer; } ``` ```c void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) { TIMx->CCMR1 &= (uint16_t)~TIM_CCMR1_IC1PSC; /@@* 复位IC1PSC位 */ TIMx->CCMR1 |= TIM_ICPSC; /@@* 设置IC1PSC值 */ } ``` - 关于配置DIER寄存器 ![](https://cf03.ickimg.com/bbsimages/202102/3c50ea0e2db6e9bffa29560707744b1c.png) `TIM_ITConfig`函数对于中断的开启,其实就是操作`DIER`寄存器: ```c void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState) { if (NewState != DISABLE) { /@@* 使能中断 */ TIMx->DIER |= TIM_IT; } else { /@@* 失能中断 */ TIMx->DIER &= (uint16_t)~TIM_IT; } } ``` ### 4.1.4 定时器中断初始化 定时器中断的使能设置已在上面的定时器配置中设置,这里只是进行常规的配置定时器中断的优先级: ```c /@@*定时器中断配置*/ NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器 ``` ## 4.2 定时器中断服务函数 此处用到了两个全局变量,用于辅助实现高电平捕获。其中: - `TIM5CH1_CAPTURE_VAL`用来记录捕获到下降沿的时候 TIM5_CNT的值。 - `TIM5CH1_CAPTURE_STA`用来记录捕获状态,我们把它当成一个寄存器那样来使用 。其各位描述下: ![](https://cf03.ickimg.com/bbsimages/202102/773f8edde99b53e1fb04503ce7aacad6.png) ```c u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态(当中一个自制的寄存器使用,初始为0) u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位) /@@** * @brief 定时器5中断服务程序 */ void TIM5_IRQHandler(void) { if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获 (1000 0000) { /@@*定时器溢出中断*/ if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET) { if(TIM5CH1_CAPTURE_STA&0X40)/@@* 之前标记了开始信号(0100 0000) */ { if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F) /@@* 高电平太长了,计数溢出了 (0011 1111) */ { TIM5CH1_CAPTURE_STA|=0X80; /@@* (强制)标记成功捕获了一次 (1000 0000) */ TIM5CH1_CAPTURE_VAL=0XFFFFFFFF; /@@* 因为溢出次数N不能再加了,就将当前的捕获值设置为32位的最大值,等效Nmax+1*/ } else /@@* 正常情况是不会溢出,最终得出正确的高电平时间 */ { TIM5CH1_CAPTURE_STA++; /@@* 累计定时器溢出次数N */ } } else { /@@* 还没有捕获到信号时,定时器溢出后什么也不做,自己清零继续计数即可 */ } } /@@*捕获1发生捕获事件*/ if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) { /@@*捕获到一个下降沿(结束信号)*/ if(TIM5CH1_CAPTURE_STA&0X40) /@@* 之前标记了开始信号(0100 0000) */ { TIM5CH1_CAPTURE_STA|=0X80; /@@* 标记成功捕获到一次高电平脉宽 (1000 0000) */ TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); /@@* 获取当前的捕获值 */ TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); /@@* CC1P=0 重新设置为上升沿捕获,用于下次捕捉信号 */ } /@@*还未开始,第一次捕获 上升沿(起始信号) */ else { TIM5CH1_CAPTURE_STA=0; /@@* 清空 捕获状态寄存器 */ TIM5CH1_CAPTURE_VAL=0; /@@* 清空 捕获值 */ TIM5CH1_CAPTURE_STA|=0X40; /@@* 标记捕获到了上升沿 (0100 0000) */ TIM_Cmd(TIM5,DISABLE ); /@@* 关闭定时器5 */ TIM_SetCounter(TIM5,0); /@@* 清空CNT,重新从0开始计数 */ TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); /@@* CC1P=1 设置为下降沿捕获 */ TIM_Cmd(TIM5,ENABLE ); /@@* 使能定时器5 */ } } } TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位 } ``` 再来对比一下这张图: ![](https://cf03.ickimg.com/bbsimages/202102/d9af0d153c176aedbd0a0f19bfe00e0c.png) - 初始化时设置为上升沿触发,触发后(起始信号),清空CNT,重新从0开始计数,并设置为下降沿捕获 - 在之后的过程中可能会有多次定时器计数溢出,即TIM5CH1_CAPTURE_STA++(使用低6位),也即N的值 - 最后捕捉到下降沿(结束信号),TIM5CH1_CAPTURE_VAL获取当前CNT的值,也即CCRx2的值 再看主函数中: ```c while(1) { /@@* 成功捕获到了一次高电平 (1000 0000) */ if(TIM5CH1_CAPTURE_STA&0X80) { temp=TIM5CH1_CAPTURE_STA&0X3F; /@@* 获取溢出的次数N (0011 1111) */ temp*=0XFFFFFFFF; /@@* 溢出时间总和 = N*溢出计数值 */ temp+=TIM5CH1_CAPTURE_VAL; /@@* 总的高电平时间 = 溢出时间总和 + 下降沿时的计数值*/ printf("HIGH:%lld us\r\n",temp); //打印总的高点平时间 TIM5CH1_CAPTURE_STA=0; //开启下一次捕获 } } ``` 当检查`TIM5CH1_CAPTURE_STA`为捕获到1次高电平后,打印高电平的持续时间: - 总的高电平时间 =**N**(TIM5CH1_CAPTURE_STA的低6位) * **ARR**(溢出计数值)+ **CCRx2**(下降沿时的计数值) > 附:一些寄存器简写的全称 > > - ARR:auto-reload register 自动重载寄存器 > > - CCR:capture/compare register 捕获/比较寄存器 > > - PSC:prescaler 预分频器 > > - CNT:counter 计数器 > > - SR:status register 状态寄存器 > > - CCMR:capture/compare mode register 捕获/比较模式寄存器 > > - CC1S:Capture/Compare 1 selection 捕获/比较1模式选择 > > - OC1M: Output compare 1 mode 输出比较1模式 > - OC1PE:Output compare 1 preload enable 输出比较1预装载使能 > - IC1F:Input capture 1 filter 输入捕获1滤波器 > - IC1PSC:Input capture 1 prescaler 输入捕获1预分频器 > > - CCER:capture/compare enable register 捕获/比较使能寄存器 > > - CC1P:Capture/Comparex output Polarity 捕获 /比较1输出极性 > - CC1E:Capture/Comparex output enable 捕获 /比较1输出使能 > > - SMCR:slave mode control register 从模式控制寄存器 > > - DCR:DMA control register DMA 控制寄存器 > > - DIER:DMA/Interrupt enable register DMA/中断使能寄存器 > > - DMAR:DMA address for full transfer 全传输 DMA 地址 > > - OR:option register 选项寄存器
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
1
)
码农爱学习
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交