DE | EN | CN

ESE模块6 系统时钟 SysTick

ARM控制器的性能注定要使用适当的运行环境或适当的实时操作系统。这些主要基于Timer计时器触发的资源分配,尤其是资源计算时间。为此ARM提供了一个特殊的计时器,它唯一任务就是生成系统触发事件。

即使没有实时操作系统,SystemTick对应用程序开发人员也非常有吸引力。本教程中使用的程序模板已经为使用SysTick做好了准备。在PEC框架(我们在此使用的库)中,默认情况下,SysTick会为PecAppKernel生成一个10毫秒的事件。PecAppKernel将以下事件分发到所有AppModule:

  • 直接用Interuppt处理10毫秒事件onTimer10ms()
  • 通过事件队列100毫秒事件onEvent100ms()
  • 通过事件队列1秒事件onEvent1s()

我们将在练习中使用这些事件。同时要对PecFramwork计时的应用要有一定的了解。

开发一个微控制器应用程序,用户可以查看微控制器SysTick的工作方式。

任务是:

请设计一种解决方案,可通过不同LED的闪烁和扬声器发出的信号音来说明微控制器的各种SysTick事件(10 ms,100 ms,1s)。

LED连接到引脚B0,B1,B3,扬声器连接到引脚B4。

SRS SysTick

进行以下准备工作:

  • 创建一个新的类图
  • 目标语言ARM C ++
  • 目标平台STM32F042 mySTM32板灯HAL
  • 下载PEC应用程序(XMC,STM32,AVR)的图表模板应用程序基本结构
  • STM32F0分配驱动程序包
  • 可选stm32F042_48Mhz

任务是以适当的形式演示系统计时器概念并产生系统事件。用户通过光信号(LED闪烁)和声音信号(扬声器)得到返馈。

首先是10毫秒事件,直接从中断函数(中断服务程序或中断处理程序)中调用此事件。对于应用程序开发人员来说,这意味着该功能应该只在特殊情况下使用,如果是这样,只能使用很少的代码,即快速的代码,否则我们会减慢系统的速度。 我们通过onTimer10ms()操作中的前缀on认识到这是一个事件。10毫秒太快了,无法识别闪烁的LED。我们可将信号放在扬声器上,因为我们可以听到超出了千赫兹范围的频率。

100毫秒的事件不是直接从中断函数中调用,而是通过事件队列(Event Queue)。 也就是事件在队列中排列,如果系统有足够的时间就会处理它们。这意味着事件的发生与事件反应的处理之间存在几微秒的延迟,但系统可以更好地管理处理器时间,并且系统仍然可以平稳运行。我们通过事件名称onEvent100ms()中的事件标识符来识别这一点。 在这我们直接连接LED。 每个开关之间的100毫秒为5赫兹,我们视为为闪烁。

1秒事件非常慢。 我们可以肉眼可见LED的变化。

为了比较并阐明此面向事件的编程概念的并行性,基于onWork事件中的waitUs函数,用PWM(调光)构建一个闪烁的LED。 这是一个很好的点来讨论onWork()事件的特征。从主循环调用此事件。这意味着当没有其他事件正在处理时,总是调用onWork。这通常是可用处理器时间的90%或99%。由此可见,我们将所有非时间紧迫的事情都放在入onWork里。 我们依然用LED来显示onWork的运行情况。对于使LED变暗的简单算法,在onWork上有足够的计算时间。

总而言之,我们需要以下系统组件:

这些必须再次关联。 库模块PecPinOutput完全可以满足要求(切换)。 设计草稿如下所示:

注意:前缀ON =事件

该实现应包括以上设计草图中描述的元素。此外还必须将特定的输出引脚再次分配给系统块。

如下完成类图:

  • 创建类RedLED,YellowLED,GreenLED并与控制器连接(聚合)
  • 创建类扬声器并连接到控制器(集合)
  • PecPinOutput用作已创建类的库模块
  • 将针脚B0,B1,B3和B4分配给系统组件(B2在指定的外形中不可用)

类模型现在应如下图所示。 验证模型并清楚地排列元素。

下一步,将操作拖动到控制器上,附加到系统事件10、100和1000毫秒并逐步插入以下事件处理程序:

  • onTimer10ms()
  • onEvent100ms()
  • onEvent1s()

在这些步骤之后,类模型应如下图所示。 请再次验证模型和排列元素。

为了实现所需的功能,我们将以下代码添加到相应的事件函数中。先是红色LED闪烁,用100毫秒事件控制红色LED,如下所示。

Controller::onEvent100ms():void
redLED.toggle();

绿色LED应最慢地闪烁,用1秒事件。

Controller::onEvent1s():void
greenLED.toggle();

相反,快速的10毫秒事件。在这里我们切换成扬声器。

Controller::onTimer10ms():void
speaker.toggle();

在这里编写的C ++代码行中所做的实际工作也相对较少。主要部分在于构建合适的类结构以及会使用PEC框架。现在我们可以测试应用程序了。

