DSP课程设计报告.docx
《DSP课程设计报告.docx》由会员分享,可在线阅读,更多相关《DSP课程设计报告.docx(14页珍藏版)》请在冰豆网上搜索。
DSP课程设计报告
淮阴工学院
《DSP技术及应用》课程设计报告
选题名称:
基于UC/OS-II任务间同步实验
系(院):
计算机工程学院
专业:
计算机科学与技术(嵌入式系统软件设计)
班级:
计算机1073班
姓名:
许晨林学号:
1071306126
指导教师:
马岱,常波
学年学期:
2009~2010学年第2学期
2010年6月12日
摘要:
uC/OS是一种公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统,商业应用需要付费。
uC/OS和uC/OS-II是专门为计算机的嵌入式应用设计的,绝大部分代码是用C语言编写的。
CPU硬件相关部分是用汇编语言编写的、总量约200行的汇编语言部分被压缩到最低限度,为的是便于移植到任何一种其它的CPU上。
uC/OS-II具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2KB。
uC/OS-II已经移植到了几乎所有知名的CPU上。
严格地说uC/OS-II只是一个实时操作系统内核,它仅仅包含了任务调度,任务管理,时间管理,内存管理和任务间的通信和同步等基本功能。
没有提供输入输出管理,文件系统,网络等额外的服务。
但由于uC/OS-II良好的可扩展性和源码开放,这些非必须的功能完全可以由用户自己根据需要分别实现。
uC/OS-II目标是实现一个基于优先级调度的抢占式的实时内核,并在这个内核之上提供最基本的系统服务,如信号量,邮箱,消息队列,内存管理,中断管理等。
关键字:
uC/OSII;移植性;实时操作系统;系统服务;抢占式
目录
1课程概述1
1.1课题目标1
1.2课程要求1
2课程相关知识知识2
2.1DSP芯片的简介2
2.2uC/OS-II的工作原理3
2.3信号量的相关知识4
3设计实现5
3.1设计思想5
3.2函数设计6
3.3实验程序9
总结14
参考文献15
1课程概述
1.1课题目标
uC/OS-II中使用信号量进行任务之间同步通过调用OSSemCreate()建立信号量,并对信号量的初始计数值赋值。
该初始值为0到65,535之间的一个数。
如果信号量是用来表示一个或者多个事件的发生,那么该信号量的初始值应设为0。
如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1(例如,把它当作二值信号量使用)。
最后,如果该信号量是用来表示允许任务访问n个相同的资源,那么该初始值显然应该是n,并把该信号量作为一个可计数的信号量使用。
通过调用OSSemPost()函数发送一个信号量,通过调用OSSemPend()函数等待一个信号量。
如果信号量当前是可用的(信号量的计数值大于0),将信号量的计数值减1,然后函数将“无错”错误代码返回给它的调用函数。
如果信号量的计数值为0,OSSemPend()函数又不是由中断服务子程序调用的,则调用OSSemPend()函数的任务要进入睡眠状态,等待另一个任务(或者中断服务子程序)发出该信号量。
下面的代码使用两个信号量实现了两个任务之间的同步。
信号量创建的代码如下,信号量Sem2初始为可用状态,而信号量Sem1初始为不可用状态。
Sem1=OSSemCreate(0);
Sem2=OSSemCreate
(1);
任务TaskLED必须等待Sem2可用才能够继续往下运行,而Sem2在TaskSEG中发送。
同样TaskSEG必须等待Sem1可用才能够继续往下运行,而Sem1在TaskLED中发送,这样就实现了程序中“…”之间代码的顺序执行,而不受OSTIMEDLY的延时值影响。
1.2课程要求
1.理解uC/OS-II的工作原理;
2.了解uC/OS-II系统任务间同步的办法;
3.掌握uC/OS-II的信号量的使用和实现方法。
2课程相关知识知识
2.1DSP芯片的简介
DSP芯片,也称数字信号处理器,是一种具有特殊结构的微处理器。
DSP芯片的内部采用程序和数据分开的哈佛结构,具有专门的硬件乘法器,广泛采用流水线操作,提供特殊的DSP指令,可以用来快速的实现各种数字信号处理算法。
根据数字信号处理的要求,DSP芯片一般具有如下的一些主要特点:
(1)在一个指令周期内可完成一次乘法和一次加法。
(2)程序和数据空间分开,可以同时访问指令和数据。
(3)片内具有快速RAM,通常可通过独立的数据总线在两块中同时访问。
(4)具有低开销或无开销循环及跳转的硬件支持。
(5)快速的中断处理和硬件I/O支持。
(6)具有在单周期内操作的多个硬件地址产生器。
(7)可以并行执行多个操作。
(8)支持流水线操作,使取指、译码和执行等操作可以重叠执行。
DSP芯片可以按照下列三种方式进行分类。
1.按基础特性分
这是根据DSP芯片的工作时钟和指令类型来分类的。
如果在某时钟频率范围内的任何时钟频率上,DSP芯片都能正常工作,除计算速度有变化外,没有性能的下降,这类DSP芯片一般称为静态DSP芯片。
例如,日本OKI电气公司的DSP芯片、TI公司的TMS320C2XX系列芯片属于这一类。
如果有两种或两种以上的DSP芯片,它们的指令集和相应的机器代码机管脚结构相互兼容,则这类DSP芯片称为一致性DSP芯片。
例如,美国TI公司的TMS320C54X就属于这一类。
2.按数据格式分
这是根据DSP芯片工作的数据格式来分类的。
数据以定点格式工作的DSP芯片称为定点DSP芯片,如TI公司的TMS320C1X/C2X、TMS320C2XX/C5X、TMS320C54X/C62XX系列,AD公司的ADSP21XX系列,AT&T公司的DSP16/16A,Motolora公司的MC56000等。
以浮点格式工作的称为浮点DSP芯片,如TI公司的TMS320C3X/C4X/C8X,AD公司的ADSP21XXX系列,AT&T公司的DSP32/32C,Motolora公司的MC96002等。
不同浮点DSP芯片所采用的浮点格式不完全一样,有的DSP芯片采用自定义的浮点格式,如TMS320C3X,而有的DSP芯片则采用IEEE的标准浮点格式,如Motorola公司的MC96002、FUJITSU公司的MB86232和ZORAN公司的ZR35325等。
3.按用途分
按照DSP的用途来分,可分为通用型DSP芯片和专用型DSP芯片。
通用型DSP芯片适合普通的DSP应用,如TI公司的一系列DSP芯片属于通用型DSP芯片。
专用DSP芯片是为特定的DSP运算而设计的,更适合特殊的运算,如数字滤波、卷积和FFT,如Motorola公司的DSP56200,Zoran公司的ZR34881,Inmos公司的IMSA100等就属于专用型DSP芯片。
2.2uC/OS-II的工作原理
uC/OS-II是一种基于优先级的可抢先的硬实时内核。
要实现多任务机制,那么目标CPU必须具备一种在运行期更改PC的途径,否则无法做到切换。
不幸的使,直接设置PC指针,目前还没有哪个CPU支持这样的指令。
但是一般CPU都允许通过类似JMP,CALL这样的指令来间接的修改PC。
我们的多任务机制的实现也正是基于这个出发点。
事实上,我们使用CALL指令或者软中断指令来修改PC,主要是软中断。
但在一些CPU上,并不存在软中断这样的概念,所以,我们在那些CPU上,使用几条PUSH指令加上一条CALL指令来模拟一次软中断的发生。
在uC/OS-II里,每个任务都有一个任务控制块,这是一个比较复杂的数据结构。
在任务控制快的偏移为0的地方,存储着一个指针,它记录了所属任务的专用堆栈地址。
事实上,再uC/OS-II内,每个任务都有自己的专用堆栈,彼此之间不能侵犯。
这点要求程序员再他们的程序中保证。
一般的做法是把他们申明成静态数组。
而且要申明成OS_STK类型。
当任务有了自己的堆栈,那么就可以将每一个任务堆栈再那里记录到前面谈到的任务控制快偏移为0的地方。
以后每当发生任务切换,系统必然会先进入一个中断,这一般是通过软中断或者时钟中断实现。
然后系统会先把当前任务的堆栈地址保存起来,仅接着恢复要切换的任务的堆栈地址。
由于哪个任务的堆栈里一定也存的是地址(还记得我们前面说过的,每当发生任务切换,系统必然会先进入一个中断,而一旦中断CPU就会把地址压入堆栈),这样,就达到了修改PC为下一个任务的地址的目的。
2.3信号量的相关知识
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。
在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。
其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
为了完成这个过程,需要创建一个信号量VI,然后将AcquireSemaphoreVI以及ReleaseSemaphoreVI分别放置在每个关键代码段的首末端。
确认这些信号量VI引用的是初始创建的信号量。
Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。
以一个停车场的运作为例。
简单起见,假设停车场只有三个车位,一开始三个车位都是空的。
这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。
这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
抽象的来讲,信号量的特性如下:
信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。
在信号量上我们定义两种操作:
Wait(等待)和Release(释放)。
当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。
Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
3设计实现
3.1设计思想
利用信号量使某任务与中断服务同步。
如图1.1所示。
注意,图中用一面旗帜,或称作一个标志表示信号量。
这个标志表示某一事件的发生(不再是一把用来保证互斥条件的钥匙)。
用来实现同步机制的信号量初始化成0,信号量用于这种类型同步的称作单向同步。
一个任务做I/O操作,然后等信号回应。
当I/O操作完成,中断服务程序(或另外一个任务)发出信号,该任务得到信号后继续往下执行。
图1.1用信号量使任务与中断服务同步
内核支持计数式信号量,信号量的值表示尚未得到处理的事件数。
可能会有一个以上的任务在等待同一事件的发生,这种情况下内核会根据以下原则之一发信号给相应的任务:
发信号给等待事件发生的任务中优先级最高的任务,或者信号给最先开始等待事件发生的那个任务根据不同的应用,发信号以标识事件发生的中断服务或任务也可以是多个。
两个任务可以用两个信号量同步它们的行为。
如图1.2所示。
这叫做双向同步。
双向同步同单向同步类似,只是两个任务要相互同步。
例如运行到某一处的第一个任务发信号给第二个任务,然后等待信号返回。
同样,当第二个任务运行到某一处时发信号给第一个任务等待返回信号。
至此,两个任务实现了互相同步。
在任务与中断服务之间不能使用双向同步,因为在中断服务中不可能等一个信号量。
图1.2两个任务用信号量同步彼此的行为
3.2函数设计
1、代码结构整体分析:
ucos对信号量的支持由os_sem.c,os_core.c支持。
其中os_core.c提供OS_EVENT数据结
构的一些基本操作,os_sem.c则实现具体的信号量。
信号量实现的分析,主要是数据结构的问题。
1)、OS_EVENT结构的实现分析
typedefstruct
{
INT8UOSEventType;//事件控制块的类型
INT8UOSEventGrp;//等待的任务组
INT16UOSEventCnt;//信号量计数
void*OSEventPtr;//此处用作链表的链接指针
INT8UOSEventTbl[OS_EVENT_TBL_SIZE];//等待任务表
}OS_EVENT;(ucos_II.H)
OS_EVENT结构体中,信号量主要涉及的域为.OSEventCnt(信号量值),OSEventGrp,OSEventTbl。
其中.OSEventCnt用于计数,OSEventGrp,.OSEventTbl用于支持任务的挂起。
参照前面的分析,.OSEventCnt相当于s.由该结构定义了以下变量用于支持多个信号量的连接。
OS_EXTOS_EVENT
OS_EXTOS_EVENT
*OSEventFreeList;
OSEventTbl[OS_MAX_EVENTS];
OSEventTbl构成由OSEventFreeList为头指针,以.OSEventPtr为链接指针的单链表。
对于OS_EVENT项的获取和回收都是基于该链表.由链表由OSStart()调用OS_InitEventList()构造.
对OS_EVENT的基本操作有:
(os_core.c)
staticvoidOS_InitEventList(void);
功能:
初始化信号量列表,将OSEventTbl中的各项构成以OSEventFreeList为头指针的单链表,以进行各项的插入与删除操作。
voidOS_EventWaitListInit(OS_EVENT*pevent);
功能:
仅初始化OS_EVENT中的.OSEventGrp,.OSEventTbl。
初始化等待的任务列表为空。
这里的等待列表和任务就绪表的结构和功能类似。
INT8UOS_EventTaskRdy(OS_EVENT*pevent,void*msg,INT8Umsk);
功能:
当事件发生时将最高优先级任务置为就绪。
即从OS_EVENT中的等待任务列表中取一最高优先级任务,可能将任务插入就绪列表中。
voidOS_EventTaskWait(OS_EVENT*pevent);
功能:
将任务挂起,将任务挂起,插入到OS_EVENT中等待任务列表中。
voidOS_EventTO(OS_EVENT*pevent);
功能:
将任务置为将就绪,但原因是超时。
2、基于OS_EVENT的信号量实现
在os_sem.C中,实现了完整信号量操作:
OS_EVENT*OSSemCreate(INT16Ucnt);
功能:
创建一个信号量。
从OSEventFreeList中取出一空闲OS_EVENT,进行必要的初始
化。
对信号量而言,主要是初始化.OSEventCnt域和调用OS_EventWaitListInit()对初始等待
任务列表清空。
OS_EVENT*OSSemDel(OS_EVENT*pevent,INT8Uopt,INT8U*err);
功能:
删除一个信号量。
回收
OS_EVENT,如果OS_EVENT有等待的任务,则调用OS_EventTaskRdy()将等待的任务列表中任务置为就绪态。
voidOSSemPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
功能:
申请一个信号量。
任务在无法获得信号量时会调用OS_EventTaskWait()挂起,再调
用OSSched()进行任务切换。
在超时返回时,调用OS_EventTo()将任务置为就绪.
INT8UOSSemPost(OS_EVENT*pevent);
功能:
发送一个信号量。
如果有任务等待,调用OS_EventTaskRdy()将任务置为就绪,调
度。
INT8UOSSemQuery(OS_EVENT*pevent,OS_SEM_DATA*pdata);
功能:
查询信号量。
可以直接通过pevent直接获取信号量的信息。
但是pevent很多时候是被
多个任务共享的。
会随时发生改变。
使用额外的pdata可以快速的获取当前信号量的副本。
INT16UOSSemAccept(OS_EVENT*pevent);
功能:
获取信号量。
如果无法获取,立即退出。
3.3实验程序
#include"includes.h"
#include"ioports.h"
#defineIMR*(volatileunsignedint*)0x00/*定义DSP寄存器的地址在头文件中
#defineIFR*(volatileunsignedint*)0x01/*定义DSP寄存器的地址在头文件中
#defineTASK_STK_SIZE256/*Sizeofeachtask'sstacks(#ofWORDs)*/
#defineN_TASKS2/*Numberofidenticaltasks*/
OS_STKTaskStk[N_TASKS][TASK_STK_SIZE];/*Tasksstacks*/
OS_STKtask_startStk[TASK_STK_SIZE];
#defineADJUST_DSP_CLKclk_adjust
unsignedintseg7[16]=
{0x03f|0x80,0x006,0x05b,0x04f,0x066,0x06d,0x07d,0x007,
0x07f,0x06f,0x077,0x07c,0x039,0x05e,0x079,0x071};
/*mailboxeventcontrolblocks*/
OS_EVENT*DispSem;
voidtask_start(void*data);
voidtask_led(void*data);
voidtask_segs(void*data);
voidhard_delay(intcnt)
{
while(cnt--);
}
voidclk_adjust()
{
asm("stm#0b,CLKMD");
asm("ChkForRdy:
");
asm("ldmCLKMD,A");
asm("and#01b,A");
asm("bcChkForRdy,ANEQ");
asm("stm#33efh,CLKMD");
asm("rpt#80h");
asm("nop");
}
voidmain()
{
/*板卡IO端口及外设初始状态初始化*/
LED_DAT=0x01;
ADJUST_DSP_CLK();
INIT_C54();
OSInit();
OSTaskCreate(task_start,(void*)0,
(void*)&task_startStk[TASK_STK_SIZE-1],0);
OSStart();
while
(1);
}
voidtask_start(void*data)
{
UBYTEerr;
OS_ENTER_CRITICAL();
StartTimer();/*installtheC54xTimer*/
OS_EXIT_CRITICAL();
DispSem=OSSemCreate
(1);/*Displaysemaphore*/
OSTaskCreate(task_led,(void*)0,
(void*)&TaskStk[0][TASK_STK_SIZE-1],2);
OSTaskCreate(task_segs,(void*)0,
(void*)&TaskStk[1][TASK_STK_SIZE-1],3);
for(;;){
err++;
OSTimeDly(100);/*Delay100clocktick*/
}
}
voidtask_led(void*data)
{
intindex=0;
INT8Uerr;
for(index=0;;index++)
{
OSSemPend(DispSem,0,&err);
LED_DAT=0x01<<(index&0x07);
}
}
voidtask_segs(void*data)
{
intdata_index;
intloop;
for(data_index=0;;data_index++)
{
OSSemPost(DispSem);
for(loop=0;loop<80;loop++)
{
show_seg7(1,seg7[(data_index+0)%10]);
show_seg7(2,seg7[(data_index+1)%10]);
show_seg7(3,seg7[(data_index+2)%10]);
show_seg7(4,seg7[(data_index+3)%10]);
show_seg7(5,seg7[(data_index+4)%10]);
show_seg7(6,seg7[(data_index+5)%10]);
show_seg7(7,seg7[(data_index+6)%10]);
show_seg7(8,seg7[(data_index+7)%10]);
}
}
return;
}
总结
通过这次课程设计,我进一步加深了对DSP的了解。
做课程设计时,先查阅相关知识,把原理吃透,确定一个大的设计方向。
总之,通过这次的设计,进一步了解了DSP的相关技术,收获很大,对软件编程、排错调试、相关仪器设备的使用技能等方面得到较全面的锻炼和提高。
通过此次课程设计,我不仅把知识融会贯通,而且丰富了大脑,同时在查找资料的过程中也了解了许多课外知识,开拓了视野,认识了将来DSP的发展方向,使自己在专业知识方面和动手能力方面有了质的飞跃。
本次课程设计既是对课堂上所学知识的全面总结和综合应用,又为今后走向社会的实际操作应用铸就了一个良好开端,它是我对所学知识理论的检验与总结,能够培养和提高设计者独立分析和解决问题的能力。
课程设计不仅是对前面所学知识的一种检验,而且也是对自己能力的一种提高。
这次课程设计使我明白了自己原来知识太理论化了,面对单独的课题的是感觉很茫然。
自己要学习的东西还太多,以前老是觉