打印
[应用相关]

VSF平台构架之简介

[复制链接]
4058|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Simon21ic|  楼主 | 2014-7-24 14:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Simon21ic 于 2014-7-24 15:52 编辑

不知不觉已经一年没法技术贴了,最近一年几乎都是项目外包、全职/兼职招聘的帖子。

首先,一年之后,再曝一下ST的USB全速库的BUG,貌似在最新的库里,也同样保留了这个BUG:
https://bbs.21ic.com/icview-565599-1-1.html

最近一直在招人,也做了一些培训,发现不少开发人员使用我的平台有一定的困难,再加上我太懒,所以就在网上写个简单的培训教程,以后招人可以简单一些。
发在st的论坛里,也只是因为以前在这个论坛发的技术帖子比较多,这里人气也不错。

VSF平台是我在几年前做的一个系统构架,经过不少时间的完善,现在已经初步具备一个开发平台的特性。
VSF平台设计的最重要的核心理念是RAD开发方式,也就是快速应用开发。
VSF平台使用的编码语言是面向对象的C语言,除了应用层的用户代码外,其他代码都是非阻塞(或者都有非阻塞版本),用户代码可以阻塞也可以非阻塞,不过一般我建议,还是使用基于非阻塞状态机轮询。

VSF的构架内容不少,我这里先列举一些,以后会继续增加:
1. 处理器的抽象层接口,当然,我在TODO里,写了一些关于这个部分的修改,现在的话,还算能够满足要求,就暂时不改了
比如,如果要把P0.1设置输出并输出高电平的话的话,使用如下代码:
<code>
interfaces->gpio.init(0);
interfaces->gpio.config_pin(0, 0, GPIO_OUTPP);
interfaces->gpio.set(0, 1UL << 0);
当然,各个处理器都不相同,很多也会提供一些特殊功能,我们目前封装的都是一些最基本的功能。特殊功能可以提供特殊的和应用相关的接口。
在VSF代码的interfaces目录下,就有这些接口的定义文件,就以GPIO举例的话,大概是如下代码:
interfaces.h里:
#if IFS_GPIO_EN
#define CORE_GPIO_INFLOAT(m)        __CONNECT(m, _GPIO_INFLOAT)
#define CORE_GPIO_INPU(m)            __CONNECT(m, _GPIO_INPU)
#define CORE_GPIO_INPD(m)            __CONNECT(m, _GPIO_INPD)
#define CORE_GPIO_OUTPP(m)            __CONNECT(m, _GPIO_OUTPP)
#define CORE_GPIO_OUTOD(m)            __CONNECT(m, _GPIO_OUTOD)
#define GPIO_INFLOAT                CORE_GPIO_INFLOAT(__TARGET_CHIP__)
#define GPIO_INPU                    CORE_GPIO_INPU(__TARGET_CHIP__)
#define GPIO_INPD                    CORE_GPIO_INPD(__TARGET_CHIP__)
#define GPIO_OUTPP                    CORE_GPIO_OUTPP(__TARGET_CHIP__)
#define GPIO_OUTOD                    CORE_GPIO_OUTOD(__TARGET_CHIP__)
struct interface_gpio_t
{
    vsf_err_t (*init)(uint8_t index);
    vsf_err_t (*fini)(uint8_t index);
    vsf_err_t (*config_pin)(uint8_t index, uint8_t pin_idx, uint8_t mode);
    vsf_err_t (*config)(uint8_t index, uint32_t pin_mask, uint32_t io,
                        uint32_t pull_en_mask, uint32_t input_pull_mask);
    vsf_err_t (*set)(uint8_t index, uint32_t pin_mask);
    vsf_err_t (*clear)(uint8_t index, uint32_t pin_mask);
    vsf_err_t (*out)(uint8_t index, uint32_t pin_mask, uint32_t value);
    vsf_err_t (*in)(uint8_t index, uint32_t pin_mask, uint32_t *value);
    uint32_t (*get)(uint8_t index, uint32_t pin_mask);
};
#define CORE_GPIO_INIT(m)                __CONNECT(m, _gpio_init)
#define CORE_GPIO_FINI(m)                __CONNECT(m, _gpio_fini)
#define CORE_GPIO_CONFIG_PIN(m)            __CONNECT(m, _gpio_config_pin)
#define CORE_GPIO_CONFIG(m)                __CONNECT(m, _gpio_config)
#define CORE_GPIO_IN(m)                    __CONNECT(m, _gpio_in)
#define CORE_GPIO_OUT(m)                __CONNECT(m, _gpio_out)
#define CORE_GPIO_SET(m)                __CONNECT(m, _gpio_set)
#define CORE_GPIO_CLEAR(m)                __CONNECT(m, _gpio_clear)
#define CORE_GPIO_GET(m)                __CONNECT(m, _gpio_get)

