搜索
bottom↓
回复: 237

【原创OS】执行效率与裸机无异的实时内核Z1/OS

  [复制链接]

出0入0汤圆

发表于 2010-10-15 13:12:09 | 显示全部楼层 |阅读模式
说执行效率近乎裸机,可能一听这话就有很多人想都不想,看都不看,直接就要拍砖。

编写此内核的起因和探索过程我几天前已经发到坛子上了http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4310923&bbs_page_no=1&bbs_id=1000

整个内核没用利用任何中断,程序执行过程无任何打断,与裸机代码没任何区别,所以,如题绝不夸张。

没有利用定时中断作为任务调度的时钟基准,这个我不敢说后无来者,但应该是前无古人。

几天前写完,经过一定测试,现在发出跟大家分享一下,希望能得到大家的指点。

先给大家介绍下这个Z1/OS内核的亮点功能:

1、任务动态建立、动态删除:比如我们要做一个路由器,对每一台连接的PC都可以是相同的程序,当有PC机链接时,单独建立一个任务处理,当PC机关机,这边还可以动态删除任务,内存利用率极高,对编程也提供了最简便的方法。

2、无定时中断打断:这在各种OS上,绝对是个难题,OS的性能损失主要也是源于中断的时间,而Z1/OS,没利用定时中断,就更不要说损失了。

3、节拍小:每个OS上都会有时钟最小节拍,如果设置大了,响应能力慢,设置小了,性能损失严重。而本内核基于非中断模式,可以把节拍放到非常小,也无需担心性能损失问题。

4、体积小、可裁减:内核功能全开,也仅需要2K左右ROM,74B左右RAM,而且还可根据需要裁剪,最小可致1K左右ROM, 45B左右RAM。

5、最大支持8个优先级,256个以上任务:支持8个优先级,同优先级任务轮渡。

6、可移植性高:不超过80行的内敛汇编,在任何单片机及环境下都极易实现。

7、可调式性:内核内部采用很多容错检测来提高内核安全性,但这样往往造成无用代码过多,效率低下。而Z1/OS,可分两版本编译,
当OS_DEBUG_EN为1时,内核编译为调试版本,进行很多错误检测。当开发完成时,可设置OS_DEBUG_EN为0。即安全又高效。

8、易学习:各种函数、宏、移植文件布局,都采用UCOS、SMAllOS 51等内核通用命名方式,让有OS基础的人,第一时间上手。



先上一个例子,让大家看看其易用性,其效果是让MEGA16、8MHZ上,PA、PB、PC、PD四组口连接四组灯,让其以不同频率、不同图案变换:

#include"Z1OS.h"

int main(void)
{
        OSInit();
        OSTaskCreate(Task0, 70, 77);
        OSTaskCreate(Task1, 70, 78);
        OSTaskCreate(Task2, 70, 79);
        OSTaskCreate(Task3, 70, 80);
        OSStart();
}

其中
OSInit();是系统初始化

OSTaskCreate()第一个参数是指定任务函数名、第二个是任务堆栈大小、第三个是任务ID
Task0、Task1、Task2、Task3是四组图案的函数,每一本身都是一个大循环,就是普通小灯程序代码,不贴了。

OSStart();系统运行。

用过OS的人看着眼熟吧,没用过的也可望名之意。但其中没有让用户自己指定堆栈地址,而是仅仅指定个堆栈大小,是因为此内核是建立在动态任务的基础上的,无需自己静态建立堆栈那么烦琐。

给大家介绍下,Z1/OS目前提供的服务函数介绍吧:

/*
*********************************************************************************************************
* 函数原型:BOOLEAN OSTaskCreate(void(*Task)(void),
*                                                                   INT16U StkSize,
*                                                                   INT8U  TaskId);
* 函数介绍
* 输入参数:void (*Task)(void)
*                 为指向新任务执行函数地址的指针
*        INT16U StkSize
*                为新任务分配的堆栈大小
*        INT8U  TaskId
*                新任务ID
* 输出参数:无
* 返回值  :TRUE        任务建立成功
*         FALSE        任务建立失败(内存不足)
*********************************************************************************************************
*/

当OS_TASK_SUSPEND_RESUME_EN设置为1时可使用
/*
*********************************************************************************************************
* 函数原型:void OSTaskSuspend(INT8U TaskID);
* 函数介绍:将指定ID任务挂起(停止)
* 输入参数:INT8U TaskID
*                                 指定任务ID值
* 输出参数:无
* 返回值  :无
*********************************************************************************************************
*/

当OS_TASK_SUSPEND_RESUME_EN设置为1时可使用
/*
*********************************************************************************************************
* 函数原型:void OSTaskResume(INT8U TaskID);
* 函数介绍:将指定ID的任务就绪,
*                                 即将被暂停或挂起(停止)的任务恢复
* 输入参数:INT8U TaskID
*                                指定任务ID值
* 输出参数:无
* 返回值  :无
*********************************************************************************************************
*/

/*
*********************************************************************************************************
* 函数原型:void OSTimeDly(INT8U Ticks);
* 函数介绍:将当前任务暂停若干节拍
* 输入参数:INT8U Ticks
                                要暂停的节拍数
* 输出参数:无
* 返回值  :无
*********************************************************************************************************
*/

当OS_TIME_DLY_MS_EN为1可使用
特殊说明,此函数采用跟GCC自带_delay_ms一样的方式,借用编译器内部方式实现,虽然参数为FP64,其只是会提高精度,而不会真的浪费内存按此编译。
/*
*********************************************************************************************************
* 函数原型:void OSTimeDlyMS(FP64 Ms);
* 函数介绍:将当前任务暂停指定的毫秒数
* 输入参数:FP64 Ms
*              要暂停的毫秒数
* 输出参数:无
* 返回值  :无
*********************************************************************************************************
*/

当OS_TIME_DLY_HMS_EN为1和OS_TIME_GET_SET_EN为1时,可使用
/*
*********************************************************************************************************
* 函数原型:void OSTimeDlyHMS(INT8U Hours, INT8U Minutes, INT8U Seconds);
* 函数介绍:将当前任务暂停指定的一段时间
* 输入参数:INT8U Hours
                                要暂停的小时数
                        INT8U Minutes
                                要暂停的分钟数
                        INT8U Seconds
                                要暂停的秒数
* 输出参数:无
* 返回值  :无
*********************************************************************************************************
*/


