打印

VSFOS -- CortexM上的事件驱动操作系统,SBS Guide

[复制链接]
3396|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Simon21ic|  楼主 | 2016-3-22 03:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Simon21ic 于 2016-3-26 19:43 编辑

抽空终于把VSFOS做到基本可以使用了,虽然还有一些功能没时间弄,不过现在已经可以玩了
主页在vsfos.com,等有空了再施工
简略文档:https://www.gitbook.com/book/versaloon/vsf-the-definitive-guide

以前做的一个介绍性的ppt: VSFos.zip (52.52 KB)

VSFOS特点
1. VSFOS的内核是基于事件驱动构架的操作系统,可以支持多个事件队列,并且事件队列之间具备抢占特性,同一个事件队列内部的任务以协作式方式运行。
2. VSFOS底层实现了通用hal,使得高层的代码硬件无关。
3. VSFOS由模块构成,并且模块使用CortexM0指令集编译,对所有CortexM的处理器都可以直接运行,并且使用了地址无关选项,放在任何地址都可以运行。
4. VSFOS的代码使用面向对象的C语言实现
5. VSFOS提供了各种模块,包括shell(命令行界面)、USB主从机协议栈、TCPIP协议栈、文件系统(支持VFS)、各种内存管理工具、流设备、块设备等等

硬件要求:
1. 基本配置:128K flash + 20K ram的stm32f103。推荐配置:384K flash + 64K ram的stm32f103。除非你可以自己配置系统,否则建议使用推荐配置的硬件。硬件需要具备USB设备端接口。
2. STLink或者其他的固件下载工具
3. Windows7系统以上的能够上网的PC一台

软件要求:
1. IAR EWARM 7.50 KS版本
2. STM32 STlink Utility,或者其他的固件下载软件
3. Windows中没有禁用VBS
4. 串口中断,推荐putty

Step by Step Setup:
1. 使用git下载github.com/versaloon/vsf源代码
2. 编译硬件依赖部分:使用IAR EWARM打开并编译vsf\example\appldr\proj\appldr_EWARM7.10_STM32\demo.eww
这里有一个硬件相关的宏设置需要修改,也就是USB的上拉IO口的配置,在工程目录下的hw_cfg_STM32.h中,只需要修改USB_PULLUP_PORT和USB_PULLUP_PIN就行,目前是使用PC13。如果USB没有上拉的话,USB_PULLUP_PORT设置为IFS_DUMMY_PORT。其他宏不用修改,可能之后就会移除。
3. 编译各个模块:使用IAR EWARM打开vsf\example\appldr\proj\modules\modules.eww,使用Batch build或者F8,选择all_release,并按Rebuild all
这里编译的代码就是硬件无关的(只需要芯片是CortexM的),只有os模块,会依赖USB上拉IO口的2个配置宏
4. 系统固件制作和下载: vsfos_stm32.zip (52.52 KB)
第二步中的硬件依赖部分固件,占用最前面的32K空间,32K之后的空间,存放模块固件,模块固定占用的空间为芯片的flash擦除大小,对于stm32有1K和2K的区分,比如一个模块100字节,也会需要占用1K或者2K空间
5. 驱动安装:stm32的USB接上电脑后,会显示一个U盘、一个CDC串口设备和一个RNDIS网卡设备,CDC串口和RNDIS网卡的驱动在U盘中,注意RNDIS驱动的安装需要禁用驱动签名。详细方法自行百度
6. 系统运行:RNDIS驱动安装完成后,PC会识别为一个网络,可以设置PC的共享上网方式,使得我们的硬件可以联网。使用串口终端打开CDC设备,进入命令行界面,先运行help看看有哪些命令,目前有一些命令还没实现功能。
7. 从网络安装模块,待续





相关帖子

沙发
Simon21ic|  楼主 | 2016-3-22 03:17 | 只看该作者
本帖最后由 Simon21ic 于 2016-3-22 16:45 编辑

上图:




使用特权

评论回复
板凳
Simon21ic|  楼主 | 2016-3-22 03:20 | 只看该作者
本帖最后由 Simon21ic 于 2016-3-22 13:04 编辑

开发自己的模块:

推荐使用vsf\example\appldr\proj\modules中的app_helloworld为模板
复制一份app_helloworld.ewd和app_helloworld.ewp,并修改为自己的模块的名字,然后在modules.eww中,按照里面的项目的格式,加入自己的模块工程。然后使用IAR打开modules.eww,激活自己的模块工程,就可以自行添加删除文件了。注意,modmain.c为模块的通用主函数,这个文件不能删除,并且不需要修改,为所有模块共享的入口和出口代码。

