tag 标签: stm32开发指南

相关博文
  • 热度 39
    2013-5-17 23:47
    11295 次阅读|
    10 个评论
       第六十一章 战舰STM32开发板综合实验        前面已经给大家讲了55个实例了,本章将设计一个综合实例,作为本指南的最后一个实验 ,该实验向大家展示了STM32的强大处理能力,并且可以测试开发板的大部分功能。该实验代码非常多,涉及GUI(ALIENTEK编写,非ucGUI)、UCOS、内存管理、图片解码、MP3播放、文件系统、USB、IAP、NES模拟器、手写识别、汉字输入等非常多的内容,故本章不讲实现和代码,只讲功能,本章将分为如下几个部分:        61.1 战舰STM32开发板综合实验简介        61.2 战舰STM32开发板综合实验详解     61.1 战舰STM32开发板综合实验简介 战舰STM32开发板是ALIENTEK的第二款STM32开发板(第一款是MiniSTM32开发板),它的出现,主要是为了弥补Mini板在一些应用上的缺陷,提供大家一个更强大的STM32开发板平台。 战舰STM32开发板的硬件资源在第一章我们已经详细介绍过,是十分强大的,强大的硬件必须配强大的软件才能体现其价值,如果IPhone装的是andriod而不是ios,IPhone就不是那个IPhone了,可能早就被三星打败了。同样,如果开发板只是一堆硬件,那就和一堆废品差不多。 战舰STM32开发板的硬件在V1.0版本的时候(2010年12月份),基本就定型了,之后近2年多的时间,我们一直在编写代码,其中绝大部分时间是在写开发板的综合实验(即本实验),我们坚持资料不完善,坚决不卖,这样战舰STM32开发板的上市时间一推再推,硬件版本也从1.0升级到了1.8,甚至有朋友笑言,我都从大二等到大四了…在此,对那些还在等待我们开发板的朋友说声抱歉,谢谢你们的支持和理解。我想说,用心做产品,真的不容易,战舰开发过程中的点点滴滴,有机会再和大家分享。 在今年7月份的时候,终于把战舰STM32开发板综合实验的最后一个功能写完了,至此综合实验的开发基本完成,前前后后,耗时近两年。 接下来我们就看看战舰STM32开发板综合实验的功能吧。 战舰STM32开发板综合实验总共有18大功能,分为2页,每页9个功能,页面的切换采用滑动操作。18大功能分别为:电子图书、数码相框、音乐播放、应用中心、时钟、系统设置、FC游戏机、收音机、记事本、运行器、3D、手写画笔、照相机、录音机、USB连接、TOM猫、无线传书、计算器。 电子图书,支持.txt/.c/.h/.lrc等4种格式的文件阅读。 数码相框,支持.bmp/.jpeg/.jpb/.gif等4种格式的图片文件播放。 音乐播放,支持.mp3/.wma/.wav/.flac/.ogg/.mid/等常见音频文件的播放。 应用中心,可以扩展16个应用程序,我们实现了其中1个,其他留给大家自己扩展。 时钟,支持温度、时间、日期、星期的显示,并加入时间3D效果显示。 系统设置,整个综合实验的设置。 FC游戏机,即NES模拟器,支持.nes文件的运行,通过开发板玩NES游戏。 收音机,支持全范围FM(76Mhz~108Mhz)接收,支持手动/半自动/全自动搜台。 记事本,可以实现文本(.txt/.c/.h/.lrc)记录编辑等功能,支持中英文输入,手写识别。 运行器,即SRAM IAP功能,支持.bin文件的运行(文件大小+SRAM大小≤60K)。 3D,可以测量角度,并支持3D演示。 手写画笔,可以作画/对bmp图片进行编辑,支持画笔颜色/尺寸设置。 照相机,可以拍照(需要摄像头模块支持),并支持成像效果设置。 录音机,支持wav文件格式的录音(8Khz/16位单声道录音)。 USB连接,支持和电脑连接读写SD卡/SPI FLASH 的内容。 TOM猫,和手机的TOM猫游戏的功能类似,模仿人声,进行人机对话。 无线传书,通过无线模块,实现两个开发板之间的无线通信。 计算器,一个科学计算器,支持各种运算,精度为12位,支持科学计数法表示。 以上,就是综合实验的18个功能简介,涉及到的内容包括:GUI(ALIENTEK编写,非ucGUI)、UCOS、内存管理、图片解码、MP3播放、文件系统、USB、IAP、NES模拟器、手写识别、汉字输入等非常多的内容。下面,我们将详细介绍这18个功能。 61.2战舰STM32开发板综合实验详解 要测试战舰STM32开发板综合实验的全部功能,大家得自备1个SD卡和1个ALIENTEK摄像头模块。不过,就算没有这两个东西,综合实验还是可以正常运行的,只是有些限制而已,比如:不能保存新建的记事本、不能保存新建的画图、不能使用录音机功能、不能使用摄像头功能等。除了这几个,其他功能都可以正常运行。 我们先来看看战舰STM32开发板综合实验的启动界面,启动界面如图61.2.1所示:   图61.2.1 综合实验启动界面 注意:综合实验支持屏幕截图(通过 USMART 控制 , 波特率为 115200 ),本章所有图片均来自屏幕截图! 上图显示了综合实验的详细启动过程,首先显示了版权信息,软硬件版本,接着显示了LCD驱动器的型号(LCD ID),然后显示CPU和内存信息,之后显示SPI FLASH的大小,接着开始初始化文件系统(FATFS),然后显示SD卡容量和FLASH Disk容量(注意 FLASH Disk就是指SPI FLASH,因为我们划分了6M空间给FATFS管理,所以FLASH Disk的容量为6124KB)。 接着,就是硬件检测,完了之后检测字库和系统文件,再初始化触摸屏,加载系统参数(参数保存在24C02里面),最后启动系统。在加载过冲中,任何一个地方出错,都会显示相应的提示信息,请在检查无误后,按复位重启。 这里有几个注意的地方: ①     如果没插入SD卡,会显示SD Card ERROR,不过系统还是会继续启动,因为没有SD卡系统还是可以启动的(前提是SPI FLASH(W25Q64)里面的系统文件和字库文件都是正常的)。 ②     系统文件和字库文件都是存在SPI FLASH(W25Q64)里面的,如这两个文件被破坏了,在启动的时候,会执行字库和系统文件的更新,此时你得准备一个SD卡,并拷贝SYSTEM文件夹( 注意:这个 SYSTEM 文件夹不是开发板例程里的 SYSTEM 文件夹,而是光盘根目录 à SD 卡根目录文件 à SYSTEM 文件夹)到SD卡根目录,以便系统更新时使用。 ③     FLASH Disk是从SPI FLASH(W25Q64)里面分割6M空间出来实现的,强制将4K字节的扇区改为512字节使用,所以在写操作的时候擦除次数会明显提升(8倍以上),因此,如非必要,请不要往FLASH Disk里面写文件。频繁的写操作,很容易将FLASH Disk写挂掉。 ④     在系统启动时,一直按着KEY0不放(加载到Touch Check的时候),可以进入强制校准。当你发现触摸屏不准的时候,可以使用这个办法强制校准。 ⑤     在系统启动时,一直按着KEY1不放(加载到Font Check的时候),可以强制更新字库。 ⑥     本系统用到触摸按键TPAD做返回(类似手机的HOME键),所以请确保多功能端口P14的ADC和TPAD用跳线帽短接! ⑦     如果插入了SD卡,系统在启动的时候,会在SD卡的根目录创建4个文件夹:TEXT、RECORDER、PAINT和PHOTO。其中,TEXT文件夹用来保存新建的文本文件(记事本功能时使用);RECORDER文件夹用来保存录音文件(录音机功能时使用);PAINT文件夹用来保存新建的画板文件(手写画笔功能时使用);PHOTO文件夹用来保存相片(照相机功能时使用)。 在SYSTEM Starting…之后,系统启动UCOSII,并加载SPB界面,在加载成功之后,来到主界面,主界面如图61.2.2所示:   图61.2.2 综合实验系统主界面 这里主界面默认是简体中文的,我们可以在系统设置里面设置语言,战舰STM32开发板综合实验支持3种语言选择:简体中文、繁体中文和英文。 在进入主界面之后,开发板上的DS0开始有规律的短亮(每2.5秒左右亮100ms),提示系统运行正常,我们可以通过DS0判断系统的运行状况。另外,如果运行过程中,出现HardFault的情况,系统则会进入HardFault中断服务函数,此时DS0和DS1都会闪烁,提示系统故障。同时在串口打印故障信息。通过串口,系统会打印其他很多信息,最常打印的是内存使用率,然后我们还可以通过USMART对系统进行调试。 如图61.2.2所示,综合实验的主界面分为2页,通过滑动切换,系统刚启动的时候加载的是主界面A,通过滑动可以切换到主界面B,类似现在的智能手机。主界面,总共18个功能图标,我们可以随便点击一个即可选中,如图61.2.3所示:   图61.2.3 选中电子图书        从上图可以看出,选中之后,图标发生了一点点变化,手机图标也是类似的效果,其实就是一个alphablend。再次点击该图标,我们就可以进入电子图书功能。 在任何界面下,都可以通过按TPAD返回上一级,直至返回到主界面。PS:TPAD就是战舰STM32开发板上的一个触摸按键,即右下角的ALIENTEK LOGO!! 在介绍完系统启动之后,我们开始介绍各个功能。 61.2.1 电子图书        双击主界面的电子图书图标,进入如图61.2.1.1所示的文件浏览界面:   图61.2.1.1 文件浏览界面        上图中,左侧的图是我们刚刚进入的时候看到的界面(类似在XP上打开我的电脑),可以看到我们有2个盘:SD卡和FLASH 磁盘。我们可以选择任何一个打开,并浏览里面的内容。注意,即使没有插入SD卡,还是会出现SD卡图标,只是此时不能打开而已!        界面的上方显示文件/文件夹的路径。如果当前路径是磁盘/磁盘根目录则显示磁盘图标,如果是文件夹,则显示文件夹图标,另外,如果路径太深,则只显示部分路径(其余用…代替)。 界面的下方显示磁盘/文件夹信息。 界面的下方,显示磁盘信息/当前文件夹信息。对磁盘,则显示当前选中磁盘的总容量和可用空间,对文件夹,则显示当前路径下文件夹总数和文件总数,并显示你当前选中的是第几个文件夹/文件。        双击打开SD卡,得到界面如右侧图片所示,此时,因为SD卡根目录的文件数目超过了1页所能显示的数目,所以在右侧出现了滚动条,我们可以拖动滚动条/按滚动条两端的按钮/直接在屏幕中心区域拖动,来查找你要打开的文件/文件夹。        选中一个文件夹,双击打开得到如图61.2.1.2所示界面:   图61.2.1.2 目标文件和文本阅读        上图左侧显示了当前文件夹下面的目标文件(即电子图书支持的文件,包括.txt/.h/.c/.lrc等格式,其中.txt/.h/.c文件共用1个图标,.lrc文件单独一个图标)。另外,如果文件名太长,在我们选中该文件名后,系统会以走字的形式,显示整个文件名。 我们打开一个lrc文件,开始文本阅读,如图右侧的图片所示,同样我们可以通过滚动条/拖动的方式来浏览,图中我们还看到有一个光标,触摸屏点到哪,它就在哪里闪烁,可以方便大家阅读。        文本阅读是将整个文本文件加载到外部内存里面来实现的,所以文本文件最大不能超过外部内存总大小,即680KB(这里仅指受内存管理的部分,不是整个外部SRAM的大小)。        当我们想退出文本阅读的时候,通过按TPAD触摸按键实现,按一下TPAD,则又回到查找目标文件状态(左侧图),按返回按钮可以返回上一层目录,如果再按一次TPAD则直接返回主界面。 61.2.2 数码相框        双击主界面的数码相框图标,进入文件浏览界面,这个和61.2.1节差不多,我们找到存放图片的文件夹,如图61.2.2.1所示:   图61.2.2.1 文件浏览和图片播放        左侧是文件浏览的界面,可以看到在图片文件夹下总共有18个文件,包括gif/jpg/bmp等,这些都是数码相框功能所支持的格式。右侧图片显示了一个正在播放的GIF图片,并在其左上角显示当前图片的名字。当然,我们也可以播放bmp和jpg文件,如图61.2.2.2所示:   图61.2.2.2 bmp和jpg图片播放 对于bmp和jpg文件,基本没有尺寸限制(但图片越大,解码时间越久),但是对于gif文件,则只支持尺寸在240*320以内的文件(因为gif图片我们不好做尺寸压缩处理),超过这个尺寸的gif图片将无法显示!! 我们可以通过按屏幕的上方(1/3屏幕)区域切换到上一张图片浏览;通过按屏幕的下方(1/3屏幕)区域切换到下一章图片;通过单击屏幕的中间(1/3屏幕)区域可以暂停自动播放,同时DS1亮,提示正在暂停状态,双击屏幕的中间区域会弹出返回按钮,如图61.2.2.3所示:   图61.2.2.3 弹出返回按钮        此时,我们可以通过按返回按钮返回文件浏览状态,当然也可以通过按TPAD按钮,直接返回文件浏览状态(不需要等返回按钮弹出)。        图片浏览支持两种自动播放模式:循环播放/随即播放。大家可以在系统设置里面设置图片播放模式。系统默认是循环播放模式,在该模式下,每隔4秒左右自动播放下一张图片,依次播放所有图片。而随机播放模式,也是每隔4秒左右自动播放下一张图片,但是不是顺序播放,而是随机的播放下一张图片。 另外需要注意,不是所有的jpg格式图片都可以在我们的开发板上正常播放的(解码程序的问题),只有JFIF格式的jpg文件才能正常解码显示,对于EXIF格式的jpg文件,则不能直接显示,大家可以将EXIF格式的jpg文件用XP的画图打开,然后再保存一下,就将EXIF格式转为JFIF格式了,这样就可以在开发板上正常解码,并显示了。 61.2.3 音乐播放        双击主界面的音乐播放图标,进入文件浏览界面,这个和61.2.1节差不多,只是这里我们浏览的文件变为了.mp3/.ogg/.wma/.flac/.wav/.midi等音频文件,我们找到存放音频文件的文件夹,如图61.2.3.1所示:   图61.2.3.1 文件浏览和ogg格式播放        左侧是文件浏览的界面,可以看到在MUSIC文件夹下总共有37个音频文件,包括mp3/ogg/wma/flac/wav等格式,这些都是播放器所支持的格式。右侧图片则是我们播放器的主界面,该界面显示了当前播放歌曲的名字、播放进度、播放时长、总时长、码率、音量、当前文件编号、总文件数、歌词等信息。下方的5个按键分别是:目录、上一曲、暂停/播放、下一曲、返回。点击播放进度条,可以直接设置歌曲播放位置,点击声音进度条,可以设置音量。上图为正在播放ogg文件,当然我们还可以播放其他音频格式,如图61.2.3.2所示:   图61.2.3.2 mp3格式播放和flac格式播放        图61.2.3.2中,分别显示了播放mp3格式和flac格式的音频文件。播放flac格式的时候,由于得不到正确的码率,所以总时间也是不正确的,图中数字仅供参考。另外播放flac因为要加载flac的patch,故无法加载频谱分析的patch,从而无法显示频谱,可以看到在右侧的图片中,没有频谱显示了,除了flac不能显示频谱,其他音频文件都是可以正常显示频谱的。 播放器还可以设置音效和播放模式(均在系统设置里面设置)。音效包括高低音调节、空间效果等设置。播放模式有3种:全部循环、随机播放、单曲循环,默认为全部循环。        另外,关于歌词显示。歌词必须和歌曲在同一个文件夹里面,且名字必须相同(当然后缀是不同的,歌词后缀为.lrc),这样才能正常显示歌词。对于没有歌词文件的歌曲,则直接播放,不显示歌词。歌词分为3行,第一行为上一句歌词,第二行为当前正在唱的歌词,第三行为将要唱的歌词。对于第二行歌词,如果太长,则会采用走字的形式来显示,走字时间由系统自动确定。        我们可以通过按目录按钮,来选择其他音频文件;按返回按键(或TPAD)则可以返回主界面,不过此时正在播放的歌曲还是会继续播放(后台播放),如果想关闭音乐播放器,则需要先按暂停,然后返回主界面,即可关闭音频播放器,否则音频播放器将一直播放音乐。        最后,我们默认是开启了FM发射的,在播放MP3的时候,音频会通过RDA5820发送出去,默认的频率是93.6Mhz,大家可以打开收音机调到93.6Mhz,就可以听到来自开发板的歌声了。FM发射频率和发射开关也都是可以在系统设置里面设置的,具体后面再介绍。 61.2.4 应用中心        双击主界面的应用中心图标,进入应用中心界面,如图61.2.4.1所示:   图61.2.4.1 应用中心和红外遥控        左侧图片是我们刚进入应用中心看到的界面,在该界面下总共有16个图标,我们仅实现了第一个:红外遥控功能。其他都没有实现,大家可以自由发挥,添加属于自己的东西。双击第一个图标,会弹出一个红外遥控的小窗口,用于接收红外信号,如图61.2.4.1右侧图片所示。        此时,我们将红外遥控对准战舰STM32开发板的红外接收头,并按钮,则可以在红外遥控窗体里面显示键值、按键次数、符号等信息。如图61.2.4.2所示:   图61.2.4.2 红外按键解码        图中,我们按下了红外遥控器下的两个按键,分别得到两个按键的键值、次数和符号等信息。其中次数是代表我们持续按下红外遥控某个按键的时长,越长该值越大。        需要注意一点是,如果当前正在播放MP3,则红外解码成功率大大降低,原因是MP3播放任务的优先级最高,严重影响红外信号接收,导致解码成功率降低,当发现无法识别的时候,可以先停止MP3的播放再试试。 61.2.5 时钟        双击主界面的时钟图标,进入时钟界面,如图61.2.5.1所示:   图61.2.5.1 时钟界面        图61.2.5.1的左侧图片为加载时钟界面时的提示界面,表明没有检测到18B20,启用内部温度传感器,之后进入时钟主界面,如右侧图片所示。在时钟界面,我们显示了日期、时间、温度、星期等信息,并且在屏幕上方区域,有一个3D的时间在显示,3D时间显示会不停的变换位置,位置变化是无规律的。我们可以在系统设置里面设置时间和日期,并且还可以设置闹钟和闹铃,这个我们后面再介绍。 图中的温度是通过STM32自带的温度传感器采集的,所以有点偏高,如果我们在开发板的U13处插入DS18B20,则会采集来自18B20的温度,这样就比较准确了。 在进入时间界面以后,要退出该界面有2个办法:1,在屏幕向左滑动触摸;2,按TPAD返回。 61.2.6 系统设置        双击主界面的系统设置图标,进入系统设置界面,如图61.2.6.1所示:   图61.2.6.1 系统设置主界面和时间设置界面        上图中左侧的图片为系统设置主界面,在系统设置里面,总共有19个项目:时间设置、日期设置、闹钟时间设置、闹钟开关设置、闹钟铃声设置、语言设置、数码相框设置、MP3播放模式设置、MP3音效设置、FM发射开关设置、FM发射频率设置、FM收音设置、背光设置、屏幕校准、传感器校准、系统文件更新、系统信息、系统状态、关于。通过这19个项目,我们 可以设置和查看各种系统参数。下面我们将一一介绍这些设置。        首先是时间设置,如图61.2.6.1右侧图片所示,双击时间设置,就会弹出一个时间是指对话框,通过这个对话框,我们就可以设置开发板的时间了。设置好之后点击确定回到系统设置主界面,如果想放弃设置,则直接点击取消(或TPAD)。        再来看看日期设置和闹钟时间设置,如图61.2.6.2所示:   图61.2.6.2 日期设置和闹钟时间设置        上图中,左侧的对话框用来设置系统日期,右侧的对话框用来设置闹钟时间。操作上同前面介绍的时间设置的方法一模一样。关于闹钟,我们等下再详细介绍,先看闹钟开关设置和闹钟铃声设置两个界面,如图61.2.6.3所示:   图61.2.6.3 闹钟开关设置和闹钟铃声设置        上图中,左侧对话框用来设置闹钟开关,右侧对话框用来设置闹钟铃声。这里,我们来介绍一下本系统的闹钟,本系统的闹钟以星期为周期,以时间为点实现闹钟,比如判断一个闹钟是否应该响铃的标准是:先判断星期的条件是否满足,比如上图我们设置是周一到周五闹铃,今天(10月5号)刚好是周五,所以满足星期条件,接着看时间是否相等,如果两个条件都满足,则闹铃。从前面的时间设置我们知道当前时间是20:30分,而上图我们设置的闹钟时间是20:35,所以时间还不相等,故不闹铃,当时间来到20:35的时候,系统将会闹铃。闹铃铃声有4种,如上图右侧图片所示,铃声由蜂鸣器产生,铃声1对应“滴”,铃声2对应“滴、滴”,铃声3和4依此类推。当闹钟时间到来的时候,产生闹铃,如图61.2.6.4所示:   图61.2.6.4 闹铃和语言设置        上图中,左侧的图片显示正在闹铃。此时会弹出一个闹钟的对话框,并显示当前时间,同时蜂鸣器发出“滴、滴、滴、滴”的闹铃声(铃声4)。按取消(或TPAD)可以关闭闹钟,按再响,则5分钟后(20:40)继续闹铃。右侧的图片为语言设置界面,系统支持3种语言设置,默认为简体中文,设置为繁体中文/English之后如图61.2.6.5所示:   图61.2.6.5 繁体中文和English        上图显示了繁体中文和English的设置,不过本章我们还是以简体中文为例进行介绍。下面,我们来看看数码相框设置和MP3播放模式设置,如图61.2.6.6所示:   图61.2.6.6 数码相框设置和MP3播放模式设置        前面提到数码相框支持全部循环播放和随机播放两种模式,就是通过上图左侧的界面设置的。而MP3的三个播放模式,则通过右侧的界面进行设置。接下来看看MP3音效设置和FM发射开关设置,如图61.2.6.7所示:   图61.2.6.7 MP3音效设置和FM发射开关设置        上图中,左侧的界面我们可以设置MP3播放的音效(VS1053的设置),包括音量、高低音以及空间效果等,大家可以根据自己喜欢设置,以上为默认设置。右侧的FM发射开关设置,用来设置是否开启FM发射,默认设置为开启,即只要不是收音机模式,其他所有界面FM发射都是开启的,这样我们就可以通过收音机来听到来自STM32开发板的声音了。        下面我们看看FM发射频率设置和FM收音设置,如图61.2.6.8所示:   图61.2.6.8 FM发射频率设置和FM收音设置        上图中,左侧的界面用于设置FM发射频率,用于设置FM发射频点,我们默认的频率是93.6Mhz,所以大家的收音机请调到93.6Mhz(默认频率),以接听来自开发板的声音。右侧的图片用于设置FM收音是否开启后台播放的功能。 接下来,我们看看背光设置和屏幕校准,如图61.2.6.9所示:   图61.2.6.9 背光设置和屏幕校准        上图中,左侧的界面用于设置LCD背光的亮度,默认我们是设置为最亮的,大家可以根据自己的喜欢设置背光亮度,背光亮度控制是通过PWM控制的。 右侧为触摸屏校准界面,这个校准界面和手机校准界面基本类似,校准的时候,请用触笔(或者其他尖一点的东西)依次点击4个十字圈的最中心(图中只是第一个,如果点击了第一个会自动弹出第二个,总共4个),在4个校准点都准确点击之后,系统提示校准成功字符串:Touch Screen Adjust OK!。如果校准失败,则提示失败信息,请重新校准,直到校准成功,如果多次校准都不成功,有可能你的触摸屏有问题了! 另外,在该界面下,如果连续10秒没有输入的话,系统会自动退出校准界面,当然,我们也可以按TPAD直接退出。        接下来,我们看看传感器校准和系统文件更新,如图61.2.6.10所示:   图61.2.6.10 传感器校准和系统文件更新提示        图中,左侧图片为传感器校准界面,这里的传感器设置ADXL345重力加速度传感器,校准的时候,请保持开发板水平并稳定,以得到最好的校准效果。 右侧的界面为系统文件更新提示界面,这里的系统文件是指SYSTEM文件夹里面的所有内容。战舰STM32开发板综合例程之所以可以没有SD卡也能正常运行,主要是将SYSTEM文件夹(注意这个不是源码里面的SYSTEM文件夹!!)拷贝到了FLASH Disk(即W25Q64)里面,这样,我们所有的系统资源都可以从W25Q64里面获得,从而正常启动。 SYSTEM文件夹目前是包含144个文件,总大小为2.6MB,包括137个图片/图标,另外包括5个字库相关文件以及2个VS1053的PATCH文件。这些文件一般不要修改,如果你想自己DIY的话,那可以修改这些文件,以达到你要的效果,不过建议修改之前备份一下,搞坏了还可以还原。        如果在图61.2.6.10的系统文件更新提示时选择确定,则会执行系统文件更新,将SD卡的SYSTEM文件夹,拷贝到FLASH Disk里面。这里有个前提,就是你的SD卡必须有这个SYSTEM文件夹!更新时界面如图61.2.6.11所示:   图61.2.6.11 系统文件更新和系统信息        上图中,左侧的界面显示了系统文件正在更新,该界面显示了当前更新的文件夹以及文件和进度等信息。右侧的界面为系统信息界面,通过该界面,可以看到软硬件的详细信息。        最后,我们来看看系统状态和关于界面,如图61.2.6.12所示:   图61.2.6.12 系统状态和关于界面        上图中,左侧的界面显示了当前系统资源状况,显示了当前CPU使用率,CPU温度以及内存使用率。图为后台正在播放MP3的时候资源使用情况,当播放高码率的歌曲的时候,CPU使用率会大增(如播放wav,则CPU使用率在60%左右)。        右侧的图片显示了战舰STM32开发板的软硬件版本以及产品序列号,这个序列号是全球唯一的,每个开发板都不一样。 61.2.7 FC游戏机        战舰STM32开发板综合实验移植了NES模拟器,可以运行nes游戏,双击主界面的系统设置图标,进入文件浏览界面,如图61.2.7.1所示:   图61.2.7.1 文件浏览和小蜜蜂游戏        左侧为nes文件浏览界面,我们随便选择一个打开即可开始游戏了,记得插上手柄哦!右侧的图片为小蜜蜂游戏的界面,当然还可以玩很多其他经典游戏,如下面的图片所示:   图61.2.7.2 超级玛丽和90坦克   图61.2.7.3 超时空要塞和中国象棋   图61.2.7.4 马戏团和淘金者        这里,我们仅列出了几种游戏,这都是80后童年时玩的经典游戏,如今,在战舰STM32开发板上,大家可以回味一下当年的经典了。        不过,我们提供的nes模拟器,由于代码问题,对大于50KB的nes文件基本不支持,不过即使这样,还是有很多游戏可玩的。另外也没有加入声音输出。如果对nes模拟器有兴趣的朋友可以完善一下这两方面,我们在光盘提供了相关资料可供研究。   61.2.8 收音机        双击主界面的收音机图标,进入收音机界面,如图61.2.8.1所示:   图61.2.8.1 收音机主界面和模式选择界面        上图中,左侧图片为收音机的主界面,显示了当前频率、单/双声道、信号强度、音量、电台编号(自动搜台的时候自动保存)等信息,界面下方的5个按钮分别是:模式选择、频率减(或上一个电台)、暂停/继续收音、频率增(或下一个电台)和返回。右侧的图片为按了模式选择后弹出的界面,选择模式设置/频段选择并按确认后,得到如图61.2.8.2所示:   图61.2.8.2 模式设置和频段选择界面        上图中,左侧的图片为模式设置界面,总共有3个模式可以设置:手动搜台、半自动搜台和全自动搜台。        手动搜台:完全手动搜索,通过频率增/减两个按钮调节频率。        半自动搜台:此时频率增/减分别代表查找下一个/上一个电台,只要按一下按钮,收音机自动查找下一个/上一个电台,找到有效电台即停止搜索,并播放这个有效电台。        全自动搜台:选中之后,收音机从最小频率开始找台,一直搜索到最大频率,把整个过程中的有效电台记录下来,搜索完毕,可以从主界面的“CH:”看到总有效电台的个数,可以通过频率增/减按钮来跳转电台。        右侧的图片为频段选择界面,本收音机支持3个频段:日本频段(76Mhz~91Mhz)、欧美频段(87~108Mhz,也是中国电台使用的频段)、扩展频段(76Mhz~108Mhz)。默认设置为欧美频段。        收音机可以后台工作,只要您在系统设置里面开启了后台收音。如果没有开启后台收音,在按返回键之后,收音机将自动关闭。        本收音机使用起来还是比较简单,使用时,请把天线拉出,如果搜不到台,一般是因为你所处环境干扰太大,建议去空旷地方试试。 61.2.9 记事本        双击主界面的记事本图标,首先弹出模式选择对话框,如图61.2.9.1所示:   图61.2.9.1 模式选择和新建文本文件        记事本支持2种模式:1,新建文本文件,这种方式完全新建一个文本文件(以当前系统时间命名),用来输入信息。2,打开已有文件,这种方式可以对已有的文件进行编辑。        上图中,右侧的界面为我们选择新建文本文件后的界面,此时出现一个空白编辑区和一个闪烁的光标,我们通过下方的键盘输入信息即可,这个输入键盘和我们的手机键盘十分类似,输入方法也是一模一样,支持中文、字母、数字和手写识别输入等几种输入方式。中文输入和标点符号输入,如图61.2.9.2所示:   图61.2.9.2中文输入和标点符号输入        中文输入就是我们前面T9拼音输入法实验的具体运用。该键盘还支持英文输入和手写识别输入,如图61.2.9.3所示:   图61.2.9.3中文输入和标点符号输入        上图中,左侧的图片为英文输入界面,比较简单;右侧的图片为手写识别的输入界面,这里我们也是用到前面手写识别实验的知识实现的。        只要新建文本文件有被编辑过,那么在返回(按TPAD返回)的时候,系统会提示是否保存,如图61.2.9.4所示:   图61.2.9.4保存提示和编辑已有文件        上图中,左侧图片为提示保存界面,如果选择确定,该文件将被保存在SD卡根目录的TEXT文件夹里面。右侧图片为打开已有文件进行编辑的界面,这样我们就可以在战舰STM32开发板上编辑.txt/.h/.c/.lrc文件了。 61.2.10 运行器 双击主界面的运行器图标,首先进入文件浏览界面,如图61.2.10.1所示:   图61.2.10.1文件浏览和运行警告        上图中,左侧为文件浏览界面,图中显示了可运行的bin文件有41个,这些全部来自我们的标准例程。bin文件的生成办法,请参考串口IAP实验这个章节。本运行器支持60K字节以内的程序运行(FLASH+SRAM总共不超过60K),我们的例程有多达41个实验可以直接在运行器里面运行(生成.bin文件),我们提供了SRAM APP版本的例程,编译后直接生成.bin文件,拷贝到SD卡,即可运行查看实验现象。所有41个例程的.bin文件,我们已单独放到一个文件夹,供大家测试使用。通过运行器,大家可以直接运行我们大部分例程,而不用再去刷代码了,方便大家测试和验证我们的实验。        右侧的图片是运行前的警告界面,因为一旦执行.bin文件,我们的系统将无法恢复,只能靠复位重启。点击确定之后,STM32就开始运行你所选择的.bin文件了,实验现象和对应实验所描述的现象一模一样。之后, 61.2.11 3D 双击主界面的3D图标,进入3D演示界面,如图61.2.11.1所示:   图61.2.11.1文件浏览和运行警告        左侧的图片为我们刚进入是的界面(假设板子是水平放置的),此时可以看到X/Y/Z三个方向的角度基本都是0,屏幕中心为一个立方体图形,该图形会随着角度的变化而变化。右侧的图片,显示了我们后我们把板子倾斜一定角度放置时的情况,可以看到X/Y/Z角度都发生了变化,而且立方体图形也产生了变化。        我们还可以通过触摸屏来控制立方体的转动,直接在屏幕滑动即可看到立方体随着我们的滑动而改变方向(视角)。 61.2.12 手写画笔        双击主界面的手写画笔图标,首先弹出模式选择对话框,如图61.2.12.1所示:   图61.2.12.1模式选择和新建画笔        上图中,左侧图片为我们双击手写画笔后,弹出的模式选择界面,我们可以选择新建画笔,建立一个新的文件;也可以选择打开一个已有的位图进行编辑。右侧的图片为我们新建画笔后输入的内容,默认画笔为最小尺寸,颜色为红色。画笔的颜色和尺寸是可以设置的,按WK_UP按键,则弹出画笔设置对话框,如图61.2.12.2所示:   图61.2.12.2画笔设置和画笔颜色设置        上图中,左侧的图片为按WK_UP按键后弹出的画笔设置对话框,我们可以选择对画笔颜色和画笔尺寸进行设置。右侧的图片为画笔颜色设置对话框,在该对话框里面,我们可以直接在颜色条快速输入要设置的颜色,也可以通过下方的三个滚动条进行精确设置,右侧的正方形区域为预览区。画笔尺寸设置界面如图61.2.12.3所示:   图61.2.12.3画笔尺寸设置和完成后的画图        上图中,左侧为画笔尺寸设置界面,我们可以通过滚动条设置画笔尺寸,对话框显示了画笔尺寸和对应的预览图。右侧的图片为我们完成的画图文件,在返回主界面(按TPAD)的时候,会提示保存,如图61.2.12.4所示:   图61.2.12.4保存画图和编辑已有位图        上图中,左侧为我们退出时弹出的提示保存对话框,如果选择确定,新的画图文件将会被保存在SD卡的PAINT文件夹里面,命名方式是以时间命名的,如PAINT20120907133223.bmp。        右侧的图片为对打开的位图进行编辑的界面,通过这个功能,我们可以在开发板上实现对一些相片(bmp格式)进行涂鸦。 61.2.13 照相机        双击主界面的照相机图标,首先初始化OV7670摄像头模块,如图61.2.13.1所示:   图61.2.13.1 初始化OV7670和等待拍照        在初始化OV7670之后,进入等待拍照模式,此时我们可以通过点击屏幕,弹出相机设置对话框,对摄像头的参数进行设置,如图61.2.13.2:   图61.2.13.2 相机设置和优先模式设置        在相机设置界面,我们可以对很多参数进行调节。右侧的图片为优先模式设置,支持速度优先和清晰度优先(通过降低帧率实现)两种模式,我们默认的是速度优先模式。 再来看看场景设置和特效设置,如图61.2.13.3所示:   图61.2.13.3 场景设置和特效设置        场景设置支持5种常用场景,特效设置支持6种特效(不含普通模式),我们可以根据自己的需要选择。  省略部分内容!!
  • 热度 29
    2013-5-3 23:02
    3544 次阅读|
    0 个评论
       第六十章 UCOSII实验3-消息队列、信号量集和软件定时器   上一章,我们学习了UCOSII的信号量和邮箱的使用,本章,我们将学习消息队列、信号量集和软件定时器的使用。本章分为如下几个部分: 60.1 UCOSII消息队列、信号量集和软件定时器简介 60.2 硬件设计 60.3 软件设计 60.4 下载验证   60.1 UCOSII消息队列、信号量集和软件定时器简介 上一章,我们介绍了信号量和邮箱的使用,本章我们介绍比较复杂消息队列、信号量集以及软件定时器的使用。 消息队列 使用消息队列可以在任务之间传递多条消息。消息队列由三个部分组成:事件控制块、消息队列和消息。当把事件控制块成员OSEventType的值置为OS_EVENT_TYPE_Q时,该事件控制块描述的就是一个消息队列。 消息队列的数据结构如图60.1.1所示。从图中可以看到,消息队列相当于一个共用一个任务等待列表的消息邮箱数组,事件控制块成员OSEventPtr指向了一个叫做队列控制块(OS_Q)的结构,该结构管理了一个数组MsgTbl ;   //定时器控制块数组 OS_EXT OS_TMR *OSTmrFreeList;                             //空闲定时器控制块链表指针 OS_EXT OS_TMR_WHEEL OSTmrWheelTbl ;//定时器轮 其中OS_TMR为定时器控制块,定时器控制块是软件定时器管理的基本单元,包含软件定时器的名称、定时时间、在链表中的位置、使用状态、使用方式,以及到时回调函数及其参数等基本信息。 OSTmrTbl ;:以数组的形式静态分配定时器控制块所需的RAM空间,并存储所有已建立的定时器控制块,OS_TMR_CFG_MAX为最大软件定时器的个数。 OSTmrFreeLiSt:为空闲定时器控制块链表头指针。空闲态的定时器控制块(OS_TMR)中,OSTmrnext和OSTmrPrev两个指针分别指向空闲控制块的前一个和后一个,组织了空闲控制块双向链表。建立定时器时,从这个链表中搜索空闲定时器控制块。 OSTmrWheelTbl :该数组的每个元素都是已开启定时器的一个分组,元素中记录了指向该分组中第一个定时器控制块的指针,以及定时器控制块的个数。运行态的定时器控制块(OS_TMR)中,OSTmrnext和OSTmrPrev两个指针同样也组织了所在分组中定时器控制块的双向链表。软件定时器管理所需的数据结构示意图如图60.1.5所示:   图60.1.5 软件定时器管理所需的数据结构示意图 OS_TMR_CFG_WHEEL_SIZE定义了OSTmrWheelTbl的大小,同时这个值也是定时器分组的依据。按照定时器到时值与OS_TMR_CFG_WHEEL_SIZE相除的余数进行分组:不同余数的定时器放在不同分组中;相同余数的定时器处在同一组中,由双向链表连接。这样,余数值为0~OS_TMR_CFG_WHEEL_SIZE-1的不同定时器控制块,正好分别对应了数组元素OSTmr-WheelTbl ~OSTmrWheelTbl 的不同分组。每次时钟节拍到来时,时钟数OSTmrTime值加1,然后也进行求余操作,只有余数相同的那组定时器才有可能到时,所以只对该组定时器进行判断。这种方法比循环判断所有定时器更高效。随着时钟数的累加,处理的分组也由0~OS_TMR_CFG_WHE EL_SIZE-1循环。这里,我们推荐OS_TMR_CFG_WHEEL_SIZE的取值为2的N次方,以便采用移位操作计算余数,缩短处理时间。 信号量唤醒定时器管理任务,计算出当前所要处理的分组后,程序遍历该分组中的所有控制块,将当前OSTmrTime值与定时器控制块中的到时值(OSTmrMatch)相比较。若相等(即到时),则调用该定时器到时回调函数;若不相等,则判断该组中下一个定时器控制块。如此操作,直到该分组链表的结尾。软件定时器管理任务的流程如图60.1.6所示。   图60.1.6 软件定时器管理任务流程 当运行完软件定时器的到时处理函数之后,需要进行该定时器控制块在链表中的移除和再插入操作。插入前需要重新计算定时器下次到时时所处的分组。计算公式如下: 定时器下次到时的OSTmrTime值(OSTmrMatch)=定时器定时值+当前OSTmrTime值 新分组=定时器下次到时的OSTmrTime值(OSTmrMatch)%OS_TMR_CFG_WHEEL_SIZE 接下来我们看看在UCOSII中,与软件定时器相关的几个函数。 1)  创建软件定时器函数 创建软件定时器通过函数OSTmrCreate实现,该函数原型为:OS_TMR *OSTmrCreate (INT32U dly, INT32U period, INT8U opt, OS_TMR_CALLBACK callback,void  *callback_arg, INT8U *pname, INT8U *perr)。 dly,用于初始化定时时间,对单次定时(ONE-SHOT模式)的软件定时器来说,这就是该定时器的定时时间,而对于周期定时(PERIODIC模式)的软件定时器来说,这是该定时器第一次定时的时间,从第二次开始定时时间变为period。 period,在周期定时(PERIODIC模式),该值为软件定时器的周期溢出时间。 opt,用于设置软件定时器工作模式。可以设置的值为:OS_TMR_OPT_ONE_SHOT或OS_TMR_OPT_PERIODIC,如果设置为前者,说明是一个单次定时器;设置为后者则表示是周期定时器。 callback,为软件定时器的回调函数,当软件定时器的定时时间到达时,会调用该函数。 callback_arg,回调函数的参数。 pname,为软件定时器的名字。 perr,为错误信息。 软件定时器的回调函数有固定的格式,我们必须按照这个格式编写,软件定时器的回调函数格式为:void (*OS_TMR_CALLBACK)(void *ptmr, void *parg)。其中,函数名我们可以自己随意设置,而ptmr这个参数,软件定时器用来传递当前定时器的控制块指针,所以我们一般设置其类型为OS_TMR*类型,第二个参数(parg)为回调函数的参数,这个就可以根据自己需要设置了,你也可以不用,但是必须有这个参数。 2)  开启软件定时器函数 任务可以通过调用函数OSTmrStart开启某个软件定时器,该函数的原型为:BOOLEAN  OSTmrStart (OS_TMR *ptmr, INT8U *perr)。其中ptmr为要开启的软件定时器指针,perr为错误信息。 3)  停止软件定时器函数 任务可以通过调用函数OSTmrStop停止某个软件定时器,该函数的原型为:BOOLEAN  OSTmrStop (OS_TMR *ptmr,INT8U opt,void *callback_arg,INT8U *perr)。 其中ptmr为要停止的软件定时器指针。 opt为停止选项,可以设置的值及其对应的意义为:        OS_TMR_OPT_NONE,直接停止,不做任何其他处理        OS_TMR_OPT_CALLBACK,停止,用初始化的参数执行一次回调函数        OS_TMR_OPT_CALLBACK_ARG,停止,用新的参数执行一次回调函数            callback_arg,新的回调函数参数。            perr,错误信息。 软件定时器我们就介绍到这。 60.2 硬件设计 本节实验功能简介:本章我们在UCOSII里面创建7个任务:开始任务、LED任务、触摸屏任务、队列消息显示任务、信号量集任务、按键扫描任务和主任务,开始任务用于创建邮箱、消息队列、信号量集以及其他任务,之后挂起;触摸屏任务用于在屏幕上画图,测试CPU使用率;队列消息显示任务请求消息队列,在得到消息后显示收到的消息数据;信号量集任务用于测试信号量集,采用OS_FLAG_WAIT_SET_ANY的方法,任何按键按下(包括TPAD),该任务都会控制蜂鸣器发出“滴”的一声;按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;主任务创建3个软件定时器(定时器1,100ms溢出一次,显示CPU和内存使用率;定时2,200ms溢出一次,在固定区域不停的显示不同颜色;定时3,,100ms溢出一次,用于自动发送消息到消息队列),并通过查询消息邮箱获得键值,根据键值执行DS1控制、控制软件定时器3的开关、触摸区域清屏、触摸屏校和软件定时器2的开关控制等。 所要用到的硬件资源如下: 1)  指示灯DS0 、DS1  2)  4个机械按键(KEY0/KEY1/KEY2/WK_UP) 3)  TPAD触摸按键 4)  蜂鸣器 5)  TFTLCD模块 这些,我们在前面的学习中都已经介绍过了。 60.3 软件设计 本章,我们在第四十三章实验 (实验38 )的基础上修改,首先,是UCOSII代码的添加,具体方法同第五十九章一模一样,本章就不再详细介绍了。本章OS_TICKS_PER_SEC的设置还是为500,即UCOSII的时钟节拍为2ms。另外由于我们创建了7个任务,加上统计任务、空闲任务和软件定时器任务,总共10个任务,如果你还想添加其他任务,请把OS_MAX_TASKS的值适当改大。 另外,我们还需要在os_cfg.h里面修改软件定时器管理部分的宏定义,修改如下: #define OS_TMR_EN                           1u         //使能软件定时器功能 #define OS_TMR_CFG_MAX                  16u       //最大软件定时器个数 #define OS_TMR_CFG_NAME_EN              1u        //使能软件定时器命名 #define OS_TMR_CFG_WHEEL_SIZE          8u        //软件定时器轮大小 #define OS_TMR_CFG_TICKS_PER_SEC       100u      //软件定时器的时钟节拍(10ms) #define OS_TASK_TMR_PRIO                        0u        //软件定时器的优先级,设置为最高     这样我们就使能UCOSII的软件定时器功能了,并且设置最大软件定时器个数为16,定时器轮大小为8,软件定时器时钟节拍为10ms(即定时器的最少溢出时间为10ms)。 最后,我们只需要修改test.c函数了,打开test.c,输入如下代码:  //省略部分内容!!!见附件。    
  • 热度 22
    2013-5-3 20:42
    2703 次阅读|
    1 个评论
    第五十七章 ENC28J60网络实验   本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块和uIP 1.0实现:TCP服务器、TCP客服端以及WEB服务器等三个功能。本章分为如下几个部分: 57.1 ENC28J60以及uIP简介 57.2 硬件设计 57.3 软件设计 57.4 下载验证   57.1 ENC28J60以及uIP简介 本章我们需要用到ENC28J60以太网控制器和uIP 1.0以太网协议栈。接下来分别介绍这两个部分。 57.1.1 ENC28J60简介 ENC28J60 是带有行业标准串行外设接口(Serial Peripheral Interface,SPI)的独立以太网控制器。它可作为任何配备有SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3 的全部规范,采用了一系列**滤机制以对传入数据包进行限制。 它还提供了一个内部DMA模块,以实现快速数据吞吐和硬件支持的IP校验和计算。 与主控制器的通信通过两个中断引脚和 SPI 实现,数据传输速率高达10 Mb/s。两个专用的引脚用于连接LED,进行网络活动状态指示。        ENC28J60的主要特点如下: l  兼容IEEE802.3协议的以太网控制器 l  集成MAC和10 BASE-T物理层 l  支持全双工和半双工模式 l  数据冲突时可编程自动重发 l  SPI接口速度可达10Mbps l  8K数据接收和发送双端口RAM l  提供快速数据移动的内部DMA控制器 l  可配置的接收和发送缓冲区大小 l  两个可编程LED输出 l  带7个中断源的两个中断引脚 l  TTL电平输入 l  提供多种封装:SOIC/SSOP/SPDIP/QFN等 ENC28J60的典型应用电路如图57.1.1.1所示:   图57.1.1.1 ENC28J60典型应用电路   ENC28J60 由七个主要功能模块组成: 1)  SPI 接口,充当主控制器和ENC28J60 之间通信通道。 2)  控制寄存器,用于控制和监视ENC28J60。 3)  双端口RAM缓冲器,用于接收和发送数据包。 4)  判优器,当DMA、发送和接收模块发出请求时对RAM 缓冲器的访问进行控制。 5)  总线接口,对通过SPI 接收的数据和命令进行解析。 6)  MAC(Medium Access Control)模块,实现符合IEEE 802.3 标准的MAC 逻辑。 7)  PHY(物理层)模块,对双绞线上的模拟数据进行编码和译码。 ENC28J60还包括其他支持模块,诸如振荡器、片内稳压器、电平变换器(提供可以接受5V 电压的I/O 引脚)和系统控制逻辑。       ENC28J60的功能框图如图57.1.1.2所示:     图57.1.1.2 ENC28J60功能框图        ALIENTEK ENC28J60网络模块采用ENC28J60作为主芯片,单芯片即可实现以太网接入,利用该模块,基本上只要是个单片机,就可以实现以太网连接。ALIENTEK ENC28J60网络模块原理图如图57.1.1.3所示:     图57.1.1.3 ALIENTEK ENC28J60网络模块原理图        ALIENTEK ENC28J60网络模块外观图如图57.1.1.4所示:   图57.1.1.4 ALIENTEK ENC28J60网络模块外观图          该模块通过一个8个引脚的排针与外部电路连接,这8个引脚分别是:GND、RST、MISO、SCK、MOSI、INT、CS和V3.3。其中GND和V3.3用于给模块供电,MISO/MOSI/SCK用于SPI通信,CS是片选信号,INT为中断输出引脚,RST为模块复位信号。
  • 热度 32
    2013-5-1 23:00
    3140 次阅读|
    0 个评论
    第五十九章 UCOSII实验2-信号量和邮箱      上一章,我们学习了如何使用UCOSII,学习了UCOSII的任务调度,但是并没有用到任务间的同步与通信,本章我们将学习两个最基本的任务间通讯方式:信号量和邮箱。本章分为如下几个部分: 59.1 UCOSII信号量和邮箱简介 59.2 硬件设计 59.3 软件设计 59.4 下载验证  59.1 UCOSII信号量和邮箱简介 系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不致导致灾难性的后果。 例如,任务A和任务B共享一台打印机,如果系统已经把打印机分配给了任务A,则任务B因不能获得打印机的使用权而应该处于等待状态,只有当任务A把打印机释放后,系统才能唤醒任务B使其获得打印机的使用权。如果这两个任务不这样做,那么会造成极大的混乱 。 任务间的同步依赖于任务间的通信。在UCOSII中,是使用信号量、邮箱(消息邮箱)和消息队列这些被称作事件的中间环节来实现任务之间的通信的。本章,我们仅介绍信号量和邮箱,消息队列将会在下一章介绍。 事件        两个任务通过事件进行通讯的示意图如图59.1.1所示:   图59.1.1 两个任务使用事件进行通信的示意图 在图59.1.1中任务1是发信方,任务2是收信方。任务1负责把信息发送到事件上,这项操作叫做发送事件。任务2通过读取事件操作对事件进行查询:如果有信息则读取,否则等待。读事件操作叫做请求事件。 为了把描述事件的数据结构统一起来,UCOSII使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱(消息邮箱)和消息队列这些事件。事件控制块中包含包括等待任务表在内的所有有关事件的数据,事件控制块结构体定义如下: typedef struct {    INT8U  OSEventType;                 //事件的类型    INT16U OSEventCnt;                   //信号量计数器    void *OSEventPtr;                        //消息或消息队列的指针    INT8U  OSEventGrp;                  //等待事件的任务组    INT8U OSEventTbl ;//任务等待表 #if OS_EVENT_NAME_EN 0u     INT8U   *OSEventName;          //事件名 #endif } OS_EVENT; 信号量 信号量是一类事件。使用信号量的最初目的,是为了给共享资源设立一个标志,该标志表示该共享资源的占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。 信号量可以分为两种:一种是二值型信号量,另外一种是N值信号量。 二值型信号量好比家里的座机,任何时候,只能有一个人占用。而N值信号量,则好比公共电话亭,可以同时有多个人(N个)使用。 UCOSII将二值型信号量称之为也叫互斥型信号量,将N值信号量称之为计数型信号量,也就是普通的信号量。本章,我们介绍的是普通信号量,互斥型信号量的介绍,请参考《嵌入式实时操作系统UCOSII原理及应用》5.4节。 接下来我们看看在UCOSII中,与信号量相关的几个函数(未全部列出,下同)。 1)  创建信号量函数 在使用信号量之前,我们必须用函数OSSemCreate来创建一个信号量,该函数的原型为:OS_EVENT *OSSemCreate (INT16U cnt)。该函数返回值为已创建的信号量的指针,而参数cnt则是信号量计数器(OSEventCnt)的初始值。 2)  请求信号量函数 任务通过调用函数OSSemPend请求信号量,该函数原型如下:void OSSemPend ( OS_EVENT *pevent, INT16U timeout, INT8U *err)。其中,参数pevent是被请求信号量的指针,timeout为等待时限,err为错误信息。 为防止任务因得不到信号量而处于长期的等待状态,函数OSSemPend允许用参数timeout设置一个等待时间的限制,当任务等待的时间超过timeout时可以结束等待状态而 进入就绪状态。如果参数timeout被设置为0,则表明任务的等待时间为无限长。 3)  发送信号量函数 任务获得信号量,并在访问共享资源结束以后,必须要释放信号量,释放信号量也叫做发送信号量,发送信号通过OSSemPost函数实现 。OSSemPost 函数在对信号量的计数器操作之前,首先要检查是否还有等待该信号量的任务。如果没有,就把信号量计数器OSEventCnt加一;如果有,则调用调度器OS_Sched( )去运行等待任务中优先级别最高的任务。函数OSSemPost的原型为:INT8U OSSemPost(OS_EVENT *pevent)。其中,pevent为信号量指针,该函数在调用成功后,返回值为OS_ON_ERR,否则会根据具体错误返回OS_ERR_EVENT_TYPE、OS_SEM_OVF。 4)  删除信号量函数 应用程序如果不需要某个信号量了,那么可以调用函数OSSemDel来删除该信号量,该函数的原型为:OS_EVENT *OSSemDel (OS_EVENT *pevent,INT8U opt, INT8U *err)。其中,pevent为要删除的信号量指针,opt为删除条件选项,err为错误信息。 邮箱 在多任务操作系统中,常常需要在任务与任务之间通过传递一个数据(这种数据叫做“消息”)的方式来进行通信。为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区称之为消息缓冲区,这样在任务间传递数据(消息)的最简单办法就是传递消息缓冲区的指针。我们把用来传递消息缓冲区指针的数据结构叫做邮箱(消息邮箱)。 在UCOSII中,我们通过事件控制块的OSEventPrt来传递消息缓冲区指针,同时使事件控制块的成员OSEventType为常数OS_EVENT_TYPE_MBOX,则该事件控制块就叫做消息邮箱。 接下来我们看看在UCOSII中,与消息邮箱相关的几个函数。 1)  创建邮箱函数 创建邮箱通过函数OSMboxCreate实现,该函数原型为:OS_EVENT *OSMboxCreate (void *msg)。函数中的参数msg为消息的指针,函数的返回值为消息邮箱的指针。 调用函数OSMboxCreate需先定义msg的初始值。在一般的情况下,这个初始值为NULL;但也可以事先定义一个邮箱,然后把这个邮箱的指针作为参数传递到函数OSMboxCreate 中,使之一开始就指向一个邮箱。 2)  向邮箱发送消息函数 任务可以通过调用函数OSMboxPost 向消息邮箱发送消息,这个函数的原型为:INT8U OSMboxPost (OS_EVENT *pevent,void *msg)。其中pevent为消息邮箱的指针,msg为消息指针。 3)  请求邮箱函数 当一个任务请求邮箱时需要调用函数OSMboxPend,这个函数的主要作用就是查看邮箱指针OSEventPtr是否为NULL,如果不是NULL就把邮箱中的消息指针返回给调用函数的任务,同时用OS_NO_ERR通过函数的参数err通知任务获取消息成功;如果邮箱指针OSEventPtr是NULL,则使任务进入等待状态,并引发一次任务调度。 函数OSMboxPend的原型为:void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)。其中pevent为请求邮箱指针,timeout为等待时限,err为错误信息。 4)  查询邮箱状态函数 任务可以通过调用函数OSMboxQuery查询邮箱的当前状态。该函数原型为:INT8U OSMboxQuery(OS_EVENT *pevent,OS_MBOX_DATA *pdata)。其中pevent为消息邮箱指针,pdata为存放邮箱信息的结构。 5)  删除邮箱函数 在邮箱不再使用的时候,我们可以通过调用函数OSMboxDel来删除一个邮箱,该函数原型为:OS_EVENT *OSMboxDel(OS_EVENT *pevent,INT8U opt,INT8U *err)。其中pevent为消息邮箱指针,opt为删除选项,err为错误信息。   关于UCOSII信号量和邮箱的介绍,就到这里。更详细的介绍,请参考《嵌入式实时操作系统UCOSII原理及应用》第五章。 59.2 硬件设计 本节实验功能简介:本章我们在UCOSII里面创建6个任务:开始任务、LED任务、触摸屏任务、蜂鸣器任务、按键扫描任务和主任务,开始任务用于创建信号量、创建邮箱、初始化统计任务以及其他任务的创建,之后挂起;LED任务用于DS0控制,提示程序运行状况;蜂鸣器任务用于测试信号量,是请求信号量函数,每得到一个信号量,蜂鸣器就叫一次;触摸屏任务用于在屏幕上画图,可以用于测试CPU使用率;按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;主任务则通过查询消息邮箱获得键值,并根据键值执行DS1控制、信号量发送(蜂鸣器控制)、触摸区域清屏和触摸屏校准等控制。 所要用到的硬件资源如下: 1)  指示灯DS0 、DS1  2)  4个按键(KEY0/KEY1/KEY2/WK_UP) 3)  蜂鸣器 4)  TFTLCD模块 这些,我们在前面的学习中都已经介绍过了。 59.3 软件设计 本章,我们在第三十一章实验 (实验26 )的基础上修改。首先,是UCOSII代码的添加,具体方法同上一章一模一样,本章就不再详细介绍了。不过,本章我们将OS_TICKS_PER_SEC设置为500,即UCOSII的时钟节拍为2ms。 在加入UCOSII代码后,我们只需要修改test.c函数了,打开test.c,输入如下代码: /////////////////////////UCOSII任务设置/////////////////////////////////// //START 任务 #define START_TASK_PRIO                        10           //设置任务优先级 #define START_STK_SIZE                             64           //设置任务堆栈大小 OS_STK START_TASK_STK ;         //任务堆栈     void start_task(void *pdata);                                            //任务函数                        //LED任务 #define LED_TASK_PRIO                          7            //设置任务优先级 #define LED_STK_SIZE                              64           //设置任务堆栈大小 OS_STK LED_TASK_STK ;                //任务堆栈 void led_task(void *pdata);                                             //任务函数   //触摸屏任务 #define TOUCH_TASK_PRIO                           6            //设置任务优先级 #define TOUCH_STK_SIZE                            64           //设置任务堆栈大小 OS_STK TOUCH_TASK_STK ;       //任务堆栈     void touch_task(void *pdata);                                         //任务函数     //篇幅所限,省略部分代码。 该部分代码我们创建了6个任务:start_task、led_task、beep_task、touch_task、main_task和key_task,优先级分别是10和7~3,堆栈大小除了main_task是128,其他都是64。 该程序的运行流程就比上一章复杂了一些,我们创建了消息邮箱msg_key,用于按键任务和主任务之间的数据传输(传递键值),另外创建了信号量sem_beep,用于蜂鸣器任务和主任务之间的通信。 本代码中,我们使用了UCOSII提供的CPU统计任务,通过OSStatInit初始化CPU统计任务,然后在主任务中显示CPU使用率。 另外,在主任务中,我们用到了任务的挂起和恢复函数,在执行触摸屏校准的时候,我们必须先将触摸屏任务挂起,待校准完成之后,再恢复触摸屏任务。这是因为触摸屏校准和触摸屏任务都用到了触摸屏和TFTLCD,而这两个东西是不支持多个任务占用的,所以必须采用独占的方式使用,否则可能导致数据错乱。 软件设计部分就为大家介绍到这里。 59.4 下载验证 在代码编译成功之后,我们通过下载代码到战舰STM32开发板上,可以看到LCD显示界面如图59.4.1所示:   图59.4.1 初始界面        从图中可以看出,默认状态下,CPU使用率仅为1%。此时通过在触摸区域画图,可以看到CPU使用率飙升(42%),说明触摸屏任务是一个很占CPU的任务;通过按KEY0,可以控制DS1的亮灭;通过按KEY1则可以控制蜂鸣器的发声(连续按下多次后,可以看到蜂鸣每隔1秒叫一次),同时,可以在LCD上面看到信号量的当前值;通过按KEY2,可以清除触摸屏的输入;通过按WK_UP可以进入校准程序,进行触摸屏校准。  
  • 热度 28
    2013-4-30 10:55
    5086 次阅读|
    1 个评论
      第五十八章 UCOSII实验1-任务调度      前面我们所有的例程都是跑的裸机程序(裸奔),从本章开始,我们将分3个章节向大家介绍UCOSII(实时多任务操作系统内核)的使用。本章,我们将向大家介绍UCOSII最基本也是最重要的应用:任务调度。本章分为如下几个部分: 58.1 UCOSII简介 58.2 硬件设计 58.3 软件设计 58.4 下载验证 58.1 UCOSII简介 UCOSII的前身是UCOS,最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5月和6月刊上刊登的文章连载,并把UCOS 的源码发布在该杂志的BBS 上。目前最新的版本:UCOSIII已经出来,但是现在使用最为广泛的还是UCOSII,本章我们主要针对UCOSII进行介绍。 UCOSII是一个可以基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。为了提供最好的移植性能,UCOSII最大程度上使用ANSI C语言进行开发,并且已经移植到近40多种处理器体系上,涵盖了从8位到64位各种CPU(包括DSP)。 UCOSII是专门为计算机的嵌入式应用设计的, 绝大部分代码是用C语言编写的。CPU 硬件相关部分是用汇编语言编写的、总量约200行的汇编语言部分被压缩到最低限度,为的是便于移植到任何一种其它的CPU 上。用户只要有标准的ANSI 的C交叉编译器,有汇编器、连接器等软件工具,就可以将UCOSII嵌人到开发的产品中。UCOSII具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点, 最小内核可编译至 2KB 。UCOSII已经移植到了几乎所有知名的CPU 上。        UCOSII构思巧妙。结构简洁精练,可读性强,同时又具备了实时操作系统的全部功能,虽然它只是一个内核,但非常适合初次接触嵌入式实时操作系统的朋友,可以说是麻雀虽小,五脏俱全。UCOSII(V2.91版本)体系结构如图58.1.1所示:     图58.1.1 UCOSII体系结构图        注意本章我们使用的是UCOSII的最新版本:V2.91版本,该版本UCOSII比早期的UCOSII(如V2.52)多了很多功能(比如多了软件定时器,支持任务数最大达到255个等),而且修正了很多已知BUG。不过,有两个文件:os_dbg_r.c和os_dbg.c,我们没有在上图列出,也不将其加入到我们的工程中,这两个主要用于对UCOS内核进行调试支持,比较少用到。        从上图可以看出,UCOSII的移植,我们只需要修改:os_cpu.h、os_cpu_a.asm和os_cpu.c等三个文件即可,其中:os_cpu.h,进行数据类型的定义,以及处理器相关代码和几个函数原型;os_cpu_a.asm,是移植过程中需要汇编完成的一些函数,主要就是任务切换函数;os_cpu.c,定义一些用户HOOK函数。        图中定时器的作用是为UCOSII提供系统时钟节拍,实现任务切换和任务延时等功能。这个时钟节拍由OS_TICKS_PER_SEC(在os_cfg.h中定义)设置,一般我们设置UCOSII的系统时钟节拍为1ms~100ms,具体根据你所用处理器和使用需要来设置。本章,我们利用STM32的SYSTICK定时器来提供UCOSII时钟节拍。 关于UCOSII在STM32的详细移植,请参考光盘资料(《UCOSII在STM32的移植详解.pdf》),这里我们就不详细介绍了。 UCOSII早期版本只支持64个任务,但是从2.80版本开始,支持任务数提高到255个,不过对我们来说一般64个任务都是足够多了,一般很难用到这么多个任务。UCOSII保留了最高4个优先级和最低4个优先级的总共8个任务,用于拓展使用,单实际上,UCOSII一般只占用了最低2个优先级,分别用于空闲任务(倒数第一)和统计任务(倒数第二),所以剩下给我们使用的任务最多可达255-2=253个(V2.91)。 所谓的任务,其实就是一个死循环函数,该函数实现一定的功能,一个工程可以有很多这样的任务(最多255个),UCOSII对这些任务进行调度管理,让这些任务可以并发工作(注意不是同时工作!!,并发只是各任务轮流占用CPU,而不是同时占用,任何时候还是只有1个任务能够占用CPU),这就是UCOSII最基本的功能。 前面我们学习的所有实验,都是一个大任务(死循环),这样,有些事情就比较不好处理,比如:MP3实验,在MP3播放的时候,我们还希望显示歌词,如果是1个死循环(一个任务),那么很可能在显示歌词的时候,MP3声音出现停顿(尤其是高码率的时候),这主要是歌词显示占用太长时间,导致VS1053由于不能及时得到数据而停顿。而如果用UCOSII来处理,那么我们可以分2个任务,MP3播放一个任务(优先级高),歌词显示一个任务(优先级低)。这样,由于MP3任务的优先级高于歌词显示任务,MP3任务可以打断歌词显示任务,从而及时给VS1053提供数据,保证音频不断,而显示歌词又能顺利进行。这就是UCOSII带来的好处。 UCOSII的任何任务都是通过一个叫任务控制块(TCB)的东西来控制的,每个任务管理块有3个最重要的参数:1,任务函数指针;2,任务堆栈指针;3,任务优先级;任务控制块就是任务在系统里面的身份证(UCOSII通过优先级识别任务),任务控制块我们就不再详细介绍了,详细介绍请参考任哲老师的《嵌入式实时操作系统UCOSII原理及应用》一书第二章。 在UCOSII中,使用CPU的时候,优先级高(数值小)的任务比优先级低的任务具有优先使用权,即任务就绪表中总是优先级最高的任务获得CPU使用权,只有高优先级的任务让出CPU使用权(比如延时)时,低优先级的任务才能获得CPU使用权。UCOSII不支持多个任务优先级相同,也就是每个任务的优先级必须不一样。 任务的调度其实就是CPU运行环境的切换,即:PC指针、SP指针和寄存器组等内容的存取过程,关于任务调度的详细介绍,请参考《嵌入式实时操作系统UCOSII原理及应用》一书第三章相关内容。 UCOSII的每个任务都是一个死循环。每个任务都处在以下 5种状态之一的状态下,这5种状态是:睡眠状态、 就绪状态、 运行状态、 等待状态(等待某一事件发生)和中断服务状态。 睡眠状态,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态。 就绪状态,系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,任务已经准备好了,但由于该任务的优先级比正在运行的任务的优先级低, 还暂时不能运行,这时任务的状态叫做就绪状态。   运行状态,该任务获得CPU使用权,并正在运行中,此时的任务状态叫做运行状态。 等待状态,正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给别的任务而使任务进入等待状态。 中断服务状态,一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序,这时任务的状态叫做中断服务状态。 UCOSII任务的5个状态转换关系如图58.1.2所示:     图58.1.2 UCOSII任务状态转换关系 接下来,我们看看在UCOSII中,与任务相关的几个函数: 1)  建立任务函数 如果想让UCOSII管理用户的任务,必须先建立任务。UCOSII提供了我们2个建立任务的函数:OSTaskCreat和OSTaskCreatExt,我们一般用OSTaskCreat函数来创建任务,该函数原型为:OSTaskCreate(void(*task)(void*pd),void*pdata,OS_STK*ptos,INTU prio)。该函数包括4个参数:task:是指向任务代码的指针;pdata:是任务开始执行时,传递给任务的参数的指针;ptos:是分配给任务的堆栈的栈顶指针;prio是分配给任务的优先级。 每个任务都有自己的堆栈,堆栈必须申明为OS_STK类型,并且由连续的内存空间组成。可以静态分配堆栈空间,也可以动态分配堆栈空间。 OSTaskCreatExt也可以用来创建任务,详细介绍请参考《嵌入式实时操作系统UCOSII原理及应用》3.5.2节。 2)  任务删除函数 所谓的任务删除,其实就是把任务置于睡眠状态,并不是把任务代码给删除了。UCOSII提供的任务删除函数原型为:INT8U OSTaskDel(INT8U prio),其中参数prio就是我们要删除的任务的优先级,可见该函数是通过任务优先级来实现任务删除的。 特别注意:任务不能随便删除,必须在确保被删除任务的资源被释放的前提下才能删除! 3)  请求任务删除函数 前面提到,必须确保被删除任务的资源被释放的前提下才能将其删除,所以我们通过向被删除任务发送删除请求,来实现任务释放自身占用资源后再删除。UCOSII提供的请求删除任务函数原型为:INT8U OSTaskDelReq(INT8U prio),同样还是通过优先级来确定被请求删除任务。 4)  改变任务的优先级函数 UCOSII在建立任务时,会分配给任务一个优先级,但是这个优先级并不是一成不变的,而是可以通过调用UCOSII提供的函数修改。UCOSII提供的任务优先级修改函数原型为:INT8U OSTaskChangePrio(INT8U oldprio,INT8U newprio)。 5)  任务挂起函数 任务挂起和任务删除有点类似,但是又有区别,任务挂起只是将被挂起任务的就绪标志删除,并做任务挂起记录,并没有将任务控制块任务控制块链表里面删除,也不需要释放其资源,而任务删除则必须先释放被删除任务的资源,并将被删除任务的任务控制块也给删了。被挂起的任务,在恢复(解挂)后可以继续运行。UCOSII提供的任务挂起函数原型为:INT8U OSTaskSuspend(INT8U prio)。 6)  任务恢复函数 有任务挂起函数,就有任务恢复函数,通过该函数将被挂起的任务恢复,让调度器能够重新调度该函数。UCOSII提供的任务恢复函数原型为:INT8U OSTaskResume(INT8U prio)。 UCOSII与任务相关的函数我们就介绍这么多。最后,我们来看看在STM32上面运行UCOSII的步骤: 1)  移植UCOSII 要想UCOSII在STM32正常运行,当然首先是需要移植UCOSII,这部分我们已经为大家做好了(参考光盘源码,想自己移植的,请参考光盘UCOSII资料)。 这里我们要特别注意一个地方,ALIENTEK提供的SYSTEM文件夹里面的系统函数直接支持UCOSII,只需要在sys.h文件里面将:SYSTEM_SUPPORT_UCOS宏定义改为1,即可通过delay_init函数初始化UCOSII的系统时钟节拍,为UCOSII提供时钟节拍。 2)  编写任务函数并设置其堆栈大小和优先级等参数。 编写任务函数,以便UCOSII调用。 设置函数堆栈大小,这个需要根据函数的需求来设置,如果任务函数的局部变量多,嵌套层数多,那么相应的堆栈就得大一些,如果堆栈设置小了,很可能出现的结果就是CPU进入HardFault,遇到这种情况,你就必须把堆栈设置大一点了。另外,有些地方还需要注意堆栈字节对齐的问题,如果任务运行出现莫名其妙的错误(比如用到sprintf出错),请考虑是不是字节对齐的问题。 设置任务优先级,这个需要大家根据任务的重要性和实时性设置,记住高优先级的任务有优先使用CPU的权利。 3)  初始化UCOSII ,并在UCOSII 中创建任务 调用OSInit,初始化UCOSII,通过调用OSTaskCreate函数创建我们的任务。 4)  启动UCOSII 调用OSStart,启动UCOSII。 通过以上4个步骤,UCOSII就开始在STM32上面运行了,这里还需要注意我们必须对os_cfg.h进行部分配置,以满足我们自己的需要。 58.2 硬件设计 本节实验功能简介:本章我们在UCOSII里面创建3个任务:开始任务、LED0任务和LED1任务,开始任务用于创建其他(LED0和LED1)任务,之后挂起;LED0任务用于控制DS0的亮灭,DS0每秒钟亮80ms;LED1任务用于控制DS1的亮灭,DS1亮300ms,灭300ms,依次循环。 所要用到的硬件资源如下: 1)  指示灯DS0 、DS1   58.3 软件设计 本章,我们在第六章实验 (实验1 )的基础上修改,在该工程源码下面加入UCOSII文件夹,存放UCOSII源码(我们已经将UCOSII源码分为三个文件夹:CORE、PORT和CONFIG)。 打开工程,新建UCOSII-CORE、UCOSII-PORT和UCOSII-CONFIG三个分组,分别添加UCOSII三个文件夹下的源码,并将这三个文件夹加入头文件包含路径,最后得到工程如图58.3.1所示:     图58.3.1 添加UCOSII源码后的工程 UCOSII-CORE分组下面是UCOSII的核心源码,我们不需要做任何变动。 UCOSII-PORT分组下面是我们移植UCOSII要修改的3个代码,这个在移植的时候完成。 UCOSII-CONFIG分组下面是UCOSII的配置部分,主要由用户根据自己的需要对UCOSII进行裁剪或其他设置。 本章,我们对os_cfg.h里面定义OS_TICKS_PER_SEC的值为200,也就是设置UCOSII的时钟节拍为5ms,同时设置OS_MAX_TASKS为10,也就是最多10个任务(包括空闲任务和统计任务在内),其他配置我们就不详细介绍了,请参考本实验源码。 前面提到,我们需要在sys.h里面设置SYSTEM_SUPPORT_UCOS为1,以支持UCOSII,通过这个设置,我们不仅可以实现利用delay_init来初始化SYSTICK,产生UCOSII的系统时钟节拍,还可以让delay_us和delay_ms函数在UCOSII下能够正常使用(实现原理请参考5.1节),这使得我们之前的代码,可以十分方便的移植到UCOSII下。虽然UCOSII也提供了延时函数:OSTimeDly和OSTimeDLyHMSM,但是这两个函数的最少延时单位只能是1个UCOSII时钟节拍,在本章,即5ms,显然不能实现us级的延时,而us级的延时在很多时候非常有用:比如IIC模拟时序,DS18B20等单总线器件操作等。而通过我们提供的delay_us和delay_ms,则可以方便的提供us和ms的延时服务,这比UCOSII本身提供的延时函数更好用。 在设置SYSTEM_SUPPORT_UCOS为1之后,UCOSII的时钟节拍由SYSTICK的中断服务函数提供,该部分代码如下: //systick中断服务函数,使用ucos时用到 void SysTick_Handler(void) {                                    OSIntEnter();               //进入中断     OSTimeTick();       //调用ucos的时钟服务程序                   OSIntExit();           //触发任务切换软中断 } 以上代码,其中OSIntEnter是进入中断服务函数,用来记录中断嵌套层数(OSIntNesting增加1);OSTimeTick是系统时钟节拍服务函数,在每个时钟节拍了解每个任务的延时状态,使已经到达延时时限的非挂起任务进入就绪状态;OSIntExit是退出中断服务函数,该函数可能触发一次任务切换(当OSIntNesting==0调度器未上锁就绪表最高优先级任务!=被中断的任务优先级时),否则继续返回原来的任务执行代码(如果OSIntNesting不为0,则减1)。 事实上,任何中断服务函数,我们都应该加上OSIntEnter和OSIntExit函数,这是因为UCOSII是一个可剥夺型的内核,中断服务子程序运行之后,系统会根据情况进行一次任务调度去运行优先级别最高的就绪任务,而并不一定接着运行被中断的任务! 最后,我们打开test.c,输入如下代码: /////////////////////////UCOSII任务设置/////////////////////////////////// //START 任务 #define START_TASK_PRIO                        10          //设置任务优先级 #define START_STK_SIZE                             64           //设置任务堆栈大小 OS_STK START_TASK_STK ;         //任务堆栈     void start_task(void *pdata);                                            //任务函数                        //LED0任务 #define LED0_TASK_PRIO                         7            //设置任务优先级 #define LED0_STK_SIZE                            64           //设置任务堆栈大小 OS_STK LED0_TASK_STK ;             //任务堆栈     void led0_task(void *pdata);                                                  //任务函数   //LED1任务 #define LED1_TASK_PRIO                         6            //设置任务优先级 #define LED1_STK_SIZE                               64           //设置任务堆栈大小 OS_STK LED1_TASK_STK ;             //任务堆栈     void led1_task(void *pdata);                                                  //任务函数 ////////////////////////////////////////////////////////////////////////////// int main(void) {                                  Stm32_Clock_Init(9); //系统时钟设置        delay_init(72);            //延时初始化              LED_Init();                         LED_Init();                  //初始化与LED连接的硬件接口        OSInit();         OSTaskCreate(start_task,(void *)0,(OS_STK *)START_TASK_STK ,START_TASK_PRIO );//创建起始任务        OSStart();              }       //开始任务 void start_task(void *pdata) {     OS_CPU_SR cpu_sr=0;        pdata = pdata;       OS_ENTER_CRITICAL();                  //进入临界区(无法被中断打断)          OSTaskCreate(led0_task,(void *)0,(OS_STK*)LED0_TASK_STK , LED0_TASK_PRIO);                                                OSTaskCreate(led1_task,(void *)0,(OS_STK*)LED1_TASK_STK , LED1_TASK_PRIO);                                         OSTaskSuspend(START_TASK_PRIO);       //挂起起始任务.        OS_EXIT_CRITICAL();                            //退出临界区(可以被中断打断) } //LED0任务 void led0_task(void *pdata) {                 while(1)        {               LED0=0; delay_ms(80);               LED0=1; delay_ms(920);        }; }   //LED1任务 void led1_task(void *pdata) {              while(1)        {               LED1=0; delay_ms(300);               LED1=1; delay_ms(300);        }; } 该部分代码我们创建了3个任务:start_task、led0_task和led1_task,优先级分别是10、7和6,堆栈大小都是64(注意OS_STK为32位数据)。我们在main函数只创建了start_task一个任务,然后在start_task再创建另外两个任务,在创建之后将自身(start_task)挂起。这里,我们单独创建start_task,是为了提供一个单一任务,实现应用程序开始运行之前的准备工作(比如:外设初始化、创建信号量、创建邮箱、创建消息队列、创建信号量集、创建任务、初始化统计任务等等)。 在应用程序中经常有一些代码段必须不受任何干扰地连续运行,这样的代码段叫做临界段(或临界区)。因此,为了使临界段在运行时不受中断所打断,在临界段代码前必须用关中断指令使CPU屏蔽中断请求,而在临界段代码后必须用开中断指令解除屏蔽使得CPU可以响应中断请求。UCOSII提供OS_ENTER_CRITICAL和OS_EXIT_CRITICAL两个宏来实现,这两个宏需要我们在移植UCOSII的时候实现,本章我们采用方法3(即OS_CRITICAL_METHOD为3)来实现这两个宏。因为临界段代码不能被中断打断,将严重影响系统的实时性,所以临界段代码越短越好! 在start_task任务中,我们在创建led0_task和led1_task的时候,不希望中断打断,故使用了临界区。其他两个任务,就十分简单了,我们就不细说了,注意我们这里使用的延时函数还是delay_ms,而不是直接使用的OSTimeDly。 另外,一个任务里面一般是必须有延时函数的,以释放CPU使用权,否则可能导致低优先级的任务因高优先级的任务不释放CPU使用权而一直无法得到CPU使用权,从而无法运行。 软件设计部分就为大家介绍到这里。 58.4 下载验证 在代码编译成功之后,我们通过下载代码到战舰STM32开发板上,可以看到DS0一秒钟闪一次,而DS1则以固定的频率闪烁,说明两个任务(led0_task和led1_task)都已经正常运行了,符合我们预期的设计。