宏OSMalloc(size)   OSFree(*p)实现内存动态申请释放,由于GCC性能卓越,暂时这两函数仅调用GCC自带的malloc、free,
以后我会实现一个轻量级的内存池,来防止内存碎片。

宏OS_ASSERT可以用来检测程序编写时的逻辑错误,本内核本身大量采用了这个宏来进行错误检测。当调试完毕,把OS_DEBUG_EN为0,自然将这些检测取消。应用程序也可以调用此宏来进行调试。





下午还有课,写不完了,把内核传上来,大家可以试一试。

此时基于GCC_AVR、MEGA16、8MHZ的版本,里面有个程序,就是上面说的四组灯,还有Proteus的仿真,大家可以先试试。
上面提到OS_XX_EN都是在os_cfg.h中配置,配置完运行自带的那个MakeFile即可。

上完课我在详说,不明白的也可以发到我邮箱,欢迎大家使用。



——————————————————————————————————————————————————————————————

好的,继续。有网友让说说这个内核的时钟基准和任务调度算法,那就先说说。这个其实就是这内核最重要的部分,要讲清楚,就说说过程。

以往协作式内核和剥夺式内核的时钟基准全部采用定时中断,这性能损失对于高端机不算什么,但对于32MHZ以下的单片机,这些损失是巨大。
拿uCOSII来说,我将它非常小心的精简、改造后移植到了Mega16,但并没有什么使用价值。在Mega128上,将4K RAM全部用掉都无法将uCOSII全部功能展开。
而且uCOSII的建议系统频率是多少?是10HZ - 100HZ!而为了适应我们低性能单片机,我勉强将其调到400HZ,但这是性能损失已经无法估量,其每2ms多点就会被中断打断一回,这太可怕了。

而我们需要的延时最少也要ms级,那是1000HZ!换句话说uCOSII移植到AVR仅仅是为了移植而移植~~并没有太大使用价值(声明下,我并没有攻击uCOSII,而且恰恰相反,我是uCOSII的忠实爱好者,在这里只是就是论事)

而这些类系统为什么需要那么大体积、而且节拍无法太小?并不其作者不愿意小,而是因为那是剥夺式内核,要考虑资源互斥等问题,所以体积再所难免。

剥夺式内核在高端非强实时机上,非常好用。但在8位机上,并不是很合适。

所以我就定了两个目标来写这个Z1/OS内核,就是一是节拍可以无限小,来把性能提高到裸机状态;二是基于协作式内核来弃用互斥信号等机制,这样可以减小体积。

我采用的时钟基准是定时器,但不中断!就是以大分频(防溢出)定时器作为计时,在需要调度时读取定时器作为基准,然后清零!这样每个任务执行时不会受任何影响,仅在主动切换时计算一回。以内8MHz的Mega16来说,理论上可以把系统频率提高到1MHz,当然这仅仅是理论。我在后面给大家下载的文件中调的是2000Hz,也就是最小节拍是0.5毫秒,已经足够用了。经计算及仿真试验,提高到0.1毫秒不成问题。

过于精细的时间,因为是协作式、无资源共享问题,可以跟裸机时一样,放入中断。这样看来,我这第一个目标完成的十分完美,可以说在8位机上已经到了极致。

对于第二个目标,一个不需要邮箱、信号量的内核,本身体积就非常小。而我在调度算法上,为了实现无任务数限制、让用户使用方便、任务动态删除建立,所以采用的是任务链的形式,将很多在8位机不可思议的事情实现了。但这面临一个新问题,怎么保证链表搜索效率?这个首先要先解决动态内存分配问题,我原计划自己实现一个轻量级内存动态管理及内存池。但后来发现,GCC中的自带的内存分配效率已经非常高,所以直接采用了编译内置的函数,以后只要实现个小型内存池就可以减少内存碎片的产生了。而后,我单独为每个任务链定义了一种畸形链表结构,说是畸形,是因为它具有记忆能力,但却只是普通的单链,整个搜索代码不超50行。

明白这些思路,再看源码,就会清晰很多。这是一段插曲,言归正传。

——————————————————————————————————————————————————————————————



我准备整理的全面一点的例子一起发出,让大家很容易使用,稍等一天。Thanks





版权声明:但凡回帖者,即获得学习及个人开发授权

如用作商用产品开发,请联系作者并经同意后使用。邮箱: yrloy@live.cn


完全编译才如此轻量 (原文件名:全功能尺寸.JPG)


例子程序的仿真 (原文件名:例子程序的仿真.JPG)

带一个例子程序,带内核源码,有中文注释ourdev_590081CG5NX4.zip(文件大小:98K) (原文件名:Z1OS.zip)

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

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。

出0入0汤圆

发表于 2010-10-15 13:20:28 | 显示全部楼层
沙发,不用定时器中断的还真没听过,仔细看看。。。

出0入0汤圆

发表于 2010-10-15 13:24:01 | 显示全部楼层
我们先仔细看看!

出0入0汤圆

发表于 2010-10-15 13:28:16 | 显示全部楼层
有兴趣,下载看看。。。

出0入0汤圆

发表于 2010-10-15 13:31:05 | 显示全部楼层
学习了

出0入4汤圆

发表于 2010-10-15 13:32:46 | 显示全部楼层
make

出0入0汤圆

发表于 2010-10-15 13:39:49 | 显示全部楼层
mark~

出0入170汤圆

发表于 2010-10-15 13:53:54 | 显示全部楼层
CC

出0入0汤圆

发表于 2010-10-15 14:03:53 | 显示全部楼层
我申请商业应用授权。不知道可以不?


我还不会用OS呢,看这个很简单,把不准学了会用到其他领域,所以先此告知LZ。

出0入0汤圆

发表于 2010-10-15 14:10:00 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-10-15 14:11:19 | 显示全部楼层
下课后请把节拍基准及任务调度算法讲下。

出5入8汤圆

发表于 2010-10-15 14:44:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-15 14:50:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-15 14:54:46 | 显示全部楼层
mark

出350入8汤圆

发表于 2010-10-15 15:03:13 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-15 15:13:49 | 显示全部楼层
学习下