需要修改的设置:
1. 工程选项中,C/C++ Compiler的PreProcessor页面,Defined symbols里
MODULE_NAME设置为模块的名字,MODULE_INIT设置为模块的入口函数,MODULE_EXIT设置为模块的出口函数。
2. Assembler的Preprocessor页面,Defined symbols里
MODULE_SIZE设置为模块的大小,这个只是一个大概的大小,之后编译完成后,IAR会调用fixsize.vbs自动设置模块的实际大小。MODULE_NAME模块的名字。

其他设置:
1. General Options里,Processor variant选择Cortex-M0
2. C/C++ Compiler的Code页面,勾选Position-independence中的Code and read-only data(ropi)
3. Output Converter里,使能Generate additional output,并且选择binary,不要Override default
4. Build Actions里的Post-build command line设置为:
wscript $PROJ_DIR$\fixsize.vbs $EXE_DIR$\$PROJ_FNAME$.bin
5. Linker中的Config页面,linker configuration file选择Override default,并且选择$PROJ_DIR$\module.icf作为连接脚本
6. Linker中的Library页面,选择Override default program entry,并设置Entry symbol为module_entry

使用特权

评论回复
地板
Simon21ic|  楼主 | 2016-3-22 03:20 | 只看该作者
本帖最后由 Simon21ic 于 2016-3-22 04:33 编辑

应用举例:app_blink,此模块功能为注册一个命令行界面的命令,可以通过这个命令来控制一个IO口以设置的间隔反转,类似LED的blink效果。

#include "vsf.h"
#include "../../vsfos/vsfos.h"

struct app_blink_t
{
        struct interface_gpio_pin_t pin;
        uint32_t interval;
};

首先需要include基本的系统头文件,并且定义blink需要的结构:一个GPIO引脚和一个时间间隔属性。
下面我们先掠过应用代码,说明一下模块的载入和退出代码:
vsf_err_t app_blink_modexit(struct vsf_module_t *module)
{
        vsf_bufmgr_free(module->ifs);
        module->ifs = NULL;
        return VSFERR_NONE;
}

vsf_err_t app_blink_modinit(struct vsf_module_t *module,
                                                                struct app_hwcfg_t const *cfg)
{
        struct vsfshell_handler_t *handlers;
        handlers = vsf_bufmgr_malloc(2 * sizeof(struct vsfshell_handler_t));
        if (!handlers) return VSFERR_FAIL;
        memset(handlers, 0, sizeof(*handlers));

        handlers[0] = (struct vsfshell_handler_t){"blink", app_blink};
        vsfshell_register_handlers(&vsfos->shell, handlers);
        module->ifs = handlers;
        return VSFERR_NONE;
}

模块的载入,就是动态分配模块的内存,并且设置为模块的接口(module->ifs)。
app_blink里,没有专用的模块接口,只是使用了vsfshell_handler_t的数组,动态分配内存并且初始化handlers之后,就调用vsfshell_register_handlers注册blink命令。
当在命令行界面中,运行blink后,就会调用到执行的命令处理函数app_blink:
static vsf_err_t app_blink(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
        struct vsfshell_handler_param_t *param =
                                                (struct vsfshell_handler_param_t *)pt->user_data;
        struct vsfsm_pt_t *outpt = param->output_pt;
        struct app_blink_t *blink = (struct app_blink_t *)param->priv;

        vsfsm_pt_begin(pt);

        if (param->argc != 4)
        {
                vsfshell_printf(outpt, "format: %s PORT PIN INTERVAL"VSFSHELL_LINEEND,
                                                        param->argv[0]);
                goto end;
        }

        param->priv = vsf_bufmgr_malloc(sizeof(struct app_blink_t));
        if (NULL == param->priv)
        {
                vsfshell_printf(outpt, "not enough resources"VSFSHELL_LINEEND);
                goto end;
        }
        blink = (struct app_blink_t *)param->priv;
        blink->pin.port = strtoul(param->argv[1], NULL, 0);
        blink->pin.pin = strtoul(param->argv[2], NULL, 0);
        blink->interval = strtoul(param->argv[3], NULL, 0);

        vsfshell_printf(outpt, "blinking on GPIO%d.%d"VSFSHELL_LINEEND,
                                        blink->pin.port, blink->pin.pin);

        // switch to background thread
        vsfshell_handler_release_io(param);
        vsfhal_gpio_init(blink->pin.port);
        vsfhal_gpio_config_pin(blink->pin.port, blink->pin.pin, GPIO_OUTPP);
        while (1)
        {
                vsfhal_gpio_clear(blink->pin.port, 1 << blink->pin.pin);
                vsfsm_pt_delay(pt, blink->interval);
                vsfhal_gpio_set(blink->pin.port, 1 << blink->pin.pin);
                vsfsm_pt_delay(pt, blink->interval);
        }

end:
        vsfshell_handler_exit(param);
        vsfsm_pt_end(pt);
        return VSFERR_NONE;
}

