使用反客科技STM32F407VET6 M1的核心板,板载8M主时钟晶振(HSE),32.768kHz低速外部晶振(LSE)。含有一个用户LED以及一个用户按键。
使用定时器输入捕获测量方波高电平时间。
学生党放假在家没有信号发生器,只能用定时器产生一个方波用于测量。
没错,我测我自己。
首先生成一个PWM波,这里我选用高级定时器1也就是TIM1进行配置。
TIM1挂载在APB2高速总线上。我使用标准库将系统时钟配置为168MHZ,则该总线频率也为168MHZ。
(标准库默认的是配置HSE为时钟源,而在我这块板子上不兼容标准库的默认配置,所以我就自己配置了HSI为系统时钟,具体代码在我上一篇博客中,这里是传送门:链接: STM32F4XX 学习日志:定时器中断模拟PWM波实现呼吸灯.)
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
这里注意一下stm32f4的引脚时钟是AHB1总线的
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
我看网上的其他代码好像F1的芯片引脚时钟不是在这条总线上的,敲代码的时候如果不注意很容易忽略(别问我怎么知道的,我之前就是只改了参数,系统也不会报错)
定时器初始化为输出比较,PWM波模式1,该模式效果可以由数据手册查到。
·
·
·
·
·
算了我觉得你可能不会去查,我还是贴出来吧【狗头】
定时器周期设置为100ms也就是10HZ
计算 168 000 000 / 16800 / 1000 = 10HZ。
168M先经过16800倍数分频率得到1 000 0HZ的频率
然后计数器每1000次计数会产生溢出。
以上
是我之前初学的时候所了解的知识,但是对于怎么计算出周期还是有点懵懵的。后来经过多次练习才了解了。
定时器需要工作肯定需要一个时钟,而这个时钟则是由系统时钟分出来的。
那么定时器的时钟有什么用呢。这可能需要一点数电的知识。
那么最简单的理解方法就是,既然定时器计数值会自动变化,那么每一次变化期间的间隔时间是多少呢????????????
那自然就是定时器的时钟周期,也就是由系统时钟分频得出的频率。
这样理解就显得容易计算了。
主时钟分过来来的频率是1 000 0HZ换算成周期也就是100us,换句话说也就是每隔100us,定时器的值变化1。
而后定时器是每计数1000次产生一个溢出。
也就有1000个100us 1000 00us=100ms=0.1s
这样理解就方便多了。
而后就是正常的配置过程,这里不做赘述。
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 1. 使能时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* 2. 配置定时器参数 */
TIM_TimeBaseStructure.TIM_Prescaler = 16800 - 1; /* 定时器时钟分频系数 */
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; /* 定时器重装载值 */
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* 计数器模式 */
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; /* 重复计数值 */
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置每次进入中断为电平翻转模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出开
TIM_OCInitStructure.TIM_Pulse = 550; //设置最初CCR为0,这样一配置完就进去中断服务程序
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置最开始的电平为高电平
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //载入寄存器
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
接下来我是用通用定时器TIM5来进行输入捕获
根据数据手册TIM5挂载在APB1总线上,当系统频率配置为168MHZ时,该总线频率为84MHZ。
也就是该定时器,可以从系统分得84MHZ的时钟。但是定时器肯定跑不了这么高的时钟,所以要先进行分频。为了方便计算,我选择84分频。所以定时器计数频率为1 000 000MHZ。
同时再给计数器设置1000的计数值。也就是计数值变化1000次产生一次上溢事件。
所以上溢时间发生的频率为1 000 HZ 也就是1ms一次。
接下来就是输入捕获的模式配置。这里我选择通道1为注入通道。
重映射引脚PA0为定时器5通道1。
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM5_ICInitStructure.TIM_ICFilter = 0x02;
这里TIM_ICPSC_DIV1意思是:配置捕获1预分频器。每次在捕获输入上检测到边缘时执行的捕获。换句话说,检测到上升沿就判断是上升沿。这句话可能有点奇怪。如果换一种说法
检测到1次上升沿就捕获。也就说还能设置检测到两次上升沿,四次,八次才捕获配置。
接下来一个成员变量就是给滤波器配置时钟周期,滤波器的时钟周期是由定时器直接分得的,这直接决定了滤波器的采样周期,这里贴出数据手册上的介绍。
1.滤波器的捕获是用于判断信号是否是变化而不是由于噪声造成抖动,当在周期内连续检测到N次的事件,才认为该事件是一个有效边沿,反之则为噪声杂波。
2.预分频的配置是用于检测何时该触发中断事件。也就是当来N次上升沿时候才触发中断。
以下贴出初始化代码。 使能更新中断与捕获中断。
TIM_ICInitTypeDef TIM5_ICInitStructure;
void TIM5_Cap_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 清除之前设置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //PA0 输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉
//初始化定时器5 TIM5
TIM_TimeBaseStructure.TIM_Period = 84-1; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =1000-1; //预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x02;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//中断分组初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级2级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
}
这里遇到了问题
首先讲一下我的思路。希望有大佬看出来哪里出问题了帮我做个解答。
TIM5->CNT //该寄存器中即为定时器当前计数值
这里先讲方法:在检测当上升沿之后,先将捕获设置为下降沿捕获,记录进入定时器更新事件中断的次数,按照刚刚的配置方法也就是记录一次中断为1ms。当捕获到下降沿的时候再记录当前的计数器的值,而后通过计算得出该信号处于上升沿的时间。
以下贴出代码(问题往下翻)
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
float TIM5CH1_CAPTURE_VAL; //输入捕获值
float TIM5CH1_CAPTURE_VAL_1,TIM5CH1_CAPTURE_VAL_2;
float TIM5CH1_CAPTURE_VAL_N=0;
void TIM5_IRQHandler(void)
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
// if( TIM5CH1_CAPTURE_STA == 0 )//未捕获到上升沿
// {
// TIM5CH1_CAPTURE_VAL_N = 0;
// }
if(TIM5CH1_CAPTURE_STA==1)//未捕获到上升沿
{
TIM5CH1_CAPTURE_VAL_N++;
// TIM5->CNT = 0 ;
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
{
if(TIM5CH1_CAPTURE_STA==0)
{
// TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
TIM5->CNT = 0 ;
TIM5CH1_CAPTURE_STA=1;
}
else
{
// TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
TIM5->CNT = 0 ;
TIM5CH1_CAPTURE_VAL=TIM5CH1_CAPTURE_VAL_N;
/*+(((float)TIM5CH1_CAPTURE_VAL_2+1000-(float)TIM5CH1_CAPTURE_VAL_1)/1000)*/
TIM5CH1_CAPTURE_VAL_N=0;
TIM5CH1_CAPTURE_STA=0;
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
好吧,这也是我自己学艺不精。讲一下问题。
可以看到,我在以上代码中并没有将我自己所讲的思路完全实现,而是只保留了用更新中断计算时间的部分。
关于记录定时器计数值以及相关的计算代码以及被我注释掉了。
原因是实际运行时,这部分出现奇怪的问题。
当我在代码中赋值部分打上断点时,通过调试可以看到其变量的值在不断的变化。
/*就是这俩行代码,打断点和不打断点是两种状态。*/
TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;
但是!当我不在这一句上打断点的时候,调试时直接运行到中断服务函数最后一句,虽然该计数器的值不断在变化,但是记录计数器的值的变量值却不会改变一直为42(十进制)。
当我重新打上断点,发现,这个变量又开始愉快的变化了(微笑)。
希望有大佬可以看出是什么问题。
需要工程文件的可以评论。