vsf_err_t CORE_GPIO_INIT(__TARGET_CHIP__)(uint8_t index);
vsf_err_t CORE_GPIO_FINI(__TARGET_CHIP__)(uint8_t index);
vsf_err_t CORE_GPIO_CONFIG_PIN(__TARGET_CHIP__)(uint8_t index, uint8_t pin_idx,
                                                uint8_t mode);
vsf_err_t CORE_GPIO_CONFIG(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask,
        uint32_t io, uint32_t pull_en_mask, uint32_t input_pull_mask);
vsf_err_t CORE_GPIO_IN(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask,
        uint32_t *value);
vsf_err_t CORE_GPIO_OUT(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask,
        uint32_t value);
vsf_err_t CORE_GPIO_SET(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask);
vsf_err_t CORE_GPIO_CLEAR(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask);
uint32_t CORE_GPIO_GET(__TARGET_CHIP__)(uint8_t index, uint32_t pin_mask);
#endif
</code>
这里使用了一些小技巧来使得这个代码对VSF支持的所有芯片都通用。
比如,如果设置__TARGET_CHIP__宏为stm32的话,那么CORE_GPIO_INIT(__TARGET_CHIP__)就是stm32_gpio_init。
所有的这些函数和宏都连接到stm32_XXXXXX。
interfaces.c里,其实只是实例化了这个结构:
<code>
const struct interfaces_info_t core_interfaces =
{
  ....
#if IFS_GPIO_EN
    ,{
        // gpio
        CORE_GPIO_INIT(__TARGET_CHIP__),
        CORE_GPIO_FINI(__TARGET_CHIP__),
        CORE_GPIO_CONFIG_PIN(__TARGET_CHIP__),
        CORE_GPIO_CONFIG(__TARGET_CHIP__),
        CORE_GPIO_SET(__TARGET_CHIP__),
        CORE_GPIO_CLEAR(__TARGET_CHIP__),
        CORE_GPIO_OUT(__TARGET_CHIP__),
        CORE_GPIO_IN(__TARGET_CHIP__),
        CORE_GPIO_GET(__TARGET_CHIP__)
    }
#endif
};
</code>
另外,奇葩的是,还有PC版的interfaces结构,当然,这个是需要我的Versaloon硬件(USB_TO_XXX),就之前写的gpio部分的代码,各个平台都是一样的(包括PC版的代码)。
有PC版的好处是,一些非实时性的东西,可以在实际硬件完成之前,在VC上实现代码,并且能够控制实际的Versaloon的硬件的各个接口来做调试。

2. 面向对象的C语言
这个基本对于模块化的设计费用有用,C语言虽然并不是天生就具备一些面向对象的语法,但是面向对象实际是和语言无关的,面向对象只是一种编码方式,和写代码的人有关。
C语言是可以实现部分面向对象的特性的。
比如,VSF平台下,对设备驱动做了抽象,实现了一个抽象的驱动结构:
<code>
struct dal_info_t
{
    void *ifs;
    void *param;
    void *info;
    void *extra;
};
</code>
其中,ifs表示和硬件接口的虚成员,param表示参数,info表示 ,extra表示一些额外信息。
这里以单个GPIO控制的按键模块作为例子:
key.h:
<code>
struct key_interface_t
{
    uint8_t port;
    uint8_t pin;
};

struct key_param_t
{
    bool valid_low;
    uint32_t filter_ms;
    struct
    {
        void (*on_PRESS)(struct dal_info_t *info);
        void (*on_PRESSED)(struct dal_info_t *info, uint32_t ms);
    } callback;
};

struct key_info_t
{
    // private
    uint32_t tickcnt_on_press;
    bool isdown_unfiltered;
    bool isdown_filtered;
};