翻译程序。将可执行程序传送到控制器的程序存储器中。

  • 创建(编译和链接)
  • 刻录
  • 连接…

接下来我们扩展应用程序,使用从框架运行时环境的主循环连续触发的onWork()事件,使黄色LED发出微弱的光(暗淡)。其他输出设备的可见和可听运行时行为不应受到影响。不使用长时间等待功能像waitMs()。

Controller::onWork():void
// continuous event from the Mainloop
uint8_t brightness=1; // use here 1 to 255 for fix brightness of the yellow LED
yellowLED.on();
for (int i=brightness; i>0; i--);
yellowLED.off();
for (int i=255-brightness; i>0; i--);

这段代码是用于控制黄色 LED 的亮度的简单循环。下面是对代码的解释:

1. 首先,定义了一个变量 `brightness`,用于表示黄色 LED 的亮度。取值范围为 1 到 255,其中值越大,LED 的亮度越高。

2. 代码中通过两个循环来控制 LED 的亮度。第一个循环将 LED 打开并持续一段时间,第二个循环将 LED 关闭并持续一段时间。

3. 第一个循环中,`brightness` 值被用作计数器 `i` 的初始值,从 `brightness` 开始递减到 1。在这个循环中,LED 保持打开状态,因为循环体为空。

4. 第二个循环中,计数器 `i` 的初始值为 `255 - brightness`,从该值开始递减到 1。在这个循环中,LED 保持关闭状态,同样因为循环体为空。

总的来说,这段代码实现了一种简单的调节 LED 亮度的方法,其中 LED 的亮度受 `brightness` 变量的控制,亮度由变量的值决定。创建,传输和测试此扩展。 使用不同的可变亮度值(1-255)进行实验。

我们可以通过自动使LED淡入和淡出来扩展黄色LED的亮度(呼吸灯)。 尝试以下变体。 就会发现,即使这个相当复杂的解决方案也不会明显影响其他LED和扬声器的运行时行为。

Controller::onWork():void
static uint16_t dim=0;
 
if (dim < 1000)
{
	yellowLED.on();
	waitUs(dim);
	yellowLED.off();
	waitUs(1000-dim);
	dim++;
}
else if (dim <2000)
{
	yellowLED.on();
	waitUs(2000-dim);
	yellowLED.off();
	waitUs(dim-1000);
	dim++;
}
else
{
	dim=0;
}

这段代码是一个简单的循环,用于控制黄色 LED 的亮度。下面是对代码的解释:

1. 首先,定义了一个静态变量 `dim`,用于跟踪 LED 的亮度。初始值为 0。

2. 代码通过比较 `dim` 的值来控制 LED 的亮度。当 `dim` 小于 1000 时,LED 会以逐渐增加的亮度亮起;当 `dim` 大于等于 1000 且小于 2000 时,LED 会以逐渐减小的亮度熄灭;当 `dim` 达到 2000 时,`dim` 会重新设置为 0,循环重新开始。

3. 在每个条件分支中,LED 会交替地亮和灭,并且每次亮灭的时间间隔取决于 `dim` 的值。

具体来说,每次循环执行时,LED 会以逐渐增加的亮度亮起,直到 `dim` 达到 1000。然后,LED 会以逐渐减小的亮度熄灭,直到 `dim` 达到 2000。然后,`dim` 会重新设置为 0,循环重新开始。整个过程中,LED 的亮度会周期性地变化,从而呈现出一种呼吸灯效果。 深入学习C / C ++中关键字 static的含义,以及为什么在上述解决方案中会使用这个关键字。

这里还有一个稍微不同但功能相同的变体,更容易理解:

Controller::onWork():void
static uint16_t dim=0;
static bool up=true;
 
if (up)
{
	dim++;
	if (dim==1000) up=false;
}
else
{
	dim--;
	if (dim==0) up=true;
}
 
yellowLED.on();
waitUs(dim);
yellowLED.off();
waitUs(1000-dim);

总结

学习和合并的步骤:

  1. 创建并打开类图
  2. 选择适用于PEC应用程序的图表模板,加载并插入STM32F4的驱动程序包
  3. 将Navigator切换到UML包
  4. 在Navigator / Explorer中找到所需的Led类并将其拖到图中
  5. 聚合类
  6. 创建操作并将其插入到类中
  7. 覆盖基类操作
  8. 在操作中创建必要的源代码
  9. 在类图中创建并刻录ARM应用程序

视频学习。

练习 4

将应用程序更改为以下俩个练习:

1. 将扬声器onTimer10ms中的代码,切换到onWork中。
构建并测试此修改后的应用程序。
比较前后扬声器的不同。

  • a. 音调比之前高很多。 这意味着什么?
  • b. 声音不连续,有干扰。 为什么?

2. 在带有waitMsonWork中插入一个500 ms的等待时间。
构建并测试此修改后的应用程序。
比较视觉和听觉俩种结果。

  • a. 听觉:带有撕裂的声音。
  • b. 视觉: LED闪烁的时间已更改。

从这些更改的结果可以得出使用等待功能的结论。

继续学习:

搜索关键字