这里就是blink的功能函数了。命令处理函数基本都类似,先得到vsfshell_handler_param_t的参数指针param,并且从这个结构中,得到outpt用于信息输出。param->argc和param->argv就是命令行中输入的参数,参数以空格分开,也可以使用双引号把带空格的字符串作为一个参数。vsfshell_printf就是类似printf的功能,只是第一个参数需要是outpt。检查完毕后,就分配app_blink_t,并且解析参数得到需要控制的IO口以及翻转的毫秒间隔。之后调用vsfshell_handler_release_io后,本任务转入后台运行,命令行界面可以继续输入命令。后台运行的任务就是初始化IO口后,在while(1)中,以设置的毫秒间隔翻转IO口。最后,如果命令处理函数退出的话,要调用一次vsfshell_handler_exit。
在之前的那个任务运行的时候,还可以再次运行blink命令,控制其他的IO口翻转,vsfshell中的命令处理函数,都是动态分配的独立任务,返回会释放资源,不返回则可以一直独立运行。

VSFOS内置的命令都在vsf\example\appldr\module\vsfos\vsfos_busybox.c中,这里有更多的示例代码。
如果模块可以提供接口给其他模块使用,最简单的可以参考vsf\tool\crc中的CRC模块的代码。
如果模块依赖某个硬件接口库,那么需要在使用这个硬件接口之前,判断硬件接口是否可用:
        if (!vsfhal_hcd_if)
        {
                vsfshell_printf(outpt, "hcd interface not available"VSFSHELL_LINEEND);
                goto end;
        }

如果模块依赖其他模块,那么在模块的入口函数里,需要调用vsf_module_get来判断依赖的模块是否存在:
        if (vsf_module_get(VSFIP_MODNAME) != NULL)
        {
                handlers[idx++] = (struct vsfshell_handler_t){"ipconfig", vsfos_busybox_ipconfig, ctx};
                handlers[idx++] = (struct vsfshell_handler_t){"arp", vsfos_busybox_arp, ctx};
                handlers[idx++] = (struct vsfshell_handler_t){"ping", vsfos_busybox_ping, ctx};
                if (vsf_module_get(VSFIP_HTTPD_MODNAME) != NULL)
                {
                        handlers[idx++] = (struct vsfshell_handler_t){"httpd", vsfos_busybox_httpd, ctx};
                }
                if (vsf_module_get(VSFIP_DNSC_MODNAME) != NULL)
                {
                        handlers[idx++] = (struct vsfshell_handler_t){"dns", vsfos_busybox_dns, ctx};
                        vsfip_dnsc_init();
                }
        }



使用特权

评论回复
5
Simon21ic|  楼主 | 2016-3-22 03:23 | 只看该作者
本帖最后由 Simon21ic 于 2016-3-23 00:48 编辑

FAQ:
目前还没人有问题

已知BUG:
1. 模拟的U盘里,不能建立新文件,Win10的一个更新版本后,会建立一个WPSettings.dat,在最新版本中已经修复,可自行更新

使用特权

评论回复
6
Simon21ic|  楼主 | 2016-3-22 23:07 | 只看该作者
yyy71cj 发表于 2016-3-22 20:25
是看起来很多的样子

内容还是有一些的,并不只是一个多任务内核,有不少操作系统的功能
比如命令行界面,网络子系统,文件子系统等等
甚至在图片里,还有ipconfig的命令查看网络的配置,当然,这些命令类似busybox的里一些linux命令,只是做了相当多的简化

使用特权

评论回复
7
zwwoshi| | 2016-3-23 09:28 | 只看该作者
持续关注中

使用特权

评论回复
8
huangqi412| | 2016-3-26 16:42 | 只看该作者
应该是看懂楼主的这套系统大致意思了。  目前CM在用的主流RTOS以UCOS为主要代表,以STM32为例,大家都是把UCOS工程拷过来,然后添加STM32官方库,再添加FAT库TCP库到工程,然后写用户功能代码,最后编译成BIN下载。要修改功能则通过IAP全部重刷。  楼主的将模式改成了电脑软件模式,分别设计编译分别安装更换。灵活强大。编译硬件依赖部分         这部分是不是类似BOOT功能主要提供下载和加载功能,也包含对应芯片所有模块底层驱动(SPI,USB,PWM)?   还包括命令行工具 ?   
模块则包括FAT,TCP,WIFI芯片驱动(比如基于SDIO连接),电机芯片驱动(比如基于SPI连接),也包括OS吧(OS也是一个可选模块)?     模块或者也可以叫插件?           相当于电脑上的硬件驱动+软件模块的集合?
用户功能代码是不是也跟模块等同?也是编成一个模块丢进去,由硬件依赖部分加载运行?
0 虽然有链接,但是没去看,因为这个肯定是很大一堆代码很多文件夹,而且没有文档和框图甚至文件目录结构,一下子去浏览也会一头雾水。   各部分都是全开源么?文字很多但是没框图一目了然,文件目录也没说明,能否框图让我们体会下。
1 文中提到多人各写模块BIN,看了是IAR,很多人是MDK,求楼主给个MDK模块工程让我们了解下这个分模块的模式。