出0入0汤圆

 楼主| 发表于 2010-10-15 15:21:04 | 显示全部楼层
回复【8楼】90999 张耀扬
-----------------------------------------------------------------------

其实如果是用于高端领域,我更建议uCOSII、ucLinux,Linux,在性能允许的情况下,不在乎底层中断那点损失。

而如果是用于低于32MHZ的中低性能单片机,我确实十分推荐我的Z1/OS

写它的目的是方便自己、方便大家、共同学习。

如果用于产品开发,能公布源代码给大家学习,我将不收取任何费用。

但如果真用于商用产品,并不公布源码,届时收取一定费用。不过,我会负责目标平台的移植、性能指标检测、特殊系统服务添加,并给予一定性能保证。

PS:可能有很多人不知道怎么辨别别人使用了自己的代码,我的这个内核我采用了汇编回文方法,反汇编时,有几段目标码一目了然,自然就知道是否使用了我的程序。

出0入0汤圆

 楼主| 发表于 2010-10-15 15:21:46 | 显示全部楼层
回复【10楼】chenhuayuan
-----------------------------------------------------------------------

好的,我会的

出0入0汤圆

发表于 2010-10-15 15:28:20 | 显示全部楼层
make

出0入0汤圆

发表于 2010-10-15 15:30:11 | 显示全部楼层
这个要学习一下

出0入0汤圆

发表于 2010-10-15 15:31:40 | 显示全部楼层
mark 一直想给avr上任务调度但是懒得做,写惯了PC机的程序写单片机有时真的很别扭……不过下步准备转战32位机可能用8位机的机会少了了,学习楼主思路~

出0入0汤圆

发表于 2010-10-15 15:37:48 | 显示全部楼层
Nice; 研读一下。

出0入0汤圆

发表于 2010-10-15 15:57:01 | 显示全部楼层
不太理解LZ的“无定时中断打断”具体指什么,是指不在定时中断中进行任务切换吗?所有的OS都可以不在定时中断中切换任务啊。等拜读一下源码先

出0入0汤圆

发表于 2010-10-15 16:01:32 | 显示全部楼层
先回贴,等下再细看

出0入0汤圆

 楼主| 发表于 2010-10-15 16:06:47 | 显示全部楼层
回复【23楼】yangsen
-----------------------------------------------------------------------

本来就是大家一起学习,能看我的代码,我很荣幸。

先说常规协作式内核,它不需要中断切换,但它们平时计时都是在中断中进行,每个中断到了,在中断函数中把每个任务节拍减一。
这样,如果一个单个任务执行很长,它就会被反复打断。

剥夺式内核这个问题更严重,它在中断程序中除了把每个任务节拍减一,还要遍历一次任务,选出高优先级就绪态任务。

当然,这并不是说这些不好,反而在高性能机上这种机制计时准确性要好的多。

但8位机上,经不起这些折腾。

出0入0汤圆

发表于 2010-10-15 16:18:02 | 显示全部楼层
回复【23楼】yangsen  
-----------------------------------------------------------------------

基本原理和想法很简单:
- 协调多任务调度,控制权 总是 任务主动释放的。
- 基于此,即便有时钟Tick也不会起到作用,只是Tick++;因此完全可以不用定时器中断,而在任务释放控制权之后,去读定时器,确认过去了多少时间,进而换算成Tick,然后完成调度。

至于具体怎么实现,那就可能有多种方法了。

出0入0汤圆

发表于 2010-10-15 16:21:36 | 显示全部楼层
/* typedef float                        FP32;        GCC不支持单精度浮点        */
typedef double                    FP64;                 /* 双精度浮点数          */
-------------------------------------------------------------------------
不对。

出0入0汤圆

发表于 2010-10-15 16:22:53 | 显示全部楼层
其实对于抢占式多任务,Tick中断也只是任务调度的一个周期性触发源。

如果只使用优先级调度,没有用到Delay的,也不需要Tick中断。uCOS/II等等完全可以这么运行。

使用时间片轮转以及Delay,完全可以把定时器设定在需要定时的时间上,到时候触发调度就行了,也不是要恒定的Tick周期。
就看怎么平衡资源消耗了。

出0入0汤圆

发表于 2010-10-15 16:30:01 | 显示全部楼层
头大了.
能不能 讲以下!

出0入0汤圆

 楼主| 发表于 2010-10-15 16:31:57 | 显示全部楼层
回复【28楼】dr2001
其实对于抢占式多任务,tick中断也只是任务调度的一个周期性触发源。
如果只使用优先级调度,没有用到delay的,也不需要tick中断。ucos/ii等等完全可以这么运行。
使用时间片轮转以及delay,完全可以把定时器设定在需要定时的时间上,到时候触发调度就行了,也不是要恒定的tick周期。
就看怎么平衡资源消耗了。
-----------------------------------------------------------------------

呵呵,听了你的话,就知道你把我的代码读懂了,看到知音很是高兴,大家共同学习讨论。

是的,其实我这种方式就是一种思想,并不深奥,但是再次以前确实没人想到或者说使用过。

不过抢占式要想用,问题很多,你怎么知道当前任务里就没有去改变其它任务的优先级?或者是恢复了一个比你预定的优先级高的任务?而且万一最高优先级任务要等待很久,就为此让其任务不运行陪着么?

也须可以实现,但还要考虑很多。


回复【27楼】stm32w 上官金虹
/* typedef float fp32; gcc不支持单精度浮点 */
typedef double      fp64;  /* 双精度浮点数   */
-------------------------------------------------------------------------
不对。
-----------------------------------------------------------------------

GCC有float、double型,但是内部都按double处理,我在百度百科和编译器手册看的。可能这些资料太老没更新?

出0入0汤圆

发表于 2010-10-15 16:34:39 | 显示全部楼层
double 是 fp32,而不是fp64

出0入0汤圆

 楼主| 发表于 2010-10-15 16:40:17 | 显示全部楼层
回复【31楼】stm32w 上官金虹
-----------------------------------------------------------------------

哦,用sizeof看了下,果真如此,谢谢

出0入0汤圆

