跳转至内容

嵌入式控制系统设计/设计模式

来自 Wikibooks,开放世界中的开放书籍
嵌入式控制系统设计的 Wikibook


嵌入式控制系统设计


针对实时环境的编程非常困难,需要对整个系统(硬件和软件)有很好的理解。在理想情况下,系统的每个部分都得到了很好的描述并且易于理解,但现实世界很少允许这种理想情况。因此,系统应该对系统中的不确定性具有鲁棒性。设计模式对所有设计人员都是有用的支持:它们是基于过去大量系统中已有效方法的针对常见问题的通用解决方案。模式也适用于创建可在多个应用程序中重用和调整的可移植代码。

设计模式总是带有一定的上下文:它们是一系列设计力量的结果,这些力量将设计拉向不同的方向,而模式则提供了一种平衡的解决方案。但是,不同系统中的力量可能如此不同,以至于由此产生的权衡也导致了不同的模式。

无需操作系统的软件模式

[编辑 | 编辑源代码]

当为微控制器(如 PICmicro)编程时,程序通常是用机器语言或低级语言(如汇编语言)编写的。汇编程序往往更小,并且在活动内存中效率更高。尽管如此,当控制器拥有足够的资源时,也可以使用更高级别的语言,例如 C。文章"使用高级编程语言"简要列出了使用高级语言相对于汇编语言的优势。(甚至更高级别的语言也具有相对于 C 的相同优势,但它们很少在小型微控制器上可用)。

在本节中,我们将仅讨论在汇编语言编程中使用的设计模式。这些设计模式看起来非常基础,但对其有很好的理解非常重要。不需要掌握汇编语言知识,但对理解本节有所帮助。

子程序

[编辑 | 编辑源代码]

当您经常需要执行相同的操作时,可以使用子程序。通过将值放入寄存器中,您可以向子程序传递参数。子程序的结果也将存储在寄存器中。这里我们给出一个简单的子程序示例

Start	movlw	02h		; Put the value 02h in the working register W
	movwf	PORTA		; Move the value from the working register W into memory location PORTA, this will turn on a led
	call 	Delay		; Call subroutine
	movlw	00h		; Put the value 00h in the working register W
	movwf	PORTA		; Move the value from the working register W into memory location PORTA, this will turn off a led
	goto	Start
	
Delay				; Subroutine Delay
Loop1	decfsz	COUNT,1		; decrease value of COUNT, and skip next instruction if zero
	goto	Loop1		; goto label Loop1
	return

数据表

[编辑 | 编辑源代码]

有时您需要一个可以存储值的结构。或者您需要进行转换,但从表中获取值更容易。然后使用数据表非常方便。

在 PICmicro 上,常量数据表(在 Flash 内存中)是一种正常的子程序。当调用子程序时,工作寄存器中的值将用于确定处理器必须跳过的指令数量。这是通过增加“程序计数器”来完成的?程序计数器是当前执行的指令的地址。增加计数器将使处理器跳过后续指令。一个小例子,在数据表中,我们有 5 个 LED(5 位)闪烁的顺序。

Start	movlw	05h		; Put value 5 in the working register (this is initialization value) 
	movwf	COUNT1		; Assign the value of the working register to COUNT1

Loop1	call	Table		; Look the pattern in the table 
	movwf	PORTA		; Move the pattern of the working register to the led register
	call 	Delay		; Call subroutine
	decf	COUNT1,F	; Decrease, put a byte in the COUNT1 register
	btfsz 	STATUS,Z	; Test if overhead
	goto	Loop1		; No, get next pattern	
	goto	Start		; Yes, run the program from the start again

Delay				; Subroutine delay
Loop2	decfsz	COUNT2,F	; Decrease value of COUNT1, and skip next instruction if zero
	goto	Loop2		; Goto label Loop2
	return

Table   addwf	PCL		; Add value to get pattern
	retlw   01h		; Return pattern 00001b, LED1 - ON
	retlw   02h		; Return pattern 00010b, LED2 - ON
	retlw   03h		; Return pattern 00100b, LED3 - ON
	retlw   04h		; Return pattern 01000b, LED4 - ON
	retlw   05h		; Return pattern 10000b, LED5 - ON
文章PIC“微控制器内存方法:表格”详细介绍了访问表格的各种方法,“跨越 256 字节页面边界”的陷阱(在长度超过 256 字节的表格中始终发生,有时甚至在非常短的表格中也发生),以及解决方法。
文章“文件选择寄存器”讨论了文件选择寄存器,该寄存器用于读取和写入RAM 中的值数组。

待办事项:编写有关基本中断编程的内容

我们将在后面的章节中断服务中详细讨论中断服务。

看门狗定时器

[编辑 | 编辑源代码]

看门狗定时器是处理器中的一个内部定时器。该定时器的目的是避免嵌入式控制器卡死。

看门狗定时器的原理很简单,它在预定义的时间后重置嵌入式控制器。但这只有在程序没有首先重置看门狗定时器的情况下才会发生。

因此,程序应该在预定义的时间内重置看门狗定时器。当看门狗定时器未及时重置时,程序可能卡死,微控制器将自行重置。另请参阅:RTOS 看门狗定时器看门狗定时器

带有操作系统的嵌入式设备的设计模式与实时编程

[编辑 | 编辑源代码]