vsf_err_t key_init(struct dal_info_t *info);
vsf_err_t key_fini(struct dal_info_t *info);
bool key_isdown_unfiltered(struct dal_info_t *info);
vsf_err_t key_poll(struct dal_info_t *info);
</code>
应用部分的代码:
<code>
struct app_info_t
{
        struct key_interface_t key_ifs;
        struct key_param_t key_param;
        struct dal_info_t key_dal;
        struct key_info_t key_info;
} app =
{
            {KEY0_PORT, KEY0_PIN},
            {true, 5, {on_key_press, on_key_pressed}},
            {&app.keys[0].key_ifs, &app.keys[0].key_param,
                &app.keys[0].key_info, NULL},
};
int main(void)
{
    interfaces->core.init(NULL);
    interfaces->tickclk.init();
    interfaces->tickclk.start();
    key_init(&app.key_dal);
    while(1)
    {
        key_poll(&app.key_dal);
    }
}
</code>
代码中,大部分只是把key类实例化,设置示例的相关参数和事件(回调函数)。
key_init和key_poll就是key的类函数,针对key的实例都有效。
当然,驱动代码是调用interfaces的各个接口来控制底层硬件的。
在这个构架下,应用层的代码,得到了最大的简化,基本上只是设置实例的各个属性和事件,并且调用各种已经实现的类函数。
这个和一些PC上的RAD开发环境类似,比如VB的开发,也是把一个button放到窗口后,设置相关的属性和事件。
大部分这样的OOC的驱动代码,都不需要全局变量。

3. 非阻塞的接口,状态机轮询机制
VSF平台的驱动以及各种协议栈,接口上都是非阻塞的,这样用户可以用这个实现状态机系统,当然,也可以阻塞调用。
还是用之前的按键驱动作为例子:
<code>
vsf_err_t key_poll(struct dal_info_t *info)
{
    struct key_info_t *key_info = (struct key_info_t *)info->info;
    struct key_param_t *param = (struct key_param_t *)info->param;
    uint32_t cur_tickcnt;
   
    cur_tickcnt = interfaces->tickclk.get_count();
    if (key_info->isdown_unfiltered)
    {
        uint32_t diff_tickcnt = cur_tickcnt - key_info->tickcnt_on_press;
        
        if (!key_isdown_unfiltered(info))
        {
            key_info->isdown_unfiltered = key_info->isdown_filtered = false;
            if (diff_tickcnt >= param->filter_ms)
            {
                if (param->callback.on_PRESSED != NULL)
                {
                    param->callback.on_PRESSED(info, diff_tickcnt);
                }
            }
        }
        else if (diff_tickcnt >= param->filter_ms)
        {
            if (!key_info->isdown_filtered)
            {
                key_info->isdown_filtered = true;
                if (param->callback.on_PRESS != NULL)
                {
                    param->callback.on_PRESS(info);
                }
            }
        }
    }
    else
    {
        if (key_isdown_unfiltered(info))
        {
            key_info->isdown_unfiltered = true;
            key_info->tickcnt_on_press = cur_tickcnt;
        }
    }
    return VSFERR_NONE;
}
</code>
key_poll代码用于在主循环中,轮训一个key实例,代码类似状态机,非阻塞实现。

4. 抽象层
一些通用的驱动,在VSF平台下,有对应的抽象层。
抽象层主要是为了在协议栈上,实现一些通用的特性,这里用USB设备端协议栈的MSC类驱动做一个例子。
MSC是U盘的类协议,在对应盘的Lun结构中,有一个指向mal(存储器抽象层的接口):
<code>
static struct dal_info_t sd_dal_info =
{
    &sd_spi_drv_ifs,
    &sd_param,
    &sd_spi_drv_info,
    &sd_mal_info,
};
struct SCSI_LUN_info_t MSCBOT_LunInfo[] =
{
    {
        &sd_dal_info,
        {
            true,
            {'S', 'i', 'm', 'o', 'n', ' ', ' ', ' '},
            {'S', 'i', 'm', 'o', 'n', ' ', ' ', ' ',
            'S', 'i', 'm', 'o', 'n', ' ', ' ', ' '},
            {'1', '.', '0', '0'},
            SCSI_PDT_DIRECT_ACCESS_BLOCK
        }
    }
};
</code>
这里的sd_dal_info是SD卡的驱动,但是,驱动的extra设置为sd_mal_info,这个就是存储器的抽象层。
在MSC的代码里:
<code>
static vsf_err_t SCSI_io_WRITE10(struct SCSI_LUN_info_t *info, uint8_t CB[16],
        struct vsf_buffer_t *buffer, uint32_t cur_page)
{
    struct mal_info_t *mal_info = (struct mal_info_t *)info->dal_info->extra;
    uint32_t lba = GET_BE_U32(&CB[2]);
    uint32_t block_size = (uint32_t)mal_info->capacity.block_size;
    vsf_err_t err;
   
