搜索
bottom↓
回复: 17

请教一下,关于使用rtos设计一个比较大的系统的几个问题

[复制链接]

出0入0汤圆

发表于 2007-6-27 10:31:15 | 显示全部楼层 |阅读模式
我想设计一个比较大的带点阵lcd显示的系统,有很多菜单,界面,大概3、4十个吧,但同时使用的(嵌套的)只有几个。类似手机吧。

1.任务分配问题:我想每个界面或者菜单设置一个任务,这样做是最简单的。但avrx的任务堆栈是静态分配的,这样的话不管这个任务有没有start,都分配了一个堆栈,ram肯定不够。ucos似乎也是静态分配的。即使改源码改成动态分配的,内存碎片问题也不太好处理。如果所有菜单和界面设置一个任务,似乎和前后台的做法差不多了,写起程序来也比较复杂。大家怎么处理的?

2.堆栈问题:我的lcd驱动开了一个临时的比较大的数组,而很多程序(任务)用到这个驱动,那么每个任务的堆栈都需要这么大的空间,我现在的想法是把这个临时数组改成全局的,这样任务堆栈就不需要这么大了,当然这样会有重入问题,不过我的程序似乎不需要重入。但这样在不需要显示的时候,这块空间就浪费了,没办法分配给其他需要大ram的程序使用了。有没有更好的办法?

3.内存碎片问题:这个ucos有个解决办法,就是把ram分成几块,不同的块分配的容量不同,这个很难说清楚,有兴趣的可以看一下ucos的书。这样做的不好的地方就是,有可能修改程序的时候,会造成某一块已分配完了,就要修改块定义。而且好像这里出问题比较难查。



有用rtos写过较大程序的给点建议吧

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2007-6-27 14:26:49 | 显示全部楼层
我也是新手。



个人看法:

1.所有界面和菜单一个任务就行了。定义一个函数指针,根据键盘处理,把实际的显示函数赋值给函数指针,在菜单显示任务中周期调用这个函数指针指向的函数。



2.液晶显示驱动不要用局部数组,用指针。

定义一个液晶显示缓冲数组LCD_BUF[],用一个任务周期显示LCD_BUF[]的内容,其他任务不要直接调用液晶驱动函数,要显示什么内容,放到LCD_BUF[]里就行了。



3.avrx任务堆栈静态分配的,而且中断用栈和任务用栈是分开的,不存在内存碎片问题。

出0入0汤圆

 楼主| 发表于 2007-6-27 15:30:28 | 显示全部楼层
1,这样的话,这个任务那是相当的大,很复杂

2,我说的局部数组不是干这个用的,我用的7565作的显示,为了可以从任意一点上开始显示任意大小图片或其他的和加快显示速度,需要用一行132个字节保存需要移位的数据。字符串地址当然是用指针。

3,静态分配任务栈的缺点就是浪费ram空间,一般的系统同时运行的任务一般只有几个,而大部分任务只有在特定事件发生时才运行,运行完又可以马上把它结束掉。而静态分配不管你的任务有没有运行,都分配ram作为任务栈,其实这些没有运行的任务完全没有必要分配ram。如果使用动态分配任务栈,任务数就只受rom大小限制,而ram大小限制了同时运行的任务数,这样我认为比较合理,所以我想把avrx修改成动态分配任务栈。动态分配有两个缺点,一个是启动任务和结束任务会慢一点,但这个问题不大,启动和停止任务的时候不多。第二就是可能会产生碎片问题,但我想一般的任务都是后进先出,这样的话,问题也不是很大。但这点在编程上要小心,碎片问题可能在长时间运行后才出问题。

另外就是不知道gcc的malloc和free函数是不是可以重入的,如果不可重入,可能要使用信号量或者关中断。

出0入0汤圆

发表于 2007-6-27 16:12:42 | 显示全部楼层
说的好难啊,你还要rtos来实现这么个东西啊。受不了。你的任务都是相关的。不是完全独立的任务啊。液晶显示和主流程倒是分开的任务。

出0入0汤圆

 楼主| 发表于 2007-6-27 17:20:47 | 显示全部楼层
不明白楼上相关的什么意思,比如说我一个界面用来设置lcd亮度,一个界面用来设置系统时间,再有一个菜单可以选择以上两个设置,我想这可以分成三个任务吧。但两个设置任务不可能同时出现,一定是退出其中一个才可能会进入另一个。

