因为要实现一个捕获精度约几十个毫秒的高电平或低电平,用16位定时器没有问题,但有时又需要捕获比较长的脉宽,约几秒。鉴于精度与长度的矛盾,16位定时器不够用,于是找出了版主以前提供的32位定时器参考例子。
有如下几个问题,盼版主解答。
(1)程序里有如下定义
#define TIMxCLK_Freq 72000000000 /*!< In mHz */
不知道是什么意思?不就是72M吗?怎么多了3个0?
(2)datasheet里有这么一段用于描述使用一个定时器作为另一个的预分频器的
使用一个定时器作为另一个的预分频器这个例子使用定时器1作为定时器2的预分频器。参考图138的连接,配置如下:
● 配置定时器1为主模式,送出它的更新事件UEV做为触发输出(TIM1_CR2寄存器的MMS=’010’)。然后每次计数器溢出时输出一个周期信号。
● 配置定时器1的周期(TIM1_ARR寄存器)。
● 配置定时器2从定时器1获得输入触发(TIM2_SMCR寄存器的TS=000)
● 配置定时器2使用外部时钟模式(TIM2_SMCR寄存器的SMS=111)
● 置TIM1_CR2寄存器的CEN=1以启动定时器2。
● 置TIM1_CR1寄存器的CEN=1以启动定时器1。
红颜色部分,主机的计数器溢出,从机可以获得一个输入触发,那么下列几种情况,从机会不会也得到一个输入触发?
(1)开启主机对应上升沿或下降沿的中断,主机检测到上升沿或者下降沿中断
(2)直接把主机的计数器清0
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define TIMxCLK_Freq 72000000000 /*!< In mHz */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t MSB = 0, LSB = 1, MSB1 = 0, MSB2 = 0;
__IO uint32_t ExtSignalFreq = 0x00;
__IO uint32_t i = 0;
/**
* @brief This function handles TIM3 global interrupt request.
* @param None
* @retval : None
*/
void TIM3_IRQHandler(void)
{
/* TIM2 CCR1 Register define the MSB of the captured value
TIM3 CCR1 Register define the LSB of the captured value */
if (i == 0)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
MSB1 = TIM_GetCapture1(TIM2);
LSB = TIM_GetCapture1(TIM3);
i = 1;
}
else
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
MSB2 = TIM_GetCapture1(TIM2);
LSB = TIM_GetCapture1(TIM3);
if (MSB1 > MSB2)
{
MSB = 0xFFFF - ((MSB1 - MSB2)) - 1;
}
else
{
MSB = (MSB2 - MSB1) - 1;
}
i = 0;
/* Calculate the external signal frequency in mHz */
ExtSignalFreq = (uint32_t)(TIMxCLK_Freq / ((MSB * 65536) + LSB));
}
}
以上为中断程序的代码。
版主看我的理解是否有问题。
这段代码的timer2是从,timer3是主。timer3发生中断时,只可能捕获到
LSB = TIM_GetCapture1(TIM3);
也就是这句,但是
MSB1 = TIM_GetCapture1(TIM2)或者 MSB2 = TIM_GetCapture1(TIM2)
我觉得MSB1和MSB2只可能是0,原因如下
datasheet里有这么一段
若CC1通道配置为输入: CCR1包含了由上一次输入捕获1事件(IC1)传输的计数器值。
从定时器timer2根本就没有产生捕获事件,它的值只能是0
确实是多了3个0。
虽然程序中没有解释,但我认为这是因为计数器精度提高到32位,如果不把计算精度也提高,进行整数运算时,可以得到的结果要损失很多精度甚至为0,尤其是结果接近1Hz或小于1Hz时。
程序中是这样使用这个定义的:ExtSignalFreq = (uint32_t)(TIMxCLK_Freq / ((MSB * 65536) + LSB));
因为没有地方用到这个运算结果ExtSignalFreq ,把结果扩大1000倍更容易观察测量数值相对输入信号的变化。
于是我改成如下
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static uint32_t LSB = 0,MSB = 0;
/**
* @brief This function handles TIM3 global interrupt request.
* @param None
* @retval : None
*/
void TIM3_IRQHandler(void)
{
/* TIM2 CCR1 Register define the MSB of the captured value
TIM3 CCR1 Register define the LSB of the captured value */
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) // 检测到上升沿 计算低电平
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
MSB = TIM_GetCounter(TIM2);
LSB = TIM_GetCapture1(TIM3);
TIM_SetCounter(TIM2,0);
TIM_SetCounter(TIM3,0);
printf("M:%d,L:%d\r\n",MSB,LSB);
}
if(TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) // // 检测到下降沿 计算高电平
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
MSB = TIM_GetCounter(TIM2);
LSB = TIM_GetCapture1(TIM3);
TIM_SetCounter(TIM2,0);
TIM_SetCounter(TIM3,0);
printf("1M:%d,L:%d\r\n",MSB,LSB);
}
}
程序运行的结果是总是和理论值对应不上,16位的我有试过,所以对定时器计算方法和基本理解上应不会存在问题。
脉宽长一点的,计算还凑合,
短一点的,比如64us,怎么也对应不上,
虽然printf也会花些时间,用的115200,但这个值是基本固定的,可以算出来再减去,但是还是对应不上。
版主和各位兄弟看下,不胜感激涕零
楼主的其他问题在AN2592中解释了,请你先看看AN2592吧。
确实是多了3个0。
虽然程序中没有解释,但我认为这是因为计数器精度提高到32位,如果不把计算精度也提高,进行整数运算时,可以得到的结果要损失很多精度甚至为0,尤其是结果接近1Hz或小于1Hz时。
程序中是这样使 ...
谢谢版主,这个解释不错,不然我老认为我还有哪里理解不对,没法下手了,呵呵。
版主,再帮我看下其他的问题,若是我对定时器理解还不够,我再去看,呵呵
#define TIMxCLK_Freq 72000000000 /*!< In mHz */
不知道是什么意思?不就是72M吗?怎么多了3个0?
其实本身注释写得很清楚了,它的单位是 mHz (毫Hz),所以要“多3个0”
第二个问题在文档里找到答案了,如下:
更新事件(UEV)可以由下列事件产生:
- 计数器上溢或下溢
- 设置UG位
- 通过从模式控制器产生的更新
第3个问题
这段文档
Using this configuration, each time the period to be measured exceeds the 16-bit master
timer Auto-reload register, an update event is generated to clock the slave timer.
When the active edge is detected on the master and slave timer inputs, the two counter
values are copied into the master CCR1 register and the slave CCR1 register, respectively.
Since the slave is clocked by the master update event, the number of master overflow is
recorded by the slave as the MSB part of the 32-bit input capture register, the LSB is read
on the Master CCR1.
红色部分按照我的理解从定时器只是记录了主定时器溢出的次数,但是并不能够产生捕获事件。我的问题就是在这里
回12楼:不错,从定时器只是记录了主定时器溢出的次数,但是也会产生捕获事件,你仔细看看AN2592中的这张配置图:
-
-
AN2592_Input_Capture.GIF
(17.09 KB)
谢谢版主,我没有仔细看图,看程序时也自以为是,惭愧。
不过这就要求把触发信号同时接到两个定时器上,也就需要两个IO口了,有些浪费。
再次感谢版主。
把一个触发信号接到2个IO?好像还真能利用起来。
本来我是要测量高电平及低电平的宽度,需要记录好几个值,如果把一个触发信号接到2个IO口上就简单多了。
1个定时器用门控模式得到高电平,1个定时器用1个上升沿及下降沿来得到低电平,软件处理会简单很多。
需要测量高电平及低电平的宽度的应用,还有其他简单的方法没?
如果要测量高电平及低电平的宽度,只要用不到楼主位的32位定时器级联操作,并不需要把一个触发信号接到2个IO口上,STM32定时器内部有专用通道,可以使用一个I/O口输入信号,在2个通道上分别捕获上升沿和下降沿。请看定时器的结构图:
提示:要学会看框图,前面一个问题也是因为你没有仔细看图,
-
-
STM32_TIM_Input_Channels.GIF
(15.03 KB)
谢谢版主,我开始就是用的你说的这种方式,后来绕来绕去,我都被绕糊涂了,我18楼说的那段话,真是糊涂,呵呵