    switch (info->status.mal_opt)
    {
    case SCSI_MAL_OPT_INIT:
        if (mal.writeblock_nb_start(info->dal_info, lba * block_size,
                                    info->status.page_num, buffer->buffer))
        {
            info->status.page_num = 0;
            info->status.memstat = SCSI_MEMSTAT_NOINIT;
            info->status.sense_key = SCSI_SENSEKEY_HARDWARE_ERROR;
            info->status.asc = 0;
            SCSI_errcode = SCSI_ERRCODE_FAIL;
            return VSFERR_FAIL;
        }
        info->status.mal_opt = SCSI_MAL_OPT_IO;
    case SCSI_MAL_OPT_IO:
        if (mal.writeblock_nb(info->dal_info, (lba + cur_page) * block_size,
                                buffer->buffer))
        {
            info->status.page_num = 0;
            info->status.memstat = SCSI_MEMSTAT_NOINIT;
            info->status.sense_key = SCSI_SENSEKEY_HARDWARE_ERROR;
            info->status.asc = 0;
            SCSI_errcode = SCSI_ERRCODE_FAIL;
            return VSFERR_FAIL;
        }
        info->status.mal_opt = SCSI_MAL_OPT_CHECKREADY;
        ....
</code>
直接调用mal.xxxx通用接口函数,对实际的存储器操作。从而,MSC的驱动,不用知道外挂的是什么存储器,甚至不知道外挂的是不是存储器。
如果应用中,不使用SD卡,换成SPI的flash的话,其实质需要把MSCBOT_LunInfo中的sd_dal_info实例换成df_dal_info(dataflash驱动)的实例就行,当然,mal层的一些参数也要做相应的设置,比如芯片容量等信息。
从概念上来讲,mal其实可以认为是块设备的驱动,因为实际在VSF平台里,液晶的驱动也是通过mal接口实现的。
当然,对饮用块设备,VSF里也定义的流设备,比如串口就是使用流的方式驱动的,收发数据其实只是操作对应通道的流。

5. 协议栈
和驱动一样,VSF平台具备一些通用协议栈,当然,目前开源的只有USB设备端的协议栈。
这个协议栈已经移植到不少于4中不同的处理器上了,启动也不是协议栈移植,而是底层的驱动接口移植。
在interfaces结构用,有USB device的通用接口,移植好这个后,高层的协议栈,类驱动,应用部分都不需要修改。
协议栈的实现同样也是面向对象的代码,非阻塞的调用方式。

6. 搭积木的应用开发
RAD的开发平台,就是要把开发尽可能简化。当然,前提是你要对VSF平台有足够哦了解。
在应用代码中,会有很多代码只是用于类的实例化,比如USB设备端的应用,几乎实际代码没几行,其他的都是定义USB相关的实例。
这个就是有我之前说的几个特性决定的,甚至可以做成GUI的开发方式,界面都可以和VB类似,有属性/时间设置窗口、应用窗口等等。

介绍部分比较简单,只是列举了一些特性,配合一些代码例子做说明。
之后如果有时间的话,会针对一些具体内容,展开说明。
另外,玩这个东西的话,需要比较强的C语言能力。
沙发
想做大牛的小马| | 2014-7-24 16:58 | 只看该作者
偶只能说。。。不明觉厉

使用特权

评论回复
板凳
Rain_King| | 2014-7-24 17:21 | 只看该作者
没有看明白............

使用特权

评论回复
地板
mmuuss586| | 2014-7-24 17:22 | 只看该作者

支持下,牛人啊;

使用特权

评论回复
5
abin0415| | 2014-8-9 09:10 | 只看该作者
顶起。

使用特权

评论回复
6
ZUI135| | 2014-8-28 20:28 | 只看该作者
typedef.........................................................................

使用特权

评论回复
7
SLEET1986| | 2014-9-1 14:41 | 只看该作者
顶你一下

使用特权

评论回复
8
Simon21ic|  楼主 | 2014-9-1 18:59 | 只看该作者
3. 基于轮询的接口,现在更新为事件驱动的状态机
这个平台不做推广,只是自己用的,大家不用顶
写这些只是为了以后给别人介绍的时候,能够省些事而已

使用特权

评论回复
9
zxywq| | 2019-6-21 16:05 | 只看该作者
新人刚来,顶一顶

使用特权

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

本版积分规则

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

266

主题

2597

帖子

104

粉丝