广义上讲,一个系统的东西都是相关的,不相关的放到一起似乎很少。

我想用os的目的,一个就是增强实时性,再就是简化编程,把一个大的系统分成若干个任务,每个任务很小,写每个任务的时候可以当作自己是都占整个mcu的,就很好实现。

出0入0汤圆

 楼主| 发表于 2007-6-27 17:26:24 | 显示全部楼层
粗粗研读了一下avrx的源码,还是有些小bug的。例如avrx.h文件中

INTERFACE void AvrXRunTask(TaskControlBlock *);

INTERFACE unsigned char AvrXInitTask(TaskControlBlock *);

这两个函数声明其实应该是

INTERFACE pProcessID AvrXRunTask(TaskControlBlock *);

INTERFACE pProcessID AvrXInitTask(TaskControlBlock *);

他们都是返回pProcessID类型的,这个返回值在很多函数的参数中会用到,本来我以为只能用AvrXSelf函数取得这个指针。其实没必要。

出0入0汤圆

发表于 2007-6-27 20:11:15 | 显示全部楼层
但两个设置任务不可能同时出现,一定是退出其中一个才可能会进入另一个。



那就合成一个任务。







这是我写的一个基于AVRX电子菜单的实验,

(单端ADC转换,双端ADC转换,频率计,PWM,歌曲演奏)还没有完全完成。

共5个任务。硬件是马老师AVR实验板,LCD是OCMJ122X32。



1.KEY                 键盘扫描任务

2.COMMAND         根据系统状态和按键控制整个系统

3.LCD_SHOW        周期显示LCD_BUF内容

4.TASK_SHOW        根据系统状态把显示内容放到LCD_BUF

5.TASK_EXE        根据系统状态执行各功能模块               





因为我的程序里各功能模块(单端ADC转换,双端ADC转换,频率计,PWM,歌曲演奏)不会并行执行,所以用一个任务实现。



学AVR才两个月,也没有实际项目经验,难免有错误的地方,还请高手指点。





系统控制任务

typedef void(*Command_t)(uint8_t);

AVRX_GCC_TASKDEF(COMMAND,60,1)

{

        uint8 key;

        Command_t cmd;

               

        while(1)

        {       

                GET_KEY(key);

               

                switch(STATE)

                {

                        case 0:

                                cmd=MainMenuCommand;

                                break;



                        case 1:

                                cmd=ADCCommand;

                                break;



                        case 2:

                                cmd=CapCommand;

                                break;



                        case 3:

                                cmd=PWMCommand;

                                break;



                        case 4:

                                cmd=ADC2Command;

                                break;



                        default:

                                cmd=MainCommand;

                                break;



                        }



                        cmd(key);//cmd指向的函数是非阻塞函数



        }

}









系统显示任务

typedef void (*Show_t)(void);

AVRX_GCC_TASKDEF(TASK_SHOW,120,5)

{



Show_t show;



        while(1)

        {

                switch(STATE)

                {

                case 0:

                        show=MainMenuShow;

                        break;



                case 1:

               

                        show=ADCShow;

                        break;



                case 2:

                        show=CapShow;

                        break;



                case 3:

                        show=PWMShow;

                        break;



                case 4:

                        show=ADC2Show;

                        break;

               

                default:

                        show=MainMenuShow;

                        break;

                }



        show(); //show指向的函数是非阻塞函数

        AvrXDelay(&TASKSHOW_TIMER,OS_TICKS_PER_SEC/10);

        }       



}











系统执行任务



void QUIT(void)

{

AvrXTaskExit();

}



typedef void (*Task_t)(void);

AVRX_GCC_TASKDEF(TASK_EXE,120,4)

{

Task_t taskexe;



        switch(STATE)

        {



        case 0:

                taskexe=QUIT;

                break;



        case 1:               

                taskexe=adc;

                break;



        case 2:

                taskexe=capture;

                break;



        case 3:

                taskexe=pwm;

                break;



        case 4:

                taskexe=adc2;

                break;

               



        default:

                taskexe=QUIT;

                break;

        }



        taskexe(); //taskexe指向的函数是一个死循环或者是删除自己

       

        while(1);//while(1)防止编译器警告

}

出0入0汤圆