汇编程序通常是特定于硬件的,并且不太可移植和模块化。这使得大型复杂系统的编程变得相当困难。这可以通过使用处理处理器和硬件接口的“抽象层”来解决。此层也称为系统的“内核”。内核还负责硬件特定的调用和任务管理,这使得能够专注于最重要的任务;创建实时程序。当然,需要更多时间和更多内存,但这使程序更灵活和模块化。此外,使用更高级别的编程语言也使代码更易于移植和调试。

在下一节中,我们将描述一些重要的设计模式。

配置与执行

[编辑 | 编辑源代码]

目标与问题

[编辑 | 编辑源代码]

实时编程中最重要的困难在于确保指定的操作会在确定的时刻发生。因此,准确了解每个操作需要多长时间非常重要。

此模式包含两个部分。在第一部分中,执行时间不可预测的所有操作都已完成。这包括内存分配、硬件初始化和对象创建。此部分称为系统配置。配置完成后,即可启动主循环。

更多信息:逻辑架构与物理架构

更多信息:C++

  • 所有内存分配应在启动之前完成。

异步与同步设计模式

[编辑 | 编辑源代码]

异步/同步设计模式也称为

  • 中断服务例程/延迟服务例程 (ISR/DSR)
  • 一级中断处理程序/二级中断处理程序 (FLIH/SLIH)
  • 快速中断处理程序/慢速中断处理程序
  • 中断上半部分/中断下半部分

当数据到达输入时,处理器将执行预定义的中断函数。有时需要在使用数据之前对其进行处理。应尽可能缩短中断函数的执行时间,以允许新数据到达,并避免错过新数据(由于中断锁定)。

在此设计模式中,数据处理分为两个阶段。

  • 中断服务例程 (ISR):这是在中断发生时处理数据的函数。通常,它只是读取输入上的数据并将其写入缓冲区。它在禁用中断的情况下运行,并以“中断返回”结束,该操作启用中断。
  • 延迟服务例程 (DSR):这是一个在后台持续运行并处理新数据的函数。它在启用中断的情况下运行,并以正常的“返回”结束。
  • 无数据丢失。
  • 有时可能会有超过所需数量的数据到达。然后,DSR 函数将仅处理所需的数据。

事件处理

[编辑 | 编辑源代码]

监视器

[编辑 | 编辑源代码]

有限状态机模式

[编辑 | 编辑源代码]

此模式也称为状态模式或FSM模式。

系统通常由不同的状态或阶段组成,每个状态都必须完成一些操作。因此,嵌入式控制器应该根据系统的状态对输入做出不同的反应。

这可以通过编程 FSM 来实现,这是一个类,其中函数的行为根据系统的状态而有所不同。

我们将在后面的章节中更详细地讨论有限状态机,嵌入式控制系统设计/有限状态机与Petri网

任务上下文

[编辑 | 编辑源代码]

实时编程的问题

[编辑 | 编辑源代码]

死锁是指两个或多个相互竞争的进程都在等待对方完成,从而导致两者都无法继续执行的情况。这通常表现为类似于“先有鸡还是先有蛋”这样的悖论

在计算机领域,死锁指的是一种特定情况,即两个或多个进程都在等待另一个进程释放资源,或者多个进程以环形链的方式等待资源(参见必要条件)。死锁是多进程中常见的问题,在多进程中,许多进程共享一种特定类型的互斥资源,称为软件。用于分时和/或实时市场的计算机通常配备硬件锁(或硬锁),它保证了对进程的独占访问,强制进行序列化。死锁尤其令人头疼,因为没有通用的解决方案可以避免(软)死锁。

这种情况可以比作两个人在画图,他们之间只有一支铅笔和一把尺子。如果一个人拿了铅笔,另一个人拿了尺子,当拿铅笔的人需要尺子,而拿尺子的人需要铅笔时,就会发生死锁,在他们放弃尺子之前。这两个请求都无法满足,因此发生了死锁。

优先级反转

[编辑 | 编辑源代码]

调度中,优先级反转是一种反模式。优先级反转发生在低优先级任务持有高优先级任务所需的共享资源时。这会导致高优先级任务的执行被阻塞,直到低优先级任务释放资源,从而有效地“反转”了这两个任务的相对优先级。如果在此期间某些不依赖于共享资源的中等优先级任务尝试运行,它将优先于低优先级任务高优先级任务。

在某些情况下,优先级反转可能不会造成直接的危害——高优先级任务的延迟执行没有被注意到,并且最终低优先级任务会释放共享资源。但是,在许多情况下,优先级反转也会导致严重的问题。如果高优先级任务被剥夺了资源,则可能导致系统故障或触发预定义的纠正措施,例如看门狗定时器重置整个系统。火星探路者遇到的麻烦是实时系统中优先级反转导致问题的经典案例。

优先级反转还可以降低系统的感知性能。低优先级任务通常具有较低的优先级,因为它们不必立即完成(例如,它们可能是批处理作业或其他非交互式活动)。类似地,高优先级任务具有较高的优先级,因为它们更有可能受到严格的时间约束——它可能正在向交互式用户提供数据,或根据实时响应保证执行操作。由于优先级反转会导致低优先级任务的执行阻塞高优先级任务,因此它可能导致系统响应能力降低,甚至违反响应时间保证。

免疫感知编程

[编辑 | 编辑源代码]

嵌入式控制系统设计

进一步阅读

[编辑 | 编辑源代码]

参考文献

[编辑 | 编辑源代码]

链接:http://www.eventhelix.com/RealtimeMantra/Patterns/

链接:http://technology.niagarac.on.ca/staff/mboldin/18F_Instruction_Set/

华夏公益教科书