2.3 中断实验2.3.1 实验目的1. 熟悉ARM的中断操作2. 掌握按键检测的方法2.3.2 实验原理1). 中断的原理和相关寄存器S3C2440A 中的中断控制器可以从60个中断源接收中断请求这些中断源由内部外设提供,例如DMA控制器、UART、IIC等当接收来自内部外设和外部中断请求引脚的多个中断请求时,在仲裁过程后中断控制器请求ARM920T的FIR或IRQ中断S3C2440A中与中断相关的寄存器有:u 中断源挂起寄存器(SRCPND)u 中断挂起寄存器(INTPND)u 中断模式寄存器(INTMOD)u 中断屏蔽寄存器(INTMSK)u 中断优先级寄存器(PRIORITY)S3C2440A中的外部中断在上述中断寄存器的基础上增加了以下寄存器:u 外部中断控制寄存器(EXTINT0~2)u 外部中断滤波寄存器(EINTFLT0~3)u 外部中断屏蔽寄存器(EINTMASK)u 外部中断挂起寄存器(EINTPEND)本次实验采用了外部中断EINT0、EINT1、EINT2和EINT4下面分析相关寄存器的意义:中断源挂起寄存器(SRCPND)该寄存器标志哪个中断源产生中断请求,产生中断请求但未必响应,该寄存器不受中断屏蔽和优先级的影响。
中断处理函数需要将其清零以免重复触发下表列出了SRCPND中部分位的意义中断挂起寄存器(INTPND)该寄存器标志中断源挂起寄存器(SRCPND)中非屏蔽的且优先级最高的中断,只有一位被标志,该中断将产生IRQ中断处理函数中应该将其清零以防止重复触发下表列出了INTPND中部分位的意义中断模式寄存器(INTMOD)用于选择中断源属于FIQ还是IRQ,只有一个中断源能够配置为FIQ下表列出了INTMOD中部分位的意义中断屏蔽寄存器(INTMSK)该寄存器用于用于屏蔽某些中断若某被屏蔽的中断产生了,中断源挂起寄存器(SRCPND)相应的位仍然会被置1,但中断挂起寄存器(INTPND)相应的位不会被置1,该中断也不会被处理下表列出了INTMSK中部分位的意义外部中断控制寄存器(EXTINT0~2)外部中断控制寄存器(EXTINT0~2)主要用于配置外部中断的触发方式,触发方式有:低电平触发、高电平触发、下降沿触发、上升沿触发、双边触发下表列出了EXTINT0的部分位的意义外部中断屏蔽寄存器(EINTMASK)外部中断屏蔽寄存器(EINTMASK)用于屏蔽某些外部中断,当其中的位为1时,表示屏蔽对应的中断源。
其部分位的意义如下表所示,本实验只用到其第4位:外部中断挂起寄存器(EINTPEND)外部中断挂起寄存器(EINTPEND)用于标志产生中断请求的外部中断源,其各位的意义如下所示2.3.3 实验仪器与设备1. TQ2440实验平台2. J-LINK 调试器2.3.4 实验步骤1. 打开实验代码文件夹中的irq_test子文件夹中的工程irq_test.mcp工程2. 阅读代码,单步执行,体会void KeyScan_Test(void)函数里面对中断相关寄存器的设置方法和意义,了解中断服务函数static void __irq Key_ISR(void)中对相关寄存器的设置方法3. 注意:要进入中断服务函数,必须全速执行程序,不能单步执行因此,可以在中断服务函数static void __irq Key_ISR(void)添加一个断点,然后点击全速执行程序当按下按键后,程序就会停在断点处4. 例程中只给出外部中断0和外部中断4的程序,只能实现对按键K2和K4的检测,仿照对外部中断0的配置,完成对按键K1和K3的检测,写出对外部中断1和2的配置代码还有对LED的控制程序,并进行调试,把代码和现象列入下表中序号程序现象1if(rINTPND==BIT_EINT1){ ClearPending(BIT_EINT1);}if(rINTPND==BIT_EINT2){ ClearPending(BIT_EINT2);}检测按键中断并清除相应中断挂起位2if((rGPFDAT&(1<<1)) == 0 ){LED_Control(1,ON);Delay(1000);LED_Control(1,OFF);}LED1亮1s后熄灭3if((rGPFDAT&(1<<2)) == 0 ){ LED_Control(3,ON);Delay(1000);LED_Control(4,OFF);}LED3亮1s后熄灭2.3.5 实验思考题比较实验2.2和实验2.3,要实现检测按键被按下的功能,采用中断和查询的方法哪个响应的速度会更快?两者有什么优缺点?答:(1)采用中断方式的相应速度会更快。
2)查询方式,就是在主函数里面不停循环,查询端口状态,明显其弊端在于响应速度,在处理事件多,处理流程复杂,函数嵌套执行的情况下,由于处理不过来容易丢失事件中断方式,是事件触发的,换言之只要有事件产生都会进入中断,并且取得最优运行,因此响应更快,及时 2.4 定时器和PWM实验2.4.1 实验目的1. 熟悉ARM的定时器操作2. 熟悉ARM的PWM操作2.4.2 实验原理3c2440A有5个16位的定时器其内部结构如图2-10所示定时器0、1、2、3有脉宽调制功能(PWM)定时器4有一个没有输出引脚的内部定时器定时器0和1共享一个8位的预分频器(预定标器),定时器2,3,4共享另一个8位预分频器(预定标器)每个定时器有一个时钟分频器,其可以生成5种不同的分频信号(1/2,1/4,1/8,1/16和TCLK)每个定时器模块从时钟分频器接收其自己的时钟信号,其分频器从相应的8位预分频器(预定标器)接收时钟8位的预分频器是可编程的且根据装载的值来分频PCLK,其值存储在TCFG0和TCFG1寄存器中当定时器使能,定时器计数缓存寄存器(TCNTBn)的值被装载到递减计数器中作为其计数初始值定时器比较缓存寄存器(TCMPBn)有一个被装载到比较寄存器中用来和递减计数的值作比较。
每个定时器有一个16位递减计数器当递减计数器为零时,定时器中断请求生成通知CPU定时器操作已经完成当定时器计数器达到0,如果使能自动重载功能,则TCNTBn的值会自动装载到计数器中TCMPBn用于脉宽调制(PWM)当递减计数器的值和定时器控制逻辑中的比较寄存器的值匹配时,定时器控制逻辑改变输出电平因此,比较寄存器决定了PWM输出的占空比与定时器有关的寄存器如下:u 定时器配置寄存器(TCFG0~1)u 定时器控制寄存器(TCON)u 定时器计数缓冲寄存器(TCNTB0~4)u 定时器比较寄存器(TCMPB0~4)u 定时器观察寄存器(TCNTO0~4)下面介绍这些寄存器的主要功能:定时器配置寄存器(TCFG0~1)定时器配置寄存器主要用于设置预分频器的分频系数和时钟分频其的分频比其各位的定义如下:定时器控制寄存器(TCON)定时器控制寄存器的第0位用于启动和关闭定时器,如下表所示定时器计数缓冲寄存器(TCNTB0~4)定时器计数缓冲寄存器用于存储计数初值,当使能自动装载功能后,当计数值为0后,会把定时器计数缓冲寄存器的值重新装载进计数器定时器比较寄存器(TCMPB0~4)当计数器的值与定时器比较寄存器的值相等时,将会导致输出电平由低电平转为高电平,通过设置改寄存器,可以调整输出方波的占空比,从而实现PWM调整。
定时器比较寄存器如下表所示:2.4.3 实验仪器与设备1. TQ2440实验平台2. J-LINK 调试器2.4.4 实验步骤1. 打开实验代码文件夹中的Song_test子文件夹中的工程song_test.mcp工程2. 单步调试程序,找出基本音符的放音语句,把响应的语句和现象列入下表中序号程序现象1Buzzer_Freq_Set0( 260 );Delay(192*4);蜂鸣器发出音调‘1’持续4拍2Buzzer_Freq_Set0( 294 );Delay(192*4);蜂鸣器发出音调‘2’持续4拍3Buzzer_Freq_Set0( 328 );Delay(192*4);蜂鸣器发出音调‘3’持续4拍4Buzzer_Freq_Set0( 347 );Delay(192*4);蜂鸣器发出音调‘4’持续4拍5Buzzer_Freq_Set0( 390 );Delay(192*4);蜂鸣器发出音调‘5’持续4拍6Buzzer_Freq_Set0( 438 );Delay(192*4);蜂鸣器发出音调‘6’持续4拍7Buzzer_Freq_Set0( 490 );Delay(192*4);蜂鸣器发出音调‘7’持续4拍3. 理解蜂鸣器唱歌的过程,全速运行程序,聆听蜂鸣器唱歌4. 更改唱歌的内容,播放另外两首歌2.4.5 实验思考题简述PWM技术的其他应用例子及其原理答:通信与控制PWM的一个优点是从处理器到被控系统信号都是数字形式的,无需进行数模转换。
让信号保持为数字形式可将噪声影响降到最小噪声只有在强到足以将逻辑1改变为逻辑0或将逻辑0改变为逻辑1时,也才能对数字信号产生影响对噪声抵抗能力的增强是PWM相对于模拟控制的另外一个优点,而且这也是在某些时候将PWM用于通信的主要原因从模拟信号转向PWM可以极大地延长通信距离在接收端,通过适当的RC或LC网络可以滤除调制高频方波并将信号还原为模拟形式PWM广泛应用在多种系统中作为一个具体的例子,我们来考察一种用PWM控制的制动器简单地说,制动器是紧夹住某种东西的一种装置许多制动器使用模拟输入信号来控制夹紧压力(或制动功率)的大小加在制动器上的电压或电流越大,制动器产生的压力就越大可以将PWM控制器的输出连接到电源与制动器之间的一个开关要产生更大的制动功率,只需通过软件加大PWM输出的占空比就可以了如果要产生一个特定大小的制动压力,需要通过测量来确定占空比和压力之间的数学关系(所得的公式或查找表经过变换可用于控制温度、表面磨损等等)例如,假设要将制动器上的压力设定为100psi,软件将作一次反向查找,以确定产生这个大小的压力的占空比应该是多少然后再将PWM占空比设置为这个新值,制动器就可以相应地进行响应了。
如果系统中有一个传感器,则可以通过闭环控制来调节占空比,直到精确产生所需的压力 总之,PWM既经济、节约空间、抗噪性能强,是一种值得广大工程师在许多设计应用中使用的有效技术2.5 串口实验2.5.1 实验目的熟悉ARM的串口操作2.5.2 实验原理1).串行通信概述常用的数据通信方式有并行通信和串行通信两种当两台数字设备之间传输距离较远时,数据往往以串行方式传输串行通信的数据是一位一位地进行传输的,在传输中每一位数据都占据一个固定的时间长度与并行通信相比,如果n位并行接口传送n位数据需时间T,则串行传送的时间最少为nT串行通信具有传输线少、成本低等优点,特别适合远距离传送1)串行数据通信模式串行数据通信模式有单工通信、半双工通信和全双工通信3种基本的通信模式u 单工通信:数据仅能从设备A到设备B进行单一方向的传输u 半双工通信:数据可以从设备A到设备B进行传输,也可以从设备B到设备A进行传输,但不能在同一时刻进行双向传输u 全双工通信:数据可以在同一时刻从设备A传输到设备B,或从设备B传输到设备A,即可以同时双向传输(2)串行通信方式串行通信在信息格式的约定上可以分为同步通信和异步通信两种方式。
A. 异步通信方式异步通信时数据是一帧一帧传送的,每帧数据包含有起始位(”0”)、数据位、奇偶校验位和停止位(”1”),每帧数据的传送靠起始位来同步一帧数据的各位代码间的时间间隔是固定的,而相邻两帧的数据其时间间隔是不固定的在异步通信的数据传送中,传输线上允许空字符异步通信对字符的格式、波特率、校验位有确定的要求u 字符的格式每个字符传送时,必须前面加一起始位,后面加上1、1.5或2位停止位例如ASCII码传送时,一帧数据的组成是:前面1个起始位,接着7位ASCII编码,再接着一位奇偶校验位,最后一位停止位,共10位u 波特率传送数据位的速率称为波特率,用位/秒(bit/s)来表示,称之为波特例如,数据传送的速率为120字符/秒,每帧包括10个数据位,则传送波特率为:10×120=1200b/s=1200波特每一位的传送时间是波特的倒数,如1/1200=0.833ms异步通信的波特率的数值通常为:150、300、600、1200、2400、4800、9600、14400、28800、115200等,数值成倍数变化u 校验位在一个有8位的字节(byte)中,其中必有奇数个或偶数个的“1”状态位。
对于偶校验就是要使字符加上校验位有偶数个“1”;奇校验就是要使字符加上校验位有奇数个“1”例如数据“00010011”,共有奇数个“1”,所以当接收器要接收偶数个“1”时(即偶校验时),则校验位就置为“1”,反之,接收器要接收奇数个“1”时(即奇校验时),则校验位就置为“0”一般校验位的产生和检查是由串行通信控制器内部自动产生,除了加上校验位以外,通信控制器还自动加上停止位,用来指明欲传送字符的结束停止位通常取1、1.5或2个位对接收器而言,若未能检测到停止位则意味着传送过程发生了错误在异步通信方式中,在发送的数据中含有起始位和停止位这两个与实际需要传送的数据毫无相关的位如果在传送1个8位的字符时,其校验位、起始位和停止位都为1个位,则相当于要传送11个位信号,传送效率只有约80%B. 同步通信方式为了提高通信效率可以采用同步通信方式同步传输采用字符块的方式,减少每一个字符的控制和错误检测数据位,因而可以具有较高的传输速率与异步方式不同的是,同步通信方式不仅在字符的本身之间是同步的,而且在字符与字符之间的时序仍然是同步的,即同步方式是将许多的字符聚集成一字符块后,在每块信息(常常称之为信息帧)之前要加上1~2个同步字符,字符块之后再加入适当的错误检测数据才传送出去。
在同步通信时必须连续传输,不允许有间隙,在传输线上没有字符传输时,要发送专用的”空闲”字符或同步字符2).RS-232C串行接口(1)RS-232C接口规格EIA所制定的传送电气规格如表2-1所示RS-232C通常以±12V的电压来驱动信号线,TTL标准与RS-232C标准之间的电平转换电路通常采用集成电路芯片实现,如MAX232等(2)RS-232C接口信号EIA制定的RS-232C接口与外界的相连采用25芯(DB-25)和9芯(DB-9)D型插接件,实际应用中,并不是每只引脚信号都必须用到,25芯和9芯D型插接件引脚的定义,与信号之间的对应关系如图(3)RS-232C的基本连接方式计算机利用RS-232C接口进行串口通信,有简单连接和完全连接两种连接方式简单连接又称三线连接,即只连接发送数据线、接收数据线和信号地,如图2-12所示如果应用中还需要使用RS-232C的控制信号,则采用完全连接方式,如图2-13所示在波特率不高于9600bps的情况下进行串口通信时,通信线路的长度通常要求小于15米,否则可能出现数据丢失现象 3).S3C2440A的UART简介UART(Universal Asynchronous Receiver and Transmitter,通用异步收发器)主要由数据线接口、控制逻辑、配置寄存器、波特率发生器、发送部分和接收部分组成,采用异步串行通信方式,采用RS-232C 9芯接插件(DB-9)连接,是广泛使用的串行数据传输方式,UART以字符为单位进行数据传输,每个字符的传输格式如图2-14所示,包括线路空闲状态(高电平)、起始位(低电平)、5~8位数据位、校验位(可选)和停止位(位数可以是1、1.5或2位)。
这种格式通过起始位和停止位来实现字符的同步UART内部一般具有配置寄存器,通过该寄存器可以配置数据位数(5~8位)、是否有校验位和校验的类型以及停止位的位数(1位、1.5位或2位)等4).S3C2410A的UART结构S3C2410A的UART提供3个独立的异步串行I/O口(SIO),它们都可以运行于中断模式或DMA模式UART可以产生中断请求或DMA请求,以便在CPU和UART之间传输数据在使用系统时钟的情况下,UART可以支持最高230.4Kbps的传输速率如果外部设备通过UEXTCLK为UART提供时钟,那么UART的传输速率可以更高每个UART通道包含两个用于接收和发送数据的16字节的FIFO缓冲寄存器如图2-15所示,S3C2410A的UART由波特率发生器、发送器、接收器以及控制单元组成波特率发生器的时钟可以由PCLK或UEXTCLK提供发送器和接收器包含16字节的FIFO缓冲寄存器和数据移位器发送时,数据被写入FIFO,然后拷贝到发送移位器中,接下来数据通过发送数据引脚(TxDn)被发送接收时,接收到的数据从接收数据引脚(RxDn)移入,然后从移位器拷贝到FIFO中2.5.3 实验仪器与设备1. TQ2440实验平台2. J-LINK 调试器2.5.4 实验步骤1. 打开实验代码文件夹中的uart_test子文件夹中的工程uart_test.mcp工程2. 阅读代码,单步执行,体会Uart0_SendByte和Uart0_Getch()函数里面对相关寄存器的设置方法和意义,实现基本的串口收发功能,并且修改收发的数据,把对应的程序和现象记录下来序号程序现象1Uart0_Init( 115200 );Uart0_SendByte('A');串口调试助手显示字母‘A’2ch=Uart0_Getch();Uart0_SendByte(ch);在串口调试助手输入任意字符串口调试助手将再次显示该字符4. 修改程序,实现一个字符串的发送功能,把程序和现象记录下来 序号程序现象1void UART0_SendString(char *pWord){ while(*pWord) { if(*pWord == '\n') {UART0_SendChar('\n'); //换行 UART0_SendChar('\r'); } else {UART0_SendChar(*pWord); } pWord++; }}串口发送字符串函数定义2UART0_SendString(“Hello,world!\n”)串口调试助手显示字符串“Hello,world!”并换行5. 修改代码,实现 PC 通过串口控制 LED 的功能,把程序和现象记录下来序号程序现象1switch(ch){ case '1': LED_Control(1,ON); break; case '2': LED_Control(2,ON); break; case '3': LED_Control(3,ON); break; case '4': LED_Control(4,ON); break; default: LED_Control(1,OFF); LED_Control(2,OFF); LED_Control(3,OFF); LED_Control(4,OFF); break;}字符判断并控制对应LED灯点亮2ch=Uart0_Getch();在串口调试助手中输入字符‘1’、‘2’、‘3’、‘4’对应LED点亮2.5.4 实验思考题如何采用带FIFO的串口进行数据收发答:在接受数据的时候放入FIFO中,然后发送数据,把FIFO中的数据通过串口发送出去。
在串口助手里很容易看出来,数据是不是错误或者丢失验证了发送与接收2.6 实时时钟实验2.6.1 实验目的熟悉ARM的实时时钟的读写2.6.2 实验原理实时时钟(RTC)单元在系统电源关闭的情况下可以在备用电池下工作S3C2440A的RTC模块的结构如图2-18所示,其能够以BCD码的形式提供秒、分钟、小时、星期、日、月、年的信息,具有闰年生成器,具有报警功能,解决2000年问题,具有独立电源引脚(RTCVDD),支持对于实时内核时间节拍的毫秒节拍时间中断与RTC相关的寄存器有:u 实时时钟控制寄存器(RTCCON)在读取或者修改时间前,必须先把该寄存器的第 0 位 RTCEN 置 1,该寄存器的位如下所示 u 年寄存器 u 月寄存器 u 日寄存器 u 时寄存器 u 分寄存器 u 秒寄存器2.6.3实验设备1. TQ2440实验平台2. J-LINK 调试器3. 串口线2.6.4 实验步骤1. 打开实验代码文件夹中的rtc_test子文件夹中的工程rtc_test.mcp工程2. 阅读代码,单步执行,体会void RTC_Time_Set( void )和void RTC_Display(void)函数里面对相关秒寄存器的写入和读取过程,通过串口观察到的结果。
3. 增加对年、月、日、时、分寄存器的写入和读取功能,并通过串口观察修改后的时间把代码和相应的现象写入下表序号程序现象1void RTC_Time_Set( void ){rRTCCON = 1 ; rBCDYEAR = 0x14 ; //年 闰年测试rBCDMON = 0x12 ; //月rBCDDATE = 0x26 ; //日 rBCDDAY = 0x04 ; //星期rBCDHOUR = 0x14 ; //小时rBCDMIN = 0x49 ; //分rBCDSEC = 0x50 ; //秒rRTCCON &= ~1 ; }RTC起始时间设置位“2014年12月26日星期四14时49分50秒”2void RTC_Display(void) {U16 year ;U8 month, day ; // weekU8 hour, minute, second ;rRTCCON = 1 ; year = 0x2000+rBCDYEAR ; //年month = rBCDMON ; //月day = rBCDDATE ; //日 hour = rBCDHOUR ; //小时minute = rBCDMIN ; //分second = rBCDSEC ; //秒rRTCCON &= ~1 ; //RTC read and write disableUart_Printf("RTCtime : %04x-%02x-%02x %02x:%02x:%02x\n", year, month, day, hour, minute, second );Delay( 900 ) ;}读取RTC当前时间在通过串口在串口调试助手中显示4. 修改工程,改变时间设置值,尝试设置闰年和非闰年,观察实验结果,把把代码和相应 的现象写入下表序号程序现象1RTC_Time_Set(); //设置时间为2014年2月28日星期一23:59:57while(1){RTC_Display();}RTC初始时间为时间为2014年2月28日星期一23:59:57;串口每秒显示一次当前时间4秒后当前时间为2014年2月29日星期二00:00:00;2RTC_Time_Set(); //设置时间为2013年2月28日星期一23:59:57while(1){RTC_Display();}RTC初始时间为时间为2013年2月28日星期一23:59:57;串口每秒显示一次当前时间4秒后当前时间为2013年3月1日星期二00:00:00;2.7 看门狗实验2.7.1 实验目的熟悉ARM的看门狗的操作2.7.2 实验原理在程序运行过程中中,由于软件问题或者外界的干扰,造成程序跑飞,而陷入死循环,程序的正常运行被打断,由于系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对程序运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称“看门狗”(watchdog)。
S3C2440A的看门狗的结构如图2-19所示,看门狗实际是一个计数器,当计数溢出的时候可以对系统进行复位,因此程序在正常运行的时候应该隔一段时间对看门狗复位,防止看门狗计数器溢出使得系统复位,这个过程叫做“喂狗”S3C2440A中与看门狗相关的寄存器有:u 看门狗控制寄存器(WTCON)看门狗控制寄存器如下表所示,其用于启动和关闭看门狗u 看门狗计数器(WTCNT)程序中需要在器溢出之前把新的计数值赋给看门狗计数器2.7.5 实验仪器与设备1. TQ2440实验平台2. J-LINK 调试器3.串口线2.7.6 实验步骤1. 打开实验代码文件夹中的wd_test子文件夹中的工程wd_test.mcp工程2. 阅读代码,了解看门狗的启动方式和喂狗的方法3. 修改工程,比较有喂狗和没有喂狗的实验结果,把程序和相应的现象列入下表中注意:要观察看门狗的复位,必须全速运行程序,不能单步执行序号程序现象1for(i=5;i<=8;i++){led_con(i,ON);delay(1);led_con(i,OFF);delay(1);}程序运行大约2s后停止运行并且AXD显示:“Processor ARM79_0 raised an exception ;cause:The processor was reset”2for(i=5;i<=8;i++){led_con(i,ON);//rWTCNT=2000; //喂狗delay(1);led_con(i,OFF);//rWTCNT=2000; //喂狗delay(1);}程序正常运行,4个LED循环依次亮灭第三章 μC/OS II操作系统下的实验3.2 μC/OS II多任务建立实验3.2.1 实验目的熟悉 μC/OS II 多任务建立方法3.2.2 实验内容1. 运行 μC/OS II 例程,熟悉任务的建立方法2. 修改例程代码,建立两个任务,其中一个实现蜂鸣器唱歌,另外一个实现按键的检测并且改变相应的 LED 的状态3.2.3 实验原理人们在实际生活中处理一个大而复杂问题, 时惯用的方法就是 “分而治之” 即把一个大问题分解多个相对简单、比较容易解决的小问题,小问题逐个被解决了,大问题也就随之解决了。
同样,在设计一个较为复杂的应用程序时,也通常把一个大型任务分解成多个小任务,然后在计算机中通过运行这些小任务,最终达到完成大任务的目的,这种做法也使用应用程序的维护变得方便起来因此,现代操作系统几乎都是对任务操作系统 在μC/OS-II 中,与上述小任务对应的是程序实体就叫做“任务”(实质是一个线程)μC/OS-II 就是一个能对这些小任务进行管理和调度的多任务操作系统 从应用程序设计的角度来看,μC/OS-II 的任务就是一个线程,就是一个用来解决用户问题的 C 语言函数和与之相关联的一些数据结构而构成的一个实体 μC/OS-II 的任务有两种:用户任务和系统任务有应用程序设计者编写得任务,叫做用户任务;有系统提供的任务叫做系统任务用户任务是为解决应用问题而编写的;系统任务是为应用程序来提供某种服务的 为了管理上的方便,μC/OS-II 把每一个任务都作为一个节点 目前,μC/OS-II 最多可以对 64 个任务(包括用户任务和系统任务)进行管理1) 任务的状态 因为在嵌入式系统中只有一个 CPU,所以在一个具体时刻只能允许一个任务占用 CPU 根据任务是否占用 CPU,以及是否处于被中断、等待等情况。
任务在μC/OS-II 中可能处于表3-1 所列的 5 种状态之一任务的状态 说明睡眠状态 任务只是以代码的形式驻留在程序空间(ROM 和 RAM)中,还没有交给操作 系统管理时的情况叫睡眠状态简单地说,任务在没有被配备任务控制块 或被剥夺了任务控制块时的状态叫做任务的睡眠状态就绪状态 如果系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,则 任务就具备了运行的充分条件,这时任务的状态叫就绪状态运行状态 处于就绪状态的任务,如果经调度器获得了 CPU 的使用权,则任务进入运 行状态,任何时刻只能有一个任务进入运行状态,就绪的任务只有当所有 优先级高于本任务的任务都转为等待状态时,才能进入运行状态等待状态 正在运行的任务, 需要等待一段时间或者需要等待一个事件发生再运行时, 该任务就会把 CPU 的使用权让给其他任务而使任务进入等待状态中断服务状态 一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程 序,这时任务的状态叫做中断服务状态任务在不同状态之间的转换如图 3-1 所示2) 用户任务代码的一般结构用户任务代码的一般结构根据嵌入式任务的工作特点,任务的执行代码通常是一个无限循环结构,并且在这个循环中可以响应中断,这种结构叫做超循环结构。
一个用 C 语言编写的任务Void MyTask(void* pdata){for(;;){可以被中断的代码;OS_ENTER_CRITICAL();//进入临界段(关中断)不可以被中断的用户代码;OS_EXIT_CRITICAL();//退出临界段(开中断)可以被中断的用户代码;}}从程序设计的角度来看,一个μC/OS-II 任务的代码就是一个 C 语言函数为了可以传递各种类型的数据甚至是函数,任务的参数是一个 void 类型的指针为 了 有 效 地 对 中 断 进 行 控 制 , 在 任 务 的 代 码 里 可 以 使 用 μ C/OS-II 定义的宏OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()来控制任务何时响应中断,何时屏蔽中断在运行这两个宏之间的代码时是不会响应中断的,这种保护的代码段叫做临界段在具体应用中可以,根据实际需要在一个任务中使用这对宏设置对个临界段因此可以说,μC/OS-II 任务是代码结构的一个带有临界段的无限循环用户应用程序的一般结构从程序代码上来看,用户任务似乎就是一个 C 语言函数,但是这个函数不是一般的 C语言函数,它是一个任务(线程)。
因此它不是被主函数或其他函数调用的,主函数 main()只负责创建和启动它们,而由操作系统负责来调度运行它们用户应用程序结构Void MyTask1(void* pdata) //定义用户任务 1{for(;;){.......}}Void MyTask2(void* pdata) //定义用户任务 2{for(;;){.......}}Void MyTask3(void* pdata) //定义用户任务 3{for(;;){.......}}void main(){.......OSIniT(); //初始化μC/OS-II........OSTaskCreat(MyT ask1,....); //创建用户任务 1OSTaskCreat(MyTask2,....); //创建用户任务 2OSTaskCreat(MyTask3,....); //创建用户任务 3.......OSStart(); //启动任务......}3) 系统任务 μC/OS-II 预定义了两个应用程序服务的系统任务:空闲任务和统计任务其中空闲任务是每个应用程序必须使用的,而统计任务则是应用程序可以根据实际需要来选择使用的。
1) 空闲任务 在多任务系统运行时,系统经常会在某个时间内无用户任务可以运行而处于所谓的空闲状态为了使 CPU 在没有使用用户任务可执行时有事可做,μC/OS-II 提高了一个叫做空闲任务 OSTaskIdle()的系统任务空闲任务代码如下Void OSTaskIdle(void* pdata) {# if OS_CRITICAL_METHOD ==3OS_CPU_SR cpu_sr;# endifpdata=pdata; //防止某些编译器报错for(;;){OS_ENTER_CRITICAL(); //关闭中断OSdleCtr++; //计数OS_EXIT_CRITICAL(); //开放中断}}μC/OS-II 规定,一个用户应用程序必须使用这个空闲任务,而且这个任务是不能用软件来删除的(2) .统计任务 μC/OS-II 提供的另一个系统任务是统计任务 OSTaskStart()这个统计任务每秒计算一次CPU 在单位时间内被使用的时间,并把计算结果以百分比的形式存放在变量 OSCPUUsage 中,以便应用程序通过访问它来了解 CPU 的利用率,所以该系统任务 OSTaskStart()叫做统计任务。
用户应用程序是否使用统计任务,用户可以根据应用程序的实际需要来进行选择如果用户应用程序要使用这个统计任务,则必须把定义的体贴头文件 OS-CFG.H 中的系统配置常数 OS_TASK_STAT_EN 设置为 1,并且必须在创建统计任务之前调用函数 OSStatInit()对统计任务进行初始化4) 任务的优先权及优先级别 前面以提到,μC/OS-II 的每个任务都必须具有一个惟一的优先级别μC/OS-II 把任务的优先权分为 64 个优先级别,每一个级别用一个都用的数字来表示数字 0 表示任务的优先级别最高;数字越大则表示任务的优先级别越低 通常,一个应用程序的任务数小于 64为了节省内存,用户可以根据应用程序的需要,在文件 OS_CFG.H 中通过给表示最低优先级别的常数 OS_LOWEST_PRIO 赋值的方法,来说明应用程序中任务优先级别的数目该常数一旦被定义,就意味着系统中可供使用的优先级别为 0、1、2、… 、OSl_LOWEST_PRIO,共 OSl_LOWEST_PRIO+1 个同时也限制了应用程序中任务的总数最多不能超过 OSl_LOWEST_PRIO+1 个。
固定地,系统总是把最低优先级别OS_LOWEST_PRIO 自动赋给空闲任务如果应用程序中还使用了统计任务,则系统会把优先级别为 OS_LOWEST_PRI-1 自动赋给统计任务,因此用户任务可以使用的优先级别是 0、1、2、… 、OSl_LOWEST_PRIO-2,共 OSl_LOWEST_PRIO-1 个3.2.4 实验仪器与设备1. TQ2440 实验平台2. J-LINK 调试器3. 串口线3.2.5 实验步骤1. 打开实验代码文件夹中的ucos_task_test子文件夹中的工程ucos_task_test.mcp工程2. 阅读代码,熟悉任务的建立方法3. 修改例程代码,建立两个任务,程序结构如图3-1所示图3-1 多任务示意图a) 修改工程,建立任务0:蜂鸣器唱歌任务(注意,单个音符的延迟应该采用μC/OS II的延迟函数OSTimeDly来实现,不用自己写延迟函数),进行调试,直到能够正常唱歌b) 建立任务1:仿照“2.2.4按键控制LED实验”建立按键检测LED控制任务c) 全速运行程序,观察在唱歌的同时,按下按键,LED能否发生变化d) 把建立任务的关键代码、两个任务的代码列出来,填入下表中序号程序现象1OSTaskCreate(Task0,(void*)0,&Task0Stk[Task0StkLengh - 1], 7); 创建一个优先级分别为7的Task02OSTaskCreate(Task1,(void*)0,&Task1Stk[Task0StkLengh - 1], 8); 创建一个优先级分别为8的Task13void Task0(void *pdata) //任务0,蜂鸣器唱歌{unsigned char Temp1,Temp2;U16 freq;// lci 1000 unsigned int Addr=0;while(1){ Temp1=SONG[Addr++]; Temp2=SONG[Addr++]; switch ( Temp1) { case 01: freq=260; break; case 02: freq=294; break; case 03: freq=328; break; case 04: freq=347; break; case 05: freq=390; break; case 06: freq=438; break; case 07: freq=490; break; case 11: freq=520; break; case 12: freq=581; break; case 13: freq=657; break; case 14: freq=694; break; case 15: freq=781; break; case 16: freq=892; break; case 17: freq=1000; break; case 21: freq=1041; break; case 22: freq=1190; break; case 23: freq=1315; break; case 24: freq=1388; break; case 25: freq=1562; break; case 26: freq=1785; break; case 27: freq=1923; break; default: freq='e';break; } Bu。