stm32f中能否用硬件定时器控制两个独立的进程
Can we control two independent processes with hardware timers in stm32f
我正在尝试为基于 STM32 的微控制器实现固件
两个任务,任务A和任务B都是相互独立的。是否可以在不同的时间实例生成中断,以便固件在到达时间后切换回任务
例如:taskA 应该每 500us 运行 一次,而 taskB 应该每 100us 运行 一次。我建议使用硬件定时器 (TIM),因为它们比操作系统的定时器更准确。你能帮忙吗?或者您有更好的主意吗?
每 100us 产生一次中断
每次中断都会给 TASKA 信号量、任务通知或在队列中放置一些东西
每五个中断给 TASKB 一个信号量、任务通知或在队列中放置一些东西
然后在中断请求结束时进行上下文切换。
另一种选择 - 使用软件定时器 https://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html。
请记住,freeRTOS 中的进程不是 运行 并发的 - 您需要交还控制权以在它们之间切换(或在配置文件中为具有相同优先级的任务启用循环调度)。
编辑:这个答案假定不使用任何 RTOS,正如问题中没有提到的那样——忽略(可能不恰当!)set 标签。使用 RTOS,解决方案将有所不同!
taskA should run once every 500us, and taskB should run once every 100us
这听起来不像您实际上依赖于任务抢占,而不是您想要定期 运行 缩短任务。这可以非常简单地实现,甚至不需要中断:
- 将定时器配置为 运行 全范围(16 位计数器的 ARR 寄存器 = 0xffff)。
- 配置预分频器,使计数器每 us 增加一次(对于 80Mhz,这将是 80 的预分频器,您在 PSC 寄存器中少写入一个,即 79)。
- 照常启动定时器(CR1 寄存器中的 CEN 位)。
现在你只需测量时间:
uint16_t timestampA = timer->CNT - 500; // so that tasks will be run immediately
uint16_t timestampB = timer->CNT - 100;
for(;;) // main loop
{
if(timer->CNT - timestampB > 100)
{
timestampB = timer->CNT;
runTaskB();
}
if(timer->CNT - timestampA > 500)
{
timestampA = timer->CNT;
runTaskA();
}
}
因为示例任务B的频率是任务A频率的倍数,所以我先检查了任务B,所以任务A总是在任务B之后立即开始,所以不会偏离期望的周期(如果你反过来,任务B每5次启动都会延迟任务A的处理时间)。
如果这种情况无法避免(考虑 200 us 和 300 us),您可以添加额外的时移(如果您完全依赖这种精度):
uint16_t timestampA = timer->CNT - 200;
uint16_t timestampB = timer->CNT - (300 - 50);
当然,轮班时间应该比其他任务需要的时间长 运行。
旁注:无需关心溢出,无符号减法是稳健的,因为所有计算都是通过模 216.
完成的
我正在尝试为基于 STM32 的微控制器实现固件
两个任务,任务A和任务B都是相互独立的。是否可以在不同的时间实例生成中断,以便固件在到达时间后切换回任务 例如:taskA 应该每 500us 运行 一次,而 taskB 应该每 100us 运行 一次。我建议使用硬件定时器 (TIM),因为它们比操作系统的定时器更准确。你能帮忙吗?或者您有更好的主意吗?
每 100us 产生一次中断
每次中断都会给 TASKA 信号量、任务通知或在队列中放置一些东西
每五个中断给 TASKB 一个信号量、任务通知或在队列中放置一些东西
然后在中断请求结束时进行上下文切换。
另一种选择 - 使用软件定时器 https://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html。
请记住,freeRTOS 中的进程不是 运行 并发的 - 您需要交还控制权以在它们之间切换(或在配置文件中为具有相同优先级的任务启用循环调度)。
编辑:这个答案假定不使用任何 RTOS,正如问题中没有提到的那样——忽略(可能不恰当!)set 标签。使用 RTOS,解决方案将有所不同!
taskA should run once every 500us, and taskB should run once every 100us
这听起来不像您实际上依赖于任务抢占,而不是您想要定期 运行 缩短任务。这可以非常简单地实现,甚至不需要中断:
- 将定时器配置为 运行 全范围(16 位计数器的 ARR 寄存器 = 0xffff)。
- 配置预分频器,使计数器每 us 增加一次(对于 80Mhz,这将是 80 的预分频器,您在 PSC 寄存器中少写入一个,即 79)。
- 照常启动定时器(CR1 寄存器中的 CEN 位)。
现在你只需测量时间:
uint16_t timestampA = timer->CNT - 500; // so that tasks will be run immediately
uint16_t timestampB = timer->CNT - 100;
for(;;) // main loop
{
if(timer->CNT - timestampB > 100)
{
timestampB = timer->CNT;
runTaskB();
}
if(timer->CNT - timestampA > 500)
{
timestampA = timer->CNT;
runTaskA();
}
}
因为示例任务B的频率是任务A频率的倍数,所以我先检查了任务B,所以任务A总是在任务B之后立即开始,所以不会偏离期望的周期(如果你反过来,任务B每5次启动都会延迟任务A的处理时间)。
如果这种情况无法避免(考虑 200 us 和 300 us),您可以添加额外的时移(如果您完全依赖这种精度):
uint16_t timestampA = timer->CNT - 200;
uint16_t timestampB = timer->CNT - (300 - 50);
当然,轮班时间应该比其他任务需要的时间长 运行。
旁注:无需关心溢出,无符号减法是稳健的,因为所有计算都是通过模 216.
完成的