嵌入式系统编程实验指导书.docx
《嵌入式系统编程实验指导书.docx》由会员分享,可在线阅读,更多相关《嵌入式系统编程实验指导书.docx(37页珍藏版)》请在冰豆网上搜索。
嵌入式系统编程实验指导书
《嵌入式系统编程》实验指导书
目录
实验一uC/OS-II的移植1
1.实验目的1
2.实验任务1
3.预习要求1
4.实验步骤1
实验二多任务的调度与切换3
1.实验目的3
2.实验任务3
3.预习要求3
4.实验步骤3
实验三多任务的同步与通信5
1.实验目的5
2.实验任务5
3.预习要求5
4.实验步骤6
实验四人机交互任务的设计8
1.实验目的8
2.实验任务8
3.预习要求8
4.实验说明9
5.实验提示15
6.思考15
实验五计时器管理任务的设计16
1.实验目的16
2.实验任务16
3.预习要求16
4.实验说明16
5.实验提示19
6.思考19
实验六离散输入/输出的任务设计20
1.实验目的20
2.实验任务20
3.预习要求20
4.实验说明20
5.实验提示22
6.思考23
实验七串行通信的任务设计24
1.实验目的24
2.实验任务24
3.预习要求24
4.实验说明25
5.实验提示28
6.思考29
实验八综合性任务的设计30
1.实验目的30
2.实验任务30
3.预习要求30
4.实验说明32
5.实验提示35
6.思考35
实验一uC/OS-II的移植
1.实验目的
(1)理解uCOS-II实时内核的工作原理;
(2)熟悉uCOS-II在XS128上的移植过程;
(3)掌握uCOS-II移植的细节。
2.实验任务
(1)观察示例程序中的代码,体会实时操作系统与前后台程序的不同之处。
(2)完成由前后台程序编程到基于实时操作系统编程的思想转变。
3.预习要求
(1)参考《嵌入式实时操作系统uCOS-II》(第2版),熟悉uCOS-II各模块的基本工作原理。
(2)参考《单片机与嵌入式系统开发方法》第9章内容以及《uCOS-II移植说明文档》。
熟悉uCOS-II在XS128上的移植过程。
4.实验步骤
(1)打开示例程序,观察程序结构。
(2)识别出哪些是与硬件无关的文件,哪些是移植需要修改和添加的文件。
(3)打开OS_CPU.H文件,该文件定义CPU的数据类型,定义相关的宏。
打开OS_CPU_C文件,分析文件里各个函数的作用。
这两个文件是与CPU特性有关的文件。
(4)分别打开OS_CFG.H,INCLUDES.H.OS_CFG.H这三个文件,了解这三个文件的作用。
用户根据自己的应用系统来定制合适的内核服务功能.包括两个文件:
OS_CFG.H,INCLUDES.H.OS_CFG.H是来配置内核,用户根据需要对内核进行定制,留下需要的部分,去掉不需要的部分,设置系统的基本情况.比如系统可提供的最大任务数量,是否定制邮箱服务,是否需要系统提供任务挂起功能,是否提供任务优先级动态改变功能等等;头文件INCLUDES.H为整个实时系统程序所需要的文件,包括了内核和用户的头文件。
(5)修改.prm文件中的中断向量,将其中的ROM_C000=READ_ONLYDATA_NEARIBCC_NEAR0xC000TO0xFEFF;改为ROM_C000=READ_ONLYDATA_NEARIBCC_NEAR0xC000TO0xEEFF;将结尾处原有的VECTOR0_Startup;改为VECTORADDRESS0xEFFE_Startup;再添加上VECTORADDRESS0xEFF6OSCtxSw;VECTORADDRESS0xEFF0OSTickISR两个中断向量。
(6)运行示例提供的测试程序,观察实验现象。
并思考其工作机制。
思考:
S12X处理器与其它处理器相关技术细节的区别,如何在其它处理器上实现UC/OS-II的移植。
实验二多任务的调度与切换
1.实验目的
(1)在移植好的UC/OS-II上学习用户任务的建立方法和任务切换的实现方式。
(2)为以后的掌握深层次模块的移植和任务切换奠定基础。
(3)进一步掌握UC/OS移植的细节,学习UC/OS-II设置合适的延时时间来实现任务切换的方法。
2.实验任务
(1)观察示例程序中的代码,进一步体会实时操作系统与前后台程序的不同之处。
(2)看懂示例代码的程序,明白多任务的调度与切换的具体过程是如何实现的。
(3)通过示例程序,明白任务调度与切换时堆栈的变化情况。
3.预习要求
(1)参考《嵌入式实时操作系统uCOS-II》(第2版),熟悉uCOS-II各模块的基本工作原理。
(2)参考《单片机与嵌入式系统开发方法》第9章内容以及《uCOS-II移植说明文档》。
熟悉uCOS-II多任务调度与切换的具体实现方法。
(3)通过预习,明白各任务堆栈中哪些是与任务调度和切换有关。
(4)思考如何用可见的方式来展现多任务的调度与切换。
4.实验步骤
(1)打开示例程序,观察程序代码。
(2)参考注释,理解弹出堆栈内容的实现方法。
(3)修改.prm文件中的中断向量,将其中的ROM_C000=READ_ONLYDATA_NEARIBCC_NEAR0xC000TO0xFEFF;改为ROM_C000=READ_ONLYDATA_NEARIBCC_NEAR0xC000TO0xEEFF;将结尾处原有的VECTOR0_Startup;改为VECTORADDRESS0xEFFE_Startup;再添加上VECTORADDRESS0xEFF6OSCtxSw;VECTORADDRESS0xEFF0OSTickISR两个中断向量。
(4)通过超级终端将编译好的程序下载到目标板上,然后运行程序,观察实验现象。
并思考其工作机制。
(5)实验现象如下图所示:
首先弹出的为初始化任务堆栈时所对应的CPU以及PPAGE的值,可比对任务初始化函数void*OSTaskStkInit(void(*task)(void*pd),void*pdata,void*ptos,INT16Uopt)中对任务堆栈初试的值。
而后弹出的Hello!
为起始任务TaskStart()的显示。
该任务为单次执行任务,执行完后通过调用系统删除任务函数将自己删除。
(该函数在本例中除了初始化时钟节拍以及显示字符串“Hello!
”外无其他作用)。
(6)观察并记录显示的堆栈内容以及核心板上LED的明亮情况,结合程序代码,分析该种现象的产生原因。
(7)撰写实验报告。
思考:
除了在任务级堆栈切换函数的钩子函数中添加显示任务堆栈内容的方法外,还有什么途径可以实现任务堆栈内容的可视化显示。
实验三多任务的同步与通信
1.实验目的
(1)在移植好的UC/OS-II上学习用户任务的建立方法和通信机制在任务中的实现。
(2)为以后的掌握深层次模块的移植奠定基础。
(3)进一步掌握UC/OS移植的细节,学习UC/OS-II使用信号量来实现任务切换的方法。
2.实验任务
(1)理解示例程序中用户任务的建立方法和通信机制在任务中的实现。
(2)观察实验现象,理解信号量实现任务切换的方法。
(3)掌握信号量的创建和使用方法。
3.预习要求
该实验是在实验二的基础上,加入信号量,目的是学习在UC/OS-II中使用信号量来实现任务切换。
为了直观的显示效果,建议使用串口,在任务切换的时候将数据发送给PC机进行显示。
信号量:
信号量实际上是一种约定机制,在多任务内核中普遍使用,信号量用于:
◆控制共享资源的使用权(满足互斥条件);
◆标志某事件的发生;
◆使两个任务的行为同步。
信号量像一把钥匙,任务要运行下去,需先拿到这把钥匙。
如果信号量已被别的任务占用,那么该任务只得被挂起,直到信号量被当前使用者释放。
信号量可以是2个值的变量,也可以是计数式的。
只取2个值的信号量,是只有2个值0和1的量,因此也称为二值信号量。
计数式的信号量的值可以是0—255或0—65535,取决于信号量规约机制使用的是8位还是16位,到底是几位,实际上取决于所使用内核的类型。
根据信号量的值,内核跟踪那些等待信号量的任务。
一般的说,对信号量只能实施3种操作:
初始化,也可以称作建立;等信号,也可以称作挂起;给信号,也可以称作发信号。
信号量初始化时,要给信号量赋初值,等待信号量的任务表应清为空。
想得到信号量的任务,须执行“等待”操作。
如果该信号量有效(信号量值大于0),则信号量值减1,任务得以继续运行。
如果信号量的值为0,等待信号量的任务就被列入等待信号量任务列表。
多数内核允许定义等待超时。
如果等待时间超过了某一设定的值,该信号量还是无效,则等待信号量的任务进入就绪态,准备运行,并返回错误代码(指出发生了等待超时错误)。
任务以“发信号”操作释放信号量。
如果没有任务等待信号量,那么信号量仅仅是简单地加1;如果有任务等待该信号量,那么就会有一个任务进入就绪态,信号量的值也就不加1。
于是钥匙给了等待信号量的诸任务中的一个任务。
至于给了那个任务,要看内核是如何调度的。
收到信号量的任务可能是以下两者之一:
◆等待信号量任务中优先级最高的;
◆最早开始等待信号量的任务,即按先进先出的原则。
有些内核具有选择项,允许在信号量初始化时,选定上述2种方法中的一种;但是µC/OS-II只支持优先级法。
明确UC/OS-II的代码结构。
与CPU类型无关的3个文件OS_CORE.C、OS_TASK.C、OS_TIME.C、OS_SEM.C是一定要有的。
其它几个文件可以不包含进去。
可以在实验二的基础上,进行修改,加入信号量。
需要修改配置文件,来允许使用信号量。
进入OS_CFG.H,将OS_SEM_EN修改为1。
如下所示:
#defineOS_SEM_EN1
/*Enable
(1)orDisable(0)codegenerationforSEMAPHORES*/
可以根据自己的应用系统来定制合适的内核服务功能.包括两个文件:
OS_CFG.H,INCLUDES.H.OS_CFG.H是来配置内核,根据需要对内核进行定制,留下需要的部分,去掉不需要的部分,设置系统的基本情况.比如系统可提供的最大任务数量,是否定制邮箱服务,是否需要系统提供任务挂起功能,是否提供任务优先级动态改变功能等等;头文件INCLUDES.H为整个实时系统程序所需要的文件,包括了内核和头文件。
4.实验步骤
打开示例程序,将编译生成的.S19文件下载在核心板上运行后,这时核心板上LED灯会按照相应顺序闪烁,超级终端上面显示“123333”。
(1)根据实际应用修改配置文件OS_CFG.H。
(2)在实验二的基础上修改任务,利用延时来实现任务的简单切换。
(3)创建一个信号量,系统初始化完成后,对所创建的信号量进行初始化。
(4)为了直观显示,第一个任务挂起4s,第三个任务挂起1s。
在第一个任务挂起之前发送信号量,第二个任务则等待第一个任务发送的信号量;第三个任务不使用信号量进行切换。
在任务运行期间,第一个任务每隔4s发送给PC机一个‘1’,第二个任务随之发送一个‘2’,第三个任务则每隔1s发送一个‘3’。
(5)编写运行成功后,将生成的.S19文件烧写在核心板上,运行观察现象。
思考:
除了运用延时和信号量来实现任务切换的方式,还有什么其它的通信机制,该如何使用?
实验四人机交互任务的设计
1.实验目的
(1)理解键盘模块和LED模块在µC/OS-II下的工作原理。
(2)掌握任务间通信机制——信号量。
(3)掌握在µC/OS-II下的MCU中键盘模块和LED模块的C编程方法。
(4)进一步体会基于实时操作系统的程序与前后台程序的不同之处。
2.实验任务
(1)通过本实验掌握键盘模块以及LED模块在µC/OS-II下的工作原理。
(2)根据实验提示,使用键盘与LED模块实现一个简单的人机交互任务。
在µC/OS-II下按下矩阵键盘上相应按键,其键值在LED上显示出来,同时发送给PC机。
3.预习要求
1.充分了解键盘的硬件电路以及识别键盘时存在的问题。
在本次实验中大家应首先回顾一下矩阵式键盘结构,同时要熟悉RTOS下键盘模块的相关代码。
可参考《嵌入式系统构件》第3章的键盘章节。
在RTOS下编写程序,对任务的管理主要是通过OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR(),OSTaskStkInit()这五个函数实现的。
大家首先要理解这些函数的工作原理。
以及任务间的通信机制如信号量、邮箱、队列等概念和原理。
本实验在任务间通信时采用的是信号量,此内容可参考《嵌入式实时操作系统UC/OS-II》的第七章信号量管理。
2.实验之前需要查阅相关资料掌握一些有关LED数码管的基本知识,①首先需要熟悉一下LED模块的硬件结构,LED简称发光二极管,是可发光的半导体设备,一般门槛电压在1.6V~2.0V之间,LED数码管分共阳极与共阴极两种,其工作特点是,共阳极接高电平,使用低电平点亮数码管;共阴极接低电平,使用高电平点亮数码管。
针对具体硬件看看共阴极与共阳极有什么区别。
②熟悉七段数码显示器多路复用技术的特点,看看七段与通常所说的八段有什么不一样?
③数码管主要是由段码与位码控制的,在电路图中什么是段码,什么是位码?
同时请思考段码与位码是如何工作的?
是如何控制LED的?
4.实验说明
1.本次实验使用的矩阵式键盘的电路图如下图4.1所示:
图4.1矩阵式键盘的电路图
采用4*4键盘,因此将键盘的行、列共8条线连接到PTA口上,其中行1-4连接到PTA0-3上,列1-4连接到PTA4-7上。
即“行”对应XS128端口A的高四位。
“列”对应端口A的低四位。
按下相应的键会将按键的相应值输出到LED指示灯显示。
如按下键A,将会把十六进制0xA的二进制编码的反码显示在指示灯上,即0101,因为LED指示灯为低电平时显示亮的状态,故将0xA的值取反会更直观。
键盘需要解决的核心问题包括扫描和去抖。
之前学习的键盘扫描有行扫描法和反转法两种。
在此不累述。
下面主要分析一下去抖机制(状态机原理)。
键盘扫描任务还要解决按键抖动的问题。
为了方便理解,先在一个完整的按键过程中定义几个状态。
第N次采样时无键按下,此时键处于初态;N+1次采样时第一次采到有键按下,键进入次态,此时按键尚不稳定;N+2采样时为连续第二次采到有键按下,键进入初稳态,次态之后初稳态之前的这段20ms的时间称为去抖周期;N+3次采样时为连续第三次采到,键进入持续稳定态,第N+I+1次采样时为抬键后第一次被采到,此时键又回到了初态。
因此把键态分为四种情况:
初态,次态,初稳,稳定态;
为了使程序根据键态进行相应的处理,键盘扫描程序应为每一个按键设置状态单元,根据每个扫描周期采样到的键的即时状态是开或是关,再结合原来的键态得到现在的键态。
可以建立一个状态机来扫描键盘。
如图4.2所示状态机在每个扫描周期执行一次。
图4.2矩阵键盘状态机
有上述分析得:
我们可用一个单独的任务KeyScanTask()来扫描键盘。
当应用程序调用了KeyInit()时,KeyScanTask()将被创建。
一旦被创建后,KeyScanTask()将每KEY_SCAN_TASK_DLY毫秒执行一次。
KEY_SCAN_TASK_DLY应该被设置为产一个范围在的10~30Hz的扫描频率之间(即33~100ms之间)。
在键盘扫描模块中,KeyScanTask()函数发挥着相当重要的作用,它负责扫描任务,通过keyInitport()函数、读取列状(KeyGetCol)、写行状态(KeySelRow)函数与硬件打交道来扫描相应的行和列。
键盘扫描任务并不解决所有的问题,它所做的工作包括扫描键盘、去除抖动、按盘编码等,当然最终要把按键编码送入缓冲区keyBuf[],应用程序可以透过接口函数从该缓冲区得到键编码值(可采用信号量机制来标志是否有按键发生),维护这个缓冲区还需要两个指针:
读指针和写指针,以及一个键计数器。
通过上图4.1和图4.2我们可以知道:
刚开始的时候,该状态机处于KEY_STATE_UP()状态。
当有键被按下时,则该状态机的状态改变为KEY_STATE_DEBOUNCE()次态,随后它将执行KEY_SCN_TASK毫秒。
请注意µC/OS-II中的函数OSTimeDlyHSM()提供一个便利的方法来除去回弹且在规定的时间间隔内扫描键盘。
在该延迟后,KeyScanTask()在KEY_STATE_DEBOUNCE状态下执行代码,再次检查看是否有键被按下。
如果键被释放,状态机将返回到KEY_STATE_UP状态。
如果该键仍然被按下,则通过调用KeyDecode()可以找到该扫描代码,并且通过KeyBufIn()把它插入到这个循环的缓冲区中。
如果缓冲区已经满了,KeyBufIn()将丢弃扫描码。
KeyBufIn()也给键盘发信号量,允许应用程序通过KeyGetKey()来获得该键的扫描码。
然后这个状态机改变为KEY_STATE_RPT_DLY状态。
如果该键按下的时间多于KEY_RPT_START_DLY扫描时间,则自动重复功能将会启动。
在这种情况下,该扫描码将插入被到缓冲区中,且状态改变为KEY_STATE_RPT_DLY状态。
如果改键不再被按下,则该状态机的状态改为KEY_STATE_DEBOUNCE状态,用来去除被释放键的回弹。
在一个扫描周期后,KeyScanTask()在Key_State_Rpt_Dly状态下执行代码,其中被按下键的扫描码将每隔KEY_RPT_DLY扫描时间被插入到缓冲区中。
像其他状态一样,如果键被释放,就需要去除回弹。
图4.3矩阵键盘模块驱动程序流程图
图4.3展示了µC/OS-II平台上工作的矩阵键盘模块的流程图。
2.
(1)七段LED显示器由7条发光二极管组成,并按“日”字型排列。
显示段可分别记为a、b、c、d、e、f、g,有的还带有一个小数点dp,可以用来显示数字和部分字符。
一个LED硬件的具体电路图如下图4.4所示,
图4.4一个LED硬件的具体电路图
(2)7段数码管可分为共阳极和共阴极两种,如下图4.5所示。
共阴极7段数码管的信号端高电平有效,只要在各个位段上加上相应的信号即可使相应的位段发光,共阳极的7段数码管则相反,在相应的位段加上低电平即可使该位段发光。
图4.5LED公共端
(3)7段数码显示器多路复用技术的工作方式就是一个一个地轮流点亮各个七段数码显示器,通过恰当地选择点亮7段LED的时间间隔(1~5ms),给人一种视觉暂留的效应,感觉每个LED都在同时显示。
除了要给七段LED显示器提供段选码之外,还要提供一个位选码,位选码就是控制每个7段LED显示位轮流接地点亮的代码。
更多相关具体信息请参照其它资料,下图4.6是本次实验用到的数码管模块的电路图:
图4.6数码管模块的电路图
如图4.6所示,上面的那个输出端口是用来控制LED的“段”,即每一段是否点亮的情况。
下面的输出端口是控制“位”的,即每一个LED的电源接通情况。
MCU通过使用一个输出端口,可以控制一个或多个LED,通过在该端口的合适比特位上写一个0或1电位用来开启LED(本实验使用的是共阳极数码管,所以输送的是1电位)。
如果需要额外的LED,可以增加一个或更多的8位端口。
这个额外的端口可以用来控制更多的数字或段。
增加一个数字端口将增加MPU的系统开销,但是不会增加系统的电流消耗。
如果愿意减少系统开销,可以增加段的端口。
但要增加电流开销。
本实验提供的代码允许多路复用的LED多达64个(需要使用两个8位的输出端口),如果需要增加更多的七段数字或者离散的LED,可以很容易的对该模块进行修改。
在软件中设计实现多路技术如图4.7所示。
假设数字少于8个,同时需要硬件计时器,它能够以一定的速率产生中断:
图4.7LED多路技术
表DispSegTbl[]包含了相对应数字的段字型数据,DispSegTbl[]中的第一个元素包括最左边数字的段字型。
DispSegTblIx是对应于段表的索引,该表将指向下一个要显示的数字,DispDigMsk是一个掩码,用来选择下一个要显示的数字,相当于位选码,在任意时刻,仅有一个数字可以被显示。
在选择下一个数字之前,DispMuxISR()将关闭当前数字的段,这个步骤用来防止显示闪烁,如果显示段在下一个数字被选择之前没有被关闭,以前的段将出现在新的被选的位上。
DispMuxISR()只关心以一定的刷新速率更新显示。
段显示数据如何写入DispSegTbl[]由是任务级代码负责。
LEDInit()初始化模块的内部变量,且初始化硬件端口。
在这个函数中,建立一个消息邮箱,用于接收更新LED显示的数据。
DispClrScr()用来清除显示器。
DispStr()用来显示一个ASCII字符串。
DispStatClr()用来开启一个单独的LED。
DispStatSet()用来关闭一个单独的LED。
最后我们还要配置一下系统文件:
首先在OS_CPU_C.C文件中添加需要调用CLK和LED中函数的声明。
externvoidClkSignalClk(void);
此函数用于通知ClkTask任务更新秒计数器,此处为配合LED使用,提高LED刷新频率,时钟节拍需要增加为原始周期的十倍,此时系统时钟的周期为2ms(1S=1000ms,1ms=1000us)。
externvoidDispMuxHandler(void);
此函数用于按将高的频率刷新LED显示数据,实际上同一时刻只有一个LED显示数据。
5.实验提示
程序描述:
将编译生成的.S19文件下载在核心板上运行后,任务1和任务2通过串口在超级终端上显示信息,而键盘则通过lED灯把按键值显示出来。
即在矩阵键盘上按下相应的键值对应LED上相应灯亮,如按下B,会将对应的二进制编码的反码显示在指示灯D40~D43上,即0100通过这4个任务来完成对键盘的测试。
6.思考
是否可采用别的通信机制来实现人机交互?
RTOS下键盘模块与其它模块如何结合使用?
实验五计时器管理任务的设计
1.实验目的
(1)理解计时器的工作原理。
(2)掌握定时器管理器模块的内部结构。
(3)进一步掌握任务之间的信号量通信机制。
2.实验任务
(1)通过本实验掌握计时器模块模块在µC/OS-II下的工作原理。
(2)根据实验提示,使用计时器模块与LED模块在µC/OS-II下进行任务的设计。
(3)在µC/OS-II下摁下规定好的按键,启动相应的计时器,同时在LED上显示所启动的计时器,计时器溢出时,通过串口将“Timer#?
TimedOut!
”发送给PC机。
3.预习要求
在本次试验之前应当充分掌握定时器管理器的基本功能和内部结构;参照《嵌入式构件》的第七章计时器管理器理解计时器的工作原理和编程方法,计时器的数据结构,各个函数的具体作用以及各函数的参数设置。
4.实验说明
计时器管理器的工作原理是计时器内部含有计数寄存器,首先要设定初值,然后启动计时器。
这样计时器就从初值开始累计减少(增加),这样减少(增加)到一定的数值后,计时器模块就发生一个中断,然后根据计数模式跳到合适的值重新计数,循环往复。
当启动一个操作,等待一定的时间,然后停止操作这样的场合,计时器有非常重要的作用。
通常该过程如下:
(1)启动一个操作(打开或关闭输出设备)。
(2)启动计时器。