发表于 2010-10-15 16:49:57 | 显示全部楼层
void OSTimeDlyHMS(INT8U Hours, INT8U Minutes, INT8U Seconds)
{
        INT32U TotalTime;
       
        OS_ASSERT( (Hours == 0)
                                && (Minutes == 0)
                                && (Seconds == 0));
        OS_ASSERT(Minutes > 59);
        OS_ASSERT(Seconds > 59);
       
        TotalTime  = (INT32U)((Hours * 3600) + (Minutes * 60) + Seconds) * (INT32U)OS_TICKS_PER_SEC;
       
        TotalTime += OSTimeGet();
       
        while(1)
        {
                OSTimeDly((INT16U)(OS_TICKS_PER_SEC));
               
                if(OSTimeGet() >= TotalTime)
                {
                        return;
                }
        }       
}

这个函数貌似有问题。

OSTimeGet()即OSTime,如果OSTime加到溢出后,if(OSTimeGet() >= TotalTime)判断就是错的。

出0入0汤圆

发表于 2010-10-15 16:53:55 | 显示全部楼层
回复【30楼】yrloy  断雪
-----------------------------------------------------------------------

我早就知道可以这么干了,呵呵,还没看你代码呢。
主要是对你验证代码使用的那个部分感兴趣。

Tick中断这个东西,原理是简单的,但做好了比较困难。
印象中好像有这么干的小OS,不过我忘记是谁了。


无论是何种调度方式,对任务优先级/状态的修改都必须经由OS完成,应用程序不可越权。因此无论协调/抢占的调度方式,OS都会在第一时间获得可能导致任务调度的事件,进而触发对应的调度机制。
如果参考uCOS/II,TaskSuspend; TaskResume; SemPost; etc. 都是有相应的机制的。主要注意判定是否在中断中,以便采用正确策略。
对ThreadX来说,他的定时器就是一个Task(他叫Thread,用于系统中的定时器功能)。

潜在的问题是,这样省了Tick中断数,但是需要更多的代码用于定时时长的计算。

出0入0汤圆

发表于 2010-10-15 17:11:04 | 显示全部楼层
2、无定时中断打断:这在各种OS上,绝对是个难题,OS的性能损失主要也是源于中断的时间,而Z1/OS,没利用定时中断,就更不要说损失了。

3、节拍小:每个OS上都会有时钟最小节拍,如果设置大了,响应能力慢,设置小了,性能损失严重。而本内核基于非中断模式,可以把节拍放到非常小,也无需担心性能损失问题。
----------------------------------------------

本质上是协作方式,实时性不如抢占式。
楼主的程序,在切换任务虽然没有使用中断,但是仍有较大的查询计算,楼主可以自己测试一下OS_Sched()函数,估计耗时也不菲。

还有,楼主只强调说可以把节拍做的很小,但实际上,楼主的系统实时性达不到一个节拍,比如说,一个任务要延时1000个节拍,
如果是枪战式,误差应该是在一个节拍内。楼主的程序误差不能保证是一个节拍,误差可能是几个,几十,几百,甚至几千。

出0入0汤圆

发表于 2010-10-15 17:17:54 | 显示全部楼层
挺好阿 学习

出0入0汤圆

发表于 2010-10-15 17:20:58 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2010-10-15 17:37:28 | 显示全部楼层
学习下

出0入0汤圆

发表于 2010-10-15 17:54:25 | 显示全部楼层
楼主的程序切换任务并不快。

http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4114144&bbs_page_no=1&search_mode=1&search_text=稳定&bbs_id=3020
stm8s105c6小操作系统,切换时间约20us(最高优化时8us).

出0入0汤圆

发表于 2010-10-15 18:03:37 | 显示全部楼层
定时中断(系统节拍)与任务调度本来就没有必然联系,程序中如果不用延时、超时等时间服务,完全可以不用定时器,这是很普通的知识,楼主说什么“难题”,“前无古人”等等,未免太托大了。

出0入0汤圆

发表于 2010-10-15 18:08:47 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-10-15 19:14:04 | 显示全部楼层
先看看代码。

出0入0汤圆

发表于 2010-10-15 19:15:16 | 显示全部楼层
学习学习···

出0入0汤圆

 楼主| 发表于 2010-10-15 19:31:38 | 显示全部楼层