发表于 2007-6-27 22:20:31 | 显示全部楼层
用操作系统的,不妨用用信号量。一个任务占用了该信号量,另一个任务在这个任务还没有释放该信号量时不能访问这个任务所使用的资源。

出0入0汤圆

 楼主| 发表于 2007-6-28 08:43:54 | 显示全部楼层
当然可以合成一个任务,我以前没有研究过rtos,甚至从在网上看到avr芯片,到现在也不超过3个星期,以前写的系统所有的程序(除了中断)都是一个任务。但我看了rtos以后,假如说资源够用的话,我觉得还是分开几个任务比较好,比如说你的几个功能可以说完全不相干,就是我以前的写法,我也会把它们写在不同的文件里,这样条理比较清晰。任务分开后,从编程的角度说优点很多,至少程序清晰明了,修改程序不会误改其它部分等等。但对于avr这类mcu,因为资源紧张,必须把一些功能合成一个任务,我想这应该是不得已才这样做的。

所以我想修改avrx,使它能够在任务运行的时候才开辟ram用作任务栈。这样的话就可以增加很多任务了,只要保证同时运行的任务不太多就可以了。

动态开辟内存我仔细想了一下还是不用malloc free来做,自己在开始的时候分配一个大的静态空间作堆,自己写分配空间代码在其中分配。比较容易判断是否溢出,代码也比较小,运行也比较快。
-----此内容被dack于2007-06-28,08:55:19编辑过

出0入0汤圆

 楼主| 发表于 2007-6-28 08:54:08 | 显示全部楼层
信号量肯定是需要用的。现在我是初学rtos,所以对如何用好rtos不是很清楚。现在网上一般的书或者讨论中的例子都是非常简单的,甚至说那些例子不用rtos可以做的更小、更快。完全是为了用rtos而用rtos,而不是因为需要用rtos才用rtos。我发现在这里高手很多,所以想讨论一下,大系统中rtos如何用的更好的问题。

我提出一些我的想法,当然可能是错误甚至谬误的,请大家指正。大家也说说自己的想法和经验。

出0入0汤圆

 楼主| 发表于 2007-6-28 09:36:20 | 显示全部楼层
记录一下我这三个星期做过的事情。

1.上网看到avr芯片,感觉功能比较强。找到本论坛,看帖子,学习。

2.阿莫处订了几个芯片、晶振、万用板、插头座等等花了116块。

3.找到pongprog网站,做了一个串口下载线。

4.找到isojtagisp网站,做了一个jtag和isp两用的小盒子,usb口的。省了光藕,改了usb转串口芯片。

5.做了一个最小系统板,只有复位键、晶振插座、jtag和isp插座,把从阿莫处买的10×10万用板用完了

6.拿公司的st7565 128×64lcd样片做了一个lcd模块,插在万用板上。

7.研究了中文版的atmega32的datasheet。

8.论坛里找c编译器,试用了iccavr,gcc,决定使用gcc。主要是因为免费,呵呵。

9.研究makefile的规则。

10.用c写了一个lcd显示驱动,显示正常。

11.用以前的办法想写一个事件驱动和任务管理的模块,写完后感觉不太好。

12.无聊,不想写了,上网,发现有rtos功能似乎很强。

13.网上搜索,似乎uc/os2很不错,下了一本书研究一下,对rtos有了基本概念。但uc/os2太大,决定还是用回老方法,前后台。

14.发现有人说avrx不错,找资料,太少了。中文的似乎就是完全翻译的官方网站,英文的也很少。但好像可以满足我的需求。这里说一下,本坛有人说avrx最多只有16个任务,那是不对的,avrx的任务数只取决于你的ram,但优先级只有16级,任务可以有相同优先级。

15.官网上下载avrx,没搞明白什么叫SINGLESTEP,不管,去掉这个功能,make,谁知道SINGLESTEP是干什么用的,说说看啊。

16.将其中一个例子程序改改,把以前发串口改成显示lcd,去掉监视任务,加上编译好的库文件avrx.a。

17.编译,咦?找不到库文件,妈的,明明在那里,怎么会找不到。怎么搞都不行。上网,查,哦,要改名,改了名,编译通过。奇怪,谁知道为什么要改名才行啊。是不是库文件一定要lib开头的才行?

18.jtag下载到目标板上,run,哈,正常。

