跳转到内容

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

来自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/

华夏公益教科书