网站免费源码大全无需下载,微信公众号登录不上,乙肝能治好吗,宁波淘宝网站建设脉冲信号用于设备控制是非常常见的#xff0c;但在一些情况下#xff0c;我们希望精确的控制脉冲的数量以实现对运动的精确控制。实现的方式也许有多种多样#xff0c;但使用计时器来实现此类操作是人们比较容易想到的。
1、原理概述
我们知道在STM32平台上#xff0c;使…脉冲信号用于设备控制是非常常见的但在一些情况下我们希望精确的控制脉冲的数量以实现对运动的精确控制。实现的方式也许有多种多样但使用计时器来实现此类操作是人们比较容易想到的。
1、原理概述
我们知道在STM32平台上使用计时器来实现PWM操作是非常常见的用法。使用的是单一计时器事实上通过主从两个计时器配合我们也可通过生成PWM波的方式精确控制输出脉冲的数量。接下来我们就来简单了解一下使用主从计时器实现精确数量脉冲输出的原理。
对于STM32平台一般都有TIM1和TIM8两个高级定时器和TIM2、TIM3、TIM、TIM5等几个通用定时器。STM32的这些定时器可以通过另外一个定时器的某一个条件被触发而启动。这里所谓某一个条件可以是定时到时、定时器超时、比较成功等各种条件。这种通过一个定时器触发另一个定时器的工作方式称为定时器的同步发出触发信号的定时器工作于主模式接受触发信号而启动的定时器工作于从模式。这些个计时器都可用作从计时器但作为主计时器则是对应不同的触发源它们的主从关系必须遵循设定不可随意配置。具体的配置关系如下 当然要实现精确控制脉冲输出就需要按照上述列表中的要求实现主从计时器的配置。对于主计时器来说要将输出配置为PWM输出并将触发输出的主从模式启用。而对于从计时器来说需要启用从模式并设为门控方式触发源则根据上述表中的描述来选择。
可是为什么主从计时器就能实现精确数量的脉冲输出呢我们借助下面的简单图示来说明这个问题。 首先按前面所述的主从计时器要求配置好主从计时器这是最基本的要求。主计时器负责设置脉冲输出的频率以及输出脉冲从计数器所控制输出的脉冲数。具体过程是这样的主进程启动主从计时器从计时器通过主计时器输出的触发信号开始脉冲计数当达到指定的计数值后产生中断停止主计时器输出直到主进程再次开启这一过程。
2、系统设计
我们已经了解了通过主从计时器实现精确数量脉冲输出的基本原理。那究竟如何实际做呢接下来我们就设计一个简单的系统实现它。
在这一系统中我是使用STM32F407作为实现平台以TIM1作为主计时器TIM4作为从计时器同时输出四路脉冲信号。四路的频率是相同的但每一路的输出数量是可以设定的。具体的操作结构如下图所示 主进程轮询控制计时器TIM1和TIM4工作而TIM1主计时器给TIM4从计时器输出触发信号而从计时器到达指定脉冲数后输出中断信号控制TIM1的输出通道停止。我们人为规定TIM4的通道1、2、3、4与TIM1的输出通道1、2、3、4相对应。
3、代码实现
我们已经说明了使用主从计时器实现精确输出脉冲数的原理也设计了我们的我们想要实现的系统结构接下来我们实现这一系统。
3.1、主计时器的配置
首先我们来看一看主计时器的配置具体代码如下
/*TIM1初始化配置*/
static void TIM1_Init_Configuration(uint32_t period)
{TIM_MasterConfigTypeDef sMasterConfig {0};TIM_OC_InitTypeDef sConfigOC {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig {0};htim1.Instance TIM1;htim1.Init.Prescaler 1;htim1.Init.CounterMode TIM_COUNTERMODE_UP;htim1.Init.Period (period-1);htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter 0;if (HAL_TIM_PWM_Init(htim1) ! HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE;sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_ENABLE;if (HAL_TIMEx_MasterConfigSynchronization(htim1, sMasterConfig) ! HAL_OK){Error_Handler();}sConfigOC.OCMode TIM_OCMODE_PWM1;sConfigOC.Pulse (period/2);sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode TIM_OCFAST_DISABLE;sConfigOC.OCIdleState TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1) ! HAL_OK){Error_Handler();}if (HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_2) ! HAL_OK){Error_Handler();}if (HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_3) ! HAL_OK){Error_Handler();}if (HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_4) ! HAL_OK){Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime 0;sBreakDeadTimeConfig.BreakState TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(htim1, sBreakDeadTimeConfig) ! HAL_OK){Error_Handler();}HAL_TIM_MspPostInit(htim1);
}
3.2、从计时器的配置
接着我们再来看一看从计时器的配置具体代码如下
/*TIM4初始化配置*/
static void TIM4_Init_Configuration(void)
{TIM_ClockConfigTypeDef sClockSourceConfig {0};TIM_SlaveConfigTypeDef sSlaveConfig {0};TIM_MasterConfigTypeDef sMasterConfig {0};htim4.Instance TIM4;htim4.Init.Prescaler 0;htim4.Init.CounterMode TIM_COUNTERMODE_UP;htim4.Init.Period 0xFFFF;htim4.Init.ClockDivision TIM_CLOCKDIVISION_DIV1;if (HAL_TIM_Base_Init(htim4) ! HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(htim4, sClockSourceConfig) ! HAL_OK){Error_Handler();}sSlaveConfig.SlaveMode TIM_SLAVEMODE_GATED;sSlaveConfig.InputTrigger TIM_TS_ITR0;if (HAL_TIM_SlaveConfigSynchronization(htim4, sSlaveConfig) ! HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(htim4, sMasterConfig) ! HAL_OK){Error_Handler();}
}
3.3、主轮询函数实现
主轮询函数控制着主从计时器的启动是实现脉冲输出的控制者包括设置脉冲数并开启从计数器的计数和中断以及启动主计时器的输出。具体代码如下
/*实现通讯数据的处理*/void HgraDataProcess(void)
{TIM_SET_CAPTUREPOLARITY(htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING); // 捕获比较1中断使能TIM_SET_CAPTUREPOLARITY(htim1,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING); // 捕获比较2中断使能TIM_SET_CAPTUREPOLARITY(htim1,TIM_CHANNEL_3,TIM_ICPOLARITY_RISING); // 捕获比较3中断使能TIM_SET_CAPTUREPOLARITY(htim1,TIM_CHANNEL_4,TIM_ICPOLARITY_RISING); // 捕获比较4中断使能__HAL_TIM_SET_COMPARE(htim4,TIM_CHANNEL_1,6400); // 输入通道1的捕获比较值CCR1__HAL_TIM_SET_COMPARE(htim4,TIM_CHANNEL_2,6400); // 输入通道2的捕获比较值CCR2__HAL_TIM_SET_COMPARE(htim4,TIM_CHANNEL_3,6400); // 输入通道3的捕获比较值CCR3__HAL_TIM_SET_COMPARE(htim4,TIM_CHANNEL_4,6400); // 输入通道4的捕获比较值CCR4HAL_TIM_OC_Start_IT(htim4,TIM_CHANNEL_1); //开启定时器4通道1的输入捕获中断HAL_TIM_OC_Start_IT(htim4,TIM_CHANNEL_2); //开启定时器4通道2的输入捕获中断HAL_TIM_OC_Start_IT(htim4,TIM_CHANNEL_3); //开启定时器4通道3的输入捕获中断HAL_TIM_OC_Start_IT(htim4,TIM_CHANNEL_4); //开启定时器4通道4的输入捕获中断HAL_TIM_PWM_Start_IT(htim1, TIM_CHANNEL_1); //开启定时器1通道1的PWM输出中断 HAL_TIM_PWM_Start_IT(htim1, TIM_CHANNEL_2); //开启定时器1通道2的PWM输出中断HAL_TIM_PWM_Start_IT(htim1, TIM_CHANNEL_3); //开启定时器1通道3的PWM输出中断HAL_TIM_PWM_Start_IT(htim1, TIM_CHANNEL_4); //开启定时器1通道4的PWM输出中断
}
3.4、中断处理函数的实现
从计时器产生中断后会根据不同的中断调用不同的中断处理函数这些回调函数是需要我们实现的在这里要实现主计时器PWM输出的停止以及中断标志的复位等处理。具体实现代码如下
/*PWM中断轮询回调函数*/
static void TIM1_PWM_PulseFinished(TIM_HandleTypeDef *htim)
{if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) ! RESET) //判断是否生成中断标志位SR{if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !RESET) //定时器中断使能是否开启{__HAL_TIM_CLEAR_FLAG(htim4, TIM_FLAG_CC1); //清除中断标志位SRif(HAL_TIM_PWM_Stop_IT(htim1, TIM_CHANNEL_1)HAL_OK) //关闭定时器1的通道1的PWM输出{HAL_TIM_OC_Stop_IT(htim4,TIM_CHANNEL_1) ; //关闭定时器4的通道1的输入中断捕获flagStop[0] 1; //关闭标志置1}}} //下面的通道2同理如此if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) ! RESET){if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) !RESET){__HAL_TIM_CLEAR_FLAG(htim4, TIM_FLAG_CC2); //清除标志位if(HAL_TIM_PWM_Stop_IT(htim1, TIM_CHANNEL_2)HAL_OK){ aHAL_TIM_OC_Stop_IT(htim4,TIM_CHANNEL_2) ; flagStop[1] 1; }}}if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) ! RESET){if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) !RESET){__HAL_TIM_CLEAR_FLAG(htim4, TIM_FLAG_CC3); //清除标志位if(HAL_TIM_PWM_Stop_IT(htim1, TIM_CHANNEL_3)HAL_OK){ HAL_TIM_OC_Stop_IT(htim4,TIM_CHANNEL_3) ; flagStop[2] 1; }}}if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) ! RESET){if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) !RESET){__HAL_TIM_CLEAR_FLAG(htim4, TIM_FLAG_CC4); //清除标志位if(HAL_TIM_PWM_Stop_IT(htim1, TIM_CHANNEL_4)HAL_OK){ HAL_TIM_OC_Stop_IT(htim4,TIM_CHANNEL_4) ; flagStop[3] 1; }}}if((flagStop[0] 1)(flagStop[1] 1)(flagStop[2] 1)(flagStop[3] 1)){flagStop[0] 0;flagStop[1] 0;flagStop[2] 0;flagStop[3] 0;__HAL_TIM_SET_COUNTER(htim4,0);}
}
4、小结
我们设计了一个四路输出的脉冲输出每一路的输出数量可以精确单独控制在输出的频率相对较低而且数量不大的情况下我们验证是没有问题的。当然在数量特别多时是否有偏差我们没有测试。而在我们使用的平台时钟为168MHz根据我们的简单测试在输出8MHz的脉冲时还是比较精确的不过这已经完全满足一般的应用需求。
其实从STM32的手册我可以知道输出指定脉冲数的方法有多种但使用主从计时器方式是比较好的一种。这种方式虽然多用了一个定时器但因为不需要频繁中断大大减少了CPU的处理资源。
欢迎关注