19.想想我的系统怎么才能用好avrx,没有整体概念,上网来问。就是这个帖子的来由。

20.粗看avrx源码,发现一点小bug。

出0入0汤圆

发表于 2007-6-28 10:05:46 | 显示全部楼层
楼主果然是高手。



我学AVR也才两个月。

1.买块马老师AVR实验版

2.熟悉编译器GCC和了解MAKEFILE

3.了解AVR指令和C,汇编混合编程

4.移植small rtos 到AVR

5.在SMALL RTOS上写了5-6个任务的实验,发现实时性非常差。

SMALL RTOS把任务堆栈空间全部分配给当前任务,用时间换空间,堆栈用量过512就明显感到实时性不行,大于1K实时性太差。

6.给SMALL RTOS 加上非系统管理中断

非系统管理中断能用了,但系统管理中断又出现问题,任务中无法控制,放弃SMALL RTOS转向AVRX

7.把small rtos上程序移植到AVRX,实时性非常好。

8.用AVR_STUDIO+gcc自己生成的makefile编译AVRX

不成功,在makefile加上 LIBS=./AVRX/avrx.a编译成功。

在论坛上知道AVR_STUDIO+gcc自己生成的makefile编译AVRX,要改lib名,

不再需要手动改mekefile

9.继续用AVRX做一个基于电子菜单的AVR实验,还未完成。



想用AVRX+马老师AVR实验板做一个有点难度的实验,不过自己自己一点经验都没有,

不知道做什么好。






-----此内容被ATmega32于2007-06-28,10:07:21编辑过

出0入0汤圆

 楼主| 发表于 2007-6-28 17:22:30 | 显示全部楼层
修改avrx任务栈为动态分配改完,不完全是动态分配,初始化时定义了一个大数组作为堆,任务栈在初始化任务时在堆中分配,结束任务时释放任务栈,考虑到内存碎片问题,释放的时候有可能空间不会回收,例如启动三个任务分别是1,2,3。1,2,3的任务栈在堆中的顺序也是1,2,3。但如果这是结束任务2,2的任务栈不会被系统立即回收,这时如果启动任务4,任务4的任务栈将排到3的后面,而不管原先2所用的空间能不能放下任务4的任务栈。一定要等到任务3,4结束时,才统一回收2,3,4的空间。这样有个问题,就是如果两个任务互相启动就会耗尽空间。暂时想不出更好的方法,先这样吧。不过程序写的时候注意点应该问题不大。

出0入0汤圆

发表于 2007-6-28 20:19:27 | 显示全部楼层
对于不能并行任务,使用公用堆栈如何?



假设任务A,B两任务不会并行执行,两任务公用一个堆栈,总共堆栈用量是A,B中叫大的一个。



任务切换的时候,先删除任务,再初始化堆栈,再运行另一个任务。



应该可行。





另外,问一下

AvrXRunTask()包含了对堆栈的初始化吗?

对已经运行的任务再调用AvrXRunTask()会出现什么情况?

对已删除的任务再调用AvrXTerminate()会出现什么情况?










-----此内容被ATmega32于2007-06-28,20:24:06编辑过

出0入0汤圆

 楼主| 发表于 2007-6-29 09:05:19 | 显示全部楼层
共用堆栈是可以的,但不太好改,而且似乎比动态分配没有什么优势,程序写的时候要自己考虑那些可以共用,比较麻烦。



AvrXRunTask()包含了对堆栈的初始化吗?

没有清零的代码

对已经运行的任务再调用AvrXRunTask()会出现什么情况?

原版的应该会重新启动这个任务,但我还没看到系统几个队列的代码,不知道会不会影响队列,我想没有人会这么用吧。

对已删除的任务再调用AvrXTerminate()会出现什么情况?

同上

出0入0汤圆

 楼主| 发表于 2007-6-29 14:38:28 | 显示全部楼层
似乎又发现了一个avrx的bug,AvrXWaitTask()这个函数似乎不起作用,谁测试一下。

出0入0汤圆

发表于 2008-4-22 17:08:59 | 显示全部楼层
楼主的情况可以用GUI了。

出0入0汤圆

发表于 2008-5-13 14:53:57 | 显示全部楼层
时间触发模式不用为每个任务分配堆栈,每个任务只占几个字节的任务结构,纯C语言,很好用的。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 16:07

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表