回复【33楼】stm32w 上官金虹
void ostimedlyhms(int8u hours, int8u minutes, int8u seconds)
{
int32u totaltime;
os_assert( (hours == 0)  
&& (minutes == 0)
&& (seconds == 0));
os_assert(minutes > 59);
os_assert(seconds > 59);
totaltime  = (int32u)((hours * 3600) + (minutes * 60) + seconds) * (int32u)os_ticks_per_sec;
totaltime += ostimeget();
while(1)
{
ostimedly((int16u)(os_ticks_per_sec));
if......
-----------------------------------------------------------------------

呵呵,这个我当然考虑到了,难道你看不出我问什么没有先把总时间清零么?因为同时几个进程应用使用这个函数就会引起混乱。我还没有说道怎么使用这个函数。

回复【35楼】stm32w 上官金虹

2、无定时中断打断:这在各种os上,绝对是个难题,os的性能损失主要也是源于中断的时间,而z1/os,没利用定时中断,就更不要说损失了。  
3、节拍小:每个os上都会有时钟最小节拍,如果设置大了,响应能力慢,设置小了,性能损失严重。而本内核基于非中断模式,可以把节拍放到非常小,也无需担心性能损失问题。  
----------------------------------------------
本质上是协作方式,实时性不如抢占式。
楼主的程序,在切换任务虽然没有使用中断,但是仍有较大的查询计算,楼主可以自己测试一下os_sched()函数,估计耗时也不菲。
还有,楼主只强调说可以把节拍做的很小,但实际上,楼主的系统实时性达不到一个节拍,比如说,一个任务要延时1000个节拍,
如果是枪战式,误差应该是在一个节拍内。楼主的程序误差不能保证是一个节拍,误差可能是几个,几十,几百,......
-----------------------------------------------------------------------

请经过思考后再说话,我给你看一张图,你看看uCOSII在8位机上怎么做到这个程度。这张图代表的项目,是我要写这个内核的起源。


8路500HzPWM波 (原文件名:8路PWM.JPG)

8个灯的梯度看的清楚么?LED在不同电流下亮度变化非常小,不用硬件,单纯IO口,你试试用uCOSII做出这个梯度看看。

还有假设uCOSII你在AVR上设置到极限200HZ左右,你想出现1ms秒的延时就要等5ms,这就是所谓的精准?

对于8位机一次在两个延时之间能执行多少指令我在另一篇文章中给出了计算方法,阐述了为何在低性能(32M以下)采用这种方式更合理。

最后声明下,我是uCOSII的爱好者,我很喜欢这个系统。

出0入0汤圆

 楼主| 发表于 2010-10-15 19:52:30 | 显示全部楼层
回复【40楼】John_Lee
定时中断(系统节拍)与任务调度本来就没有必然联系,程序中如果不用延时、超时等时间服务,完全可以不用定时器,这是很普通的知识,楼主说什么“难题”,“前无古人”等等,未免太托大了。
-----------------------------------------------------------------------

我不是来吵架的,我想阁下稍看一眼代码就应该知道我不但提供了延时,还提供了多种延时服务。而且我也没说我不用定时器,我是说不用中断。

不实验,主观判断,甚至一点不看就直接评论。。。你说实话,我冤没冤枉你?

出0入0汤圆

发表于 2010-10-15 20:14:21 | 显示全部楼层
回复【44楼】yrloy  断雪
-----------------------------------------------------------------------

在节拍延迟问题上, stm32w 上官金虹 所指出的是正确的。建议再考虑一下。

协调多任务调度,只有Task自行交出控制权这一条路可走;因此,如果要求一定的实时性,那么:
所有任务,所有的执行路径,在最坏情况下,两个Yield返回点间的执行时间都要小于给定的要求。这个时间,比如一个Tick,或者其它值。
在协调式调度中,也可以给出优先级,时间片,满足相应的调度要求。

你的LED Demo中,任务程序肯定很简单,恩,没有暴露类似的问题。
如果是一个大任务,而正好没有使用Yield机制分割出足够小的时间片,那么显然存在调度器很久执行一次的问题。

Contiki/ProtoThread相关网站/Paper的测试表明,共享堆栈的Coroutine调度,会比切换堆栈Context的效率高不少。不过这也是显然的。压寄存器那么多。

出0入0汤圆

发表于 2010-10-15 20:41:22 | 显示全部楼层
还没看代码,根据楼主的一些描述,我的理解是这样的,楼主的OS就好比win3.2,是协作式多任务,ucosii就好比win95,是抢先式多任务。

协作式多任务需要每个任务自我约束,不能占住资源不放,否则别的任务就没运行的机会,抢先式多任务的话,系统会强制打断任务的运行,以利于其他任务的运行。

协作式多任务的系统开销小,效率高,但是对每个任务的编写要求比抢先式的高,就是要注意给别的任务合理运行的机会,在统筹安排上要更加注意。

楼主的想法是很好的,这是个介于裸机和抢先式多任务之间的一个东西,在资源紧缺的系统里,还是比较有用的。

初看之下的一点理解,如有不对大家一起讨论。

出0入0汤圆

发表于 2010-10-15 20:43:55 | 显示全部楼层
楼主的系统,在任务的安排上会有比较严格的约束,也就是说相互的耦合度比较高。

出0入0汤圆

发表于 2010-10-15 20:56:38 | 显示全部楼层
这里越来越像一个有创造性的社区了,不管这东西性能与质量如何,都顶一下。

出0入0汤圆

 楼主| 发表于 2010-10-15 21:06:12 | 显示全部楼层
回复【46楼】dr2001
回复【44楼】yrloy  断雪
-----------------------------------------------------------------------
在节拍延迟问题上, stm32w 上官金虹 所指出的是正确的。建议再考虑一下。
协调多任务调度,只有task自行交出控制权这一条路可走;因此,如果要求一定的实时性,那么:
所有任务,所有的执行路径,在最坏情况下,两个yield返回点间的执行时间都要小于给定的要求。这个时间,比如一个tick,或者其它值。
在协调式调度中,也可以给出优先级,时间片,满足相应的调度要求。
你的led demo中,任务程序肯定很简单,恩,没有暴露类似的问题。
如果是一个大任务,而正好没有使用yield机制分割出足够小的时间片,那么显然存在调度器很久执行一次的问题。
contiki/protothread相关网站/paper......
-----------------------------------------------------------------------


那个项目是实验室对我的考核题,要求用1602液晶显示,可以实现屏幕上光标前后左右,通过PS2键盘输入来改变8位二进数的输出,用灯来表示,但灯的亮度每个都不同,这些不是利用硬件改变,是软件实现。灯光要求稳定,键盘输入不能阻塞。

谈不上复杂,但是LED在不同电流下亮度变化非常小,每个亮度有明显变化不太好控制。

而且才8路500HZ的PWM,哪怕再实现很多复杂功能2ms的空间也足以保证PWM稳定。

时钟不严格的问题是存在的,其实有个很简单的办法解决,就是万一定时器真的溢出了,就强制改变调度。这样有点像剥夺式,但却在平时能保证程序高效运行不被打断。没实现的原因是不想增加添加互斥信号量而增大体积。可以实现下,然后让大家可自由裁剪。



回复【47楼】jpchen
-----------------------------------------------------------------------

哈哈,WIN3.2,我没用过这老家伙,记得一本讲Win32API编程的书提到过早期Windows是协作式内核。
但是像这种协作式内核早就有了,我文中主要说的意思体现在没用定时中断就实现了这些。
而且时间安排上其实并不复杂,因为8M的单片机8000条指令才1ms,不用任何延时就连续执行8000条指令的程序还真不多见。
协作式内核很少存在共享资源的问题,没有信号量、邮箱,应用程序反倒简单许多的。

出0入0汤圆

发表于 2010-10-15 21:06:55 | 显示全部楼层
有点意思。。和一般的小型OS很大的区别呀。

出0入0汤圆

发表于 2010-10-15 21:10:15 | 显示全部楼层
回复【48楼】jpchen
-----------------------------------------------------------------------

得之,毕竟是在资源稀缺的8位机上,既要高效率又要低耦合度是不现实的,只能作出折中了,楼主想法很好,让8位机也可以不用纯裸奔了,但是依然不能调度自如~

出0入0汤圆

发表于 2010-10-15 21:16:26 | 显示全部楼层
回复【47楼】jpchen  
-----------------------------------------------------------------------

非抢占调度会要求任务有事儿没事儿都要用Delay/Yield释放一下控制权;抢占调度的程序不用。这点如你所述。

但协调调度有两种实现方式,所有任务共享堆栈的Coroutine模式;独立堆栈方式。
独立堆栈方式,每次任务切换依然要保存现场,其实和抢占调度的区别已经很小;主要就是中断结束是否调度。
Coroutine方式不需要保护现场,如果编译器支持Addressable Label,某些情况下效率会高,且节约内存。

LZ的OS使用了独立堆栈的实现方法,依然存在大量的现场保护消耗,这点上没有节约太多资源;
对多任务机制上遇到的很多问题,依然会遇到,Mutex之类如果不实现,那么类似的操作就要求中间必然不存在控制权释放点;
与“标准”实现相比,主要的修改,是Tick的机制;别的,最后还都是类似的。

出0入0汤圆

发表于 2010-10-15 21:23:32 | 显示全部楼层
OK

出0入0汤圆

发表于 2010-10-15 21:33:27 | 显示全部楼层
回复【50楼】yrloy  断雪
-----------------------------------------------------------------------

其实就是说,设计中的任务释放控制权的间隔远小于Tick(这里是2ms)。。。

在LZ位置的OS中,即便代码只用一个Task完成PWM,这个Task vs 放在允许重入的中断中完成,资源的消耗没准更多。。。Task切换可是全压栈REG的。

跑大程序,可以看看Contiki:8MHz AVR 1284P,跑基于uIP的基本标准的IPv6协议栈+6LoWPAN协议栈+IPv6 Router+IETF RPL路由协议+WEB Server。一个任务运行时间轻松破ms数量级。

出0入0汤圆

 楼主| 发表于 2010-10-15 21:38:21 | 显示全部楼层
回复【55楼】dr2001
-----------------------------------------------------------------------

嗯,这就是另一个问题了,你说的没错,我这个主要是Tick机制的改进。

对于Stack的机制,如果能把保存返回地址的栈和保存局部变量的栈分开,效率会更进一层楼。

想过,但想到兼容问题就没有进一步进行。嗯,会考虑一下的。谢谢你的建议。

出0入0汤圆

发表于 2010-10-15 22:00:53 | 显示全部楼层
先记下。回头看,关门了。。。。

出0入0汤圆

发表于 2010-10-15 22:05:03 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-10-15 22:33:30 | 显示全部楼层
函数名和ucos一样一样的!~

出0入0汤圆

发表于 2010-10-15 22:44:28 | 显示全部楼层
版权声明:但凡回帖者,即获得学习及个人开发授权

冲这个回一下。

出0入0汤圆

 楼主| 发表于 2010-10-15 22:45:33 | 显示全部楼层
回复【59楼】ljt8015
-----------------------------------------------------------------------

是啊,我本就是uCosII的忠实爱好者,只是在8位机上它暂时没满足我的要求才写了这个系统。名字一样为了方便用过的人快速学习这个系统呀。
但是实现上是完全不同的。

出0入0汤圆

发表于 2010-10-15 23:36:16 | 显示全部楼层
脚印留下一个。

出0入0汤圆

发表于 2010-10-15 23:45:21 | 显示全部楼层
回复【61楼】yrloy 断雪
回复【59楼】ljt8015  
-----------------------------------------------------------------------
是啊,我本就是ucosii的忠实爱好者,只是在8位机上它暂时没满足我的要求才写了这个系统。名字一样为了方便用过的人快速学习这个系统呀。
但是实现上是完全不同的。
-----------------------------------------------------------------------

貌似没有看到汇编代码!~

出0入0汤圆

发表于 2010-10-15 23:46:36 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-16 00:04:52 | 显示全部楼层
有想法,有行动,不错,我也来测试下

出0入0汤圆

发表于 2010-10-16 06:41:41 | 显示全部楼层
节拍没有使用中断,任务的中断实时性提高了。

但是任务本身的实时性并没有提高。

楼主可以测试一下,你的任务切换时间,从任务调用OSSched开始到跳转的另外一个任务,这个时间估计至少要50us,
如果任务比较多,估计要几百us。

出0入0汤圆

发表于 2010-10-16 07:00:50 | 显示全部楼层
回复【33楼】stm32w 上官金虹
void ostimedlyhms(int8u hours, int8u minutes, int8u seconds)  
{  
int32u totaltime;  
os_assert( (hours == 0)   
&& (minutes == 0)  
&& (seconds == 0));  
os_assert(minutes > 59);  
os_assert(seconds > 59);  
totaltime  = (int32u)((hours * 3600) + (minutes * 60) + seconds) * (int32u)os_ticks_per_sec;  
totaltime += ostimeget();  
while(1)  
{  
ostimedly((int16u)(os_ticks_per_sec));  
if......
-----------------------------------------------------------------------

呵呵,这个我当然考虑到了,难道你看不出我问什么没有先把总时间清零么?因为同时几个进程应用使用这个函数就会引起混乱。我还没有说道怎么使用这个函数。

回复【35楼】stm32w 上官金虹

2、无定时中断打断:这在各种os上,绝对是个难题,os的性能损失主要也是源于中断的时间,而z1/os,没利用定时中断,就更不要说损失了。   
3、节拍小:每个os上都会有时钟最小节拍,如果设置大了,响应能力慢,设置小了,性能损失严重。而本内核基于非中断模式,可以把节拍放到非常小,也无需担心性能损失问题。   
----------------------------------------------  
本质上是协作方式,实时性不如抢占式。  
楼主的程序,在切换任务虽然没有使用中断,但是仍有较大的查询计算,楼主可以自己测试一下os_sched()函数,估计耗时也不菲。  
还有,楼主只强调说可以把节拍做的很小,但实际上,楼主的系统实时性达不到一个节拍,比如说,一个任务要延时1000个节拍,  
如果是枪战式,误差应该是在一个节拍内。楼主的程序误差不能保证是一个节拍,误差可能是几个,几十,几百,......
-----------------------------------------------------------------------

请经过思考后再说话,我给你看一张图,你看看uCOSII在8位机上怎么做到这个程度。这张图代表的项目,是我要写这个内核的起源。



8路500HzPWM波 (原文件名:8路PWM.JPG)


8个灯的梯度看的清楚么?LED在不同电流下亮度变化非常小,不用硬件,单纯IO口,你试试用uCOSII做出这个梯度看看。

还有假设uCOSII你在AVR上设置到极限200HZ左右,你想出现1ms秒的延时就要等5ms,这就是所谓的精准?

对于8位机一次在两个延时之间能执行多少指令我在另一篇文章中给出了计算方法,阐述了为何在低性能(32M以下)采用这种方式更合理。

最后声明下,我是uCOSII的爱好者,我很喜欢这个系统。

-----------------------------------------------------------------------

1.在你的程序中,并没有看到哪里把OSTime清零了,可能是没仔细看。

2.UCOS基本不用

3.我说的是任务实时性
  A:楼主的系统,任务切换并不快。
  B:任务延时精度达不到1个节拍。

4.用中断作为节拍的抢占式OS,节拍是可以做到1ms的。
  我自己也用OS(当然,不是ucos),AVR7.3728M时钟,节拍都是用1ms。

出0入0汤圆

发表于 2010-10-16 09:18:58 | 显示全部楼层
MARK

出0入0汤圆

 楼主| 发表于 2010-10-16 09:55:28 | 显示全部楼层
回复【66楼】stm32w 上官金虹
节拍没有使用中断,任务的中断实时性提高了。
但是任务本身的实时性并没有提高。
楼主可以测试一下,你的任务切换时间,从任务调用ossched开始到跳转的另外一个任务,这个时间估计至少要50us,
如果任务比较多,估计要几百us。

-----------------------------------------------------------------------

嗯,这点对,我的就是要形成一种裸机效果还方便多任务切换编程,强实时、无规律的事件裸机上放到中断,现在还放到中断,而且响应速度跟裸机一致。

当然,为了支持动态建立还无任务数限制,导致搜索时间跟任务数挂钩。我的算法每次并没有把所有节拍都递减浪费时间,而是储存在链结构里,如果是0链的任务,搜索起来是非常快的,看着判断层数很多,实际上已经考虑到了编译器的优化,在.lst文件里并没有几行码,你可以看一下。先假设16个任务(通常够用了),而且所有任务都不合逻辑的放到第7链上,最坏情况,第7链最后一个任务就绪,假设AVR平均每条指令2周期,搜索8个任务链 8 * 7 = 56条,任务连跳16个,16 * 5 = 80条, 前段判断最坏结果代码153 条,总计289,共578周期,假设在内部8M环境下,每条0.125,在乘2,共计72.25us;切换时间72.25us这是最不符合逻辑下的16任务排布,如果在第一链上,还会少很多,第一链上上第一任务,只有153 * 0.125 = 38.25us.

回复【63楼】ljt8015
回复【61楼】yrloy 断雪
回复【59楼】ljt8015   
-----------------------------------------------------------------------  
是啊,我本就是ucosii的忠实爱好者,只是在8位机上它暂时没满足我的要求才写了这个系统。名字一样为了方便用过的人快速学习这个系统呀。  
但是实现上是完全不同的。
-----------------------------------------------------------------------
貌似没有看到汇编代码!~
-----------------------------------------------------------------------

呵呵,有一小部分的,只不过借用GCC的机制全部都内联掉了

出0入0汤圆

发表于 2010-10-16 09:56:21 | 显示全部楼层
顶一下先,嘛,lz也不容易

出0入0汤圆

发表于 2010-10-16 09:59:02 | 显示全部楼层
看看

出0入0汤圆

发表于 2010-10-16 10:09:36 | 显示全部楼层
【楼主位】 yrloy 断雪

----------------------------
我自己也有一个OS,欢迎来讨论。
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4325720&bbs_page_no=1&bbs_id=1000

出0入0汤圆

发表于 2010-10-16 10:10:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-16 12:10:53 | 显示全部楼层
MARK

出0入264汤圆

发表于 2010-10-16 16:13:40 | 显示全部楼层
学习了。

出0入0汤圆

发表于 2010-10-17 22:36:19 | 显示全部楼层
回复【楼主位】yrloy 断雪
-----------------------------------------------------------------------
之前一直在关注 呵呵
先顶再看,这几天不知道是不是网络出问题了,老是上不了坛子

出0入0汤圆

发表于 2010-10-18 09:59:11 | 显示全部楼层
昨晚粗看了一下,因为我没有gcc和protues,所以没有试验。。仅仅交流一下我的看法

第一,lz的系统的确没有用中断,但是任务切换的效率并不高。也就是说从一个任务将要放弃cpu到另一个任务得到cpu的时间差并不小,甚至高过一些抢占式的系统(不是指ucos,坛子里有人写过效率更高的)。实际上说到系统的效率高,都是指的任务切换的时间小,而不是时钟节拍小。
第二,调度方法是协作式的,我并不是说协作式的系统不好,只是协作式的在实时性上几乎可以肯定地说不如抢占式。当一个任务执行一次需要很长的时间的时候,其他的高优先级的任务也要等到这个任务自己释放cpu以后才可以运行。lz的测试程序都是很短的,所以遇不到这个情况。当然可以用状态机的思想将大任务分割成小任务,然后自己释放cpu,但是这样的话,就是去了使用os编写多任务程序简单易用的优势。

本来我想移植一个ICC的版本,不过发现效果并没有那么好。而且ICC的堆栈有硬件堆栈和软件堆栈之分,移植起来忒麻烦了,还是不搞了。。

不过楼主的开源交流精神还是要赞一下 O(∩_∩)O~

出0入0汤圆

 楼主| 发表于 2010-10-18 11:45:07 | 显示全部楼层
回复【78楼】lbc___
昨晚粗看了一下,因为我没有gcc和protues,所以没有试验。。仅仅交流一下我的看法
第一,lz的系统的确没有用中断,但是任务切换的效率并不高。也就是说从一个任务将要放弃cpu到另一个任务得到cpu的时间差并不小,甚至高过一些抢占式的系统(不是指ucos,坛子里有人写过效率更高的)。实际上说到系统的效率高,都是指的任务切换的时间小,而不是时钟节拍小。
第二,调度方法是协作式的,我并不是说协作式的系统不好,只是协作式的在实时性上几乎可以肯定地说不如抢占式。当一个任务执行一次需要很长的时间的时候,其他的高优先级的任务也要等到这个任务自己释放cpu以后才可以运行。lz的测试程序都是很短的,所以遇不到这个情况。当然可以用状态机的思想将大任务分割成小任务,然后自己释放cpu,但是这样的话,就是去了使用os编写多任务程序简单易用的优势。
本来我想移植一个icc的版本,不过发现效果并没有那么好。......
-----------------------------------------------------------------------

呵呵,谢谢您的支持!~

切换的时间稍长并不是因为这个,而是因为我为了支持动态删除、建立任务、任务数无限制。
本身的思路就是节省掉高频Tick的时间。
论实时性,一个好的抢占式系统的第一条件就是所有函数运行时间确定,不会因为任务的多少、信号量的多少、选择条件的走向而影响时间,民间能做到真的很少,所以说也达不到强实时。

之所以在低性能机上选择协作式,我由于主频迫于无奈,题目里要求,需要8路以上的500HzPWM波。协作式只有一个目的,虽然不会保证最高级得到准确响应,但会保证整体运行效率最高。分解任务其实并不复杂。整体算来,这是在性能、易用性和实时性之间的一个折中方案吧。

出0入0汤圆

发表于 2010-10-18 11:54:41 | 显示全部楼层
回复【79楼】yrloy 断雪
-----------------------------------------------------------------------

呃。。。其实动态任务删除和建立,在单片机级别的处理器上我觉得不是很实用,可以去掉它以换取更快的切换时间。呵呵

出0入0汤圆

发表于 2010-10-18 11:57:54 | 显示全部楼层
我表示ProtoThread,4Mhz实现8个梯度无压力
LZ之前一个帖子我也回过,协作式状态机,前人已有无数,前无古人有点过,不过必竟LZ有很多自己的思想,比很多实用新型专利的“改造”强多了,
LZ钻研的精神也是要大力提倡的,这才是真正学到了东东的方法和体现。
网上随便下几个demo改几句实验成功就算会玩开发板那是扯蛋。

出0入0汤圆

 楼主| 发表于 2010-10-18 12:02:01 | 显示全部楼层
回复【80楼】lbc___
回复【79楼】yrloy 断雪
-----------------------------------------------------------------------
呃。。。其实动态任务删除和建立,在单片机级别的处理器上我觉得不是很实用,可以去掉它以换取更快的切换时间。呵呵
-----------------------------------------------------------------------

呵呵,是啊,很不常用。

我保留它的思路是要支持热插拔!

比如说做一个路由,一个新机插入,直接建立一个新任务。

类似的,只要能检测到回馈信息的外设,平时一个空闲任务里不断向不同空IO口发送检测信息,一旦某个口收到,直接建立新任务,拔下来时再删除。单片机里能支持把接口通用的必要性,说大不大,但说不大也大,总有特殊任务需要。

出0入0汤圆

 楼主| 发表于 2010-10-18 12:09:12 | 显示全部楼层
回复【81楼】snoopyzz
我表示protothread,4mhz实现8个梯度无压力
lz之前一个帖子我也回过,协作式状态机,前人已有无数,前无古人有点过,不过必竟lz有很多自己的思想,比很多实用新型专利的“改造”强多了,
lz钻研的精神也是要大力提倡的,这才是真正学到了东东的方法和体现。
网上随便下几个demo改几句实验成功就算会玩开发板那是扯蛋。
-----------------------------------------------------------------------

谢谢您。

顺便问一下,ProtoThread是什么OS,我其实对这个看似简单的小灯很感兴趣,LED在不同电流下亮度变化非常小,需要非常精细。我希望能找到一个合适的OS来做一件事。

我想找一个,可以让16路以上舵机同时工作的通用OS,我写的这个,16路绝不成问题,但不通用。

出0入0汤圆

发表于 2010-10-18 12:22:19 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-10-18 12:23:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-18 12:58:34 | 显示全部楼层
8位机还是用Protothread吧!它是精简的协作式调度典范,真正近乎裸机!

出0入0汤圆

 楼主| 发表于 2010-10-18 13:06:07 | 显示全部楼层
回复【86楼】charlie2008
8位机还是用protothread吧!它是精简的协作式调度典范,真正近乎裸机!
-----------------------------------------------------------------------

听刚才那位朋友一说,我就google了下,在这个http://www.sics.se/~adam/pt/上下了,不知道是我找错了,还是怎么回事,读了并在AVR试验了一下。

并不像介绍中说的占用很小ROM、RAM,很多宏,代码简短,执行速率高。没了任务栈,RAM非常小,但是体积一点不小,反而非常大?!

不过这个东西思路让人佩服,很好,内存占用小。ROM大,但可以理解。

出0入0汤圆

发表于 2010-10-18 13:30:40 | 显示全部楼层
ROM占用大吗?不觉得,用起来很好,它只是被包装过的协作式状态机而已,和我直接使用祼机代码大不了多少

出0入0汤圆

发表于 2010-10-18 13:57:33 | 显示全部楼层
回复【87楼】yrloy  断雪
-----------------------------------------------------------------------

再认真看一下,这个有两个实现:
一个是标准C的Swtich,在接续点很多的时候,判断比较会消耗很多资源。
一个是可寻址标签,这个节约资源而且很快,这个编译器功能就是为了这东西设计的。

生成的代码大,一个是使用的接续方式的原因,一个是你的代码可能有问题。

出10入10汤圆

发表于 2010-10-18 14:21:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-18 15:56:09 | 显示全部楼层
先顶,有空再看。

出0入0汤圆

发表于 2010-10-18 17:53:20 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-10-19 08:52:58 | 显示全部楼层
想法不错

出0入0汤圆

发表于 2010-10-19 11:41:26 | 显示全部楼层
mark一下啊。

出0入0汤圆

发表于 2010-10-22 08:58:29 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-31 00:27:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-31 08:31:35 | 显示全部楼层
哈哈

出0入0汤圆

发表于 2010-10-31 08:47:11 | 显示全部楼层
mark

出95入100汤圆

发表于 2010-10-31 09:06:12 | 显示全部楼层
这个要mark  os 协作

出0入0汤圆

发表于 2010-10-31 19:42:42 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-11-1 17:36:07 | 显示全部楼层
GCC版本是什么呢,我的3.4.3编译不过去哦,,,,,,,,,,
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-10-3 09:18

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

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