使用特权

评论回复
9
Simon21ic|  楼主 | 2016-3-26 19:41 | 只看该作者
本帖最后由 Simon21ic 于 2016-3-26 20:02 编辑
huangqi412 发表于 2016-3-26 16:42
应该是看懂楼主的这套系统大致意思了。  目前CM在用的主流RTOS以UCOS为主要代表,以STM32为例,大家都是把U ...

ucos其实更多的只是一个多任务内核,外面可以用fatfs做文件系统,但是,并没有通用的文件系统构架,也就是说,你访问fatfs使用fatfs提供的接口,如果以后加入另一个exfat,就需要用exfat的接口。
我做的这个,说实话,只是用来玩玩的。不过各个部分都按照标准的方式实际了,比如说,文件系统层,有标准接口。我这里的httpd服务器,访问文件系统,不会因为fat32换成exfat而需要修改。当然,这个有好处也有麻烦的地方,就是需要自己按照自己的接口标准,实现文件系统。

目前设计上,硬件依赖部分包含芯片的外设驱动、多任务内核(包括IPC等等)、内存管理和一些C库,功能只能检测各个模块,并且载入os模块。然后其他部分,包括OS也都只是模块,模块可以提供接口给其他模块使用(模块依赖性)。命令行shell也只是一个模块,由os模块调用,并且os模块里,有一个busybox,实现各种命令,包括repo命令,可以用来下载其他模块,不过这个只是设想,还没有时间调试代码。

我们没用MDK,目前只有IAR的工程,大部分都开源,除了wifi驱动、蓝牙协议栈,不过即使不开源,只要提供二进制模块,也一样可以用。系统结构图之前做过一个简单的版本,已经放到LZ位

虽然系统功能可以很复杂,但是一般应用的话,系统运行构架还是一样,就是main函数的死循环里,只需要调用休眠。各种中断发送事件并触发pendsv中断,然后pendsv里以非阻塞的方式处理各个中断。一般一个中断处理时间在十几到几十us,然后系统继续休眠。当然,有些实时中断,比如过流检测的中断,直接关闭PWM驱动啥的,这个不需要经过pendsv,可以非常快的实时响应。然后,如果系统有一些复杂的运算,比如MP3解码,这些一般不要求实时性,就可以放在main函数里运行,可以被中断(包括pendsv)抢占,也不会影响其他任务。

总结来说
1. 非实时任务,main里运行(其他任务发送事件给main事件队列,main里有事件就处理时间,没事件就休眠)
2. 实时任务的硬实时部分,直接在中断里处理
3. 实时任务的软实时部分(中断处理代码中,发送事件给pendsv事件队列,并激活pendsv),在pendsv(最低优先级中断)里处理
任务不需要软件调度,完全有硬件的中断系统调度,所以多任务内核在cortexM3上编译,也占用300-600字节的flash

使用特权

评论回复
10
huangqi412| | 2016-3-28 14:51 | 只看该作者
本帖最后由 huangqi412 于 2016-3-28 14:53 编辑
Simon21ic 发表于 2016-3-26 19:41
ucos其实更多的只是一个多任务内核,外面可以用fatfs做文件系统,但是,并没有通用的文件系统构架,也就是 ...

额,那么  用户功能代码是在  编译硬件依赖部分  么          比如用户功能想通过SPI接口的无线芯片发送一段文字。先查看FLASH里是否有无线芯片驱动模块,再用无线芯片驱动模块里面的函数配置无线芯片参数什么的,然后发送。

如果模块依赖其他模块,那么在模块的入口函数里,需要调用vsf_module_get来判断依赖的模块是否存在:
  •         if (vsf_module_get(VSFIP_MODNAME) != NULL)




比如我安装了好多个模块,他们都按段依次烧到FLASH里了,  需要用某个模块时候要先确认是否存在某个模块,  那么模块烧进FLASH时候是否将模块注册到这个VSF框架里了, 重启后有个FLASH型注册表可以查找。  还是说重启后每次软件去挨段FLASH扫描模块建个RAM模块列表。


你这个真的好先进,应该大多数人一下子搞不清。 问题理论上都能通过读代码知道,不过还是有说明和框图比较能让人很快了解大概。    对一个陌生东西应该大家最开始想入手还是怎么加自己的功能代码,以及怎么添加一个新的模块。虽然看不太懂,非常感谢楼主让我们大开眼界,了解这种先进的开发方式。




使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:www.versaloon.com --- under construction

266

主题

2597

帖子

104

粉丝