建议使用以下浏览器,以获得最佳体验。 IE 9.0+以上版本 Chrome 31+ 谷歌浏览器 Firefox 30+ 火狐浏览器
请选择 进入手机版 | 继续访问电脑版
设置昵称

在此一键设置昵称,即可参与社区互动!

确定
我再想想
选择版块
直达楼层
标签
您还可以添加5个标签
  • 没有搜索到和“关键字”相关的标签
  • 云产品
  • 解决方案
  • 技术领域
  • 通用技术
  • 平台功能
取消

采纳成功

您已采纳当前回复为最佳回复

樊心昊

发帖: 172粉丝: 139

发消息 + 关注

发表于2020年05月15日 19:09:08 1169 4
直达本楼层的链接
楼主
显示全部楼层
[技术干货] 0x02 LiteOS_Lab仓库组件详解--Driver(上)

摘要:本贴详解driver层,相信大家肯定有疑问,为啥我不说完AT层再说driver层?因为AT层就是通过调用driver层中的API来操作串口的,所以不得不在AT层的详解中先把driver层说了。

image.png

编程的抽象思想


        我们在接到一个客户的需求时,帮客户制作一份程序,大家有没有想过“我能不能把这个客户需要的代码下次卖给其他客户,节约自己的工作量”。要如何才能将刚刚我们提出的想法实现呢?这就需要通过编程抽象思想来实现。

        比如说有两位客户,客户①的需求:我要在8051内核单片机上实现一个AT收发的框架;客户②的需求:我要在stm32f103单片机上实现一个AT收发的框架。那么这是我们就可以运用代码接口抽象化、模块化、降低模块与模块间耦合的思想了,将这份代码从下到上分为3层:

①串口层:用于初始化串口,将上层的数据通过串口发出。将收到的数据提供给串口,我们在更换不同类型的单片机只需要从新写这一层。

②driver层:也就是驱动层,它提供读、写、初始化等等函数给上层模块使用,上层模块操作串口和操作内存卡的感觉是一样的,通过这层完全屏蔽了底层设备的差异,所有能进行读写的设备都可以通过driver层注册并提供读、写函数即可,在更换底层设备,例如将内存卡换成u盘只要适配好内存卡硬件层和u盘硬件层,其余均不用改动。

③AT层:AT指令大家都知道,语法为AT+命令=参数、AT+命令?等,我们实现好这一层,只要调用相应函数并传入命令和参数就可以使用了。

所以所上面两位客户的需求可以共用相同的AT层和driver层,只用分别添加uart硬件层即可,就算客户后面改需求我们也不怕了,改硬件我们只需要修改硬件层(uart层)即可。

driver层中的结构体


        使用结构体编程运用了面向对象的思想,把一个设备想象成对象,他有自己的属性和行为,属性对应各种变量(设备名、只读、只写等),行为对应各种函数(初始化函数、读函数、写函数等)。

typedef struct
{
    const char            *name;      //device driver name                            设备名称
    los_driv_op_t         *op;        //device operate functions                      设备能进行的操作
    void                  *pri;       //private data,will be passed to op functions   设备的私有数据,例如MCU有多个串口,使用同样的IO方法,用pri表示操作的不同的串口
    uint32_t               flag;      //flags, like O_RDONLY O_WRONLY O_RDWR          设备的数据:只读、只写、读写
}os_driv_para_t;

/* 该结构体中存储设备具有的能力的函数指针,意思就是设备可以具有以下能力,如果没有该能力或者不使用该能力可以不实现相关函数 */
typedef struct
{
    fn_devopen    open;   //triggered by the application    打开设备
    fn_devread    read;   //triggered by the application    读
    fn_devwrite   write;  //triggered by the application    写
    fn_devclose   close;  //triggered by the application    关闭设备
    fn_devioctl   ioctl;  //triggered by the application    暂时忽略该函数,因为不使用
    fn_devseek    seek ;  //triggered by the application    查看设备是否存在
    fn_devinit    init;   //if first open,then will be called    设备初始化
    fn_devdeinit  deinit; //if the last close, then will be called    设备去除初始化
}los_driv_op_t;

/* 这个是一个driver层中私有的结构体,用于实现一个环形链式数据结构(链表),待会我画一张图给大家看 */
struct driv_cb
{
    void                  *nxt;              //add the deice to device list    //结构体指针,用于指向后一个结构体
    void                  *pri;              //bsp developed para              //结构体指针,用于指向前一个结构体
    const char            *name;             //the device name                 //设备名
    int                    flagmask;         //copy from the register          //
    const los_driv_op_t   *op;               //operation method                //该设备具有的行为(初始化、读、写等)
    unsigned int           drivstatus;       //show the state here like init or something like this    //设备的状态
    los_dev_t              devlst;           //the open list,support the multi open                    //设备被哪些地方打开过,类似于互斥锁
    //following member used for the debug
    size_t            total_write;            //how many data has been sent                            //向这个设备中一共写入了多少数据
    size_t            total_read;             //how many data has received                             //向这个设备中一共接收了多少数据
    size_t            opencounter;            //reference counter                                      //这个设备被打开的次数类似于信号量的作用
    unsigned int      errno;                  //the last errno has happend                             //最近的一个错误
};

/* 该结构体用于维护所以在driver层中注册过的设备 */
typedef struct
{
    osal_mutex_t                    lock;      //used to lock the devlst            这是一个互斥锁,可以防止多个进行同时访问driver层导致出错
    struct driv_cb                 *drivlst;   //all the dev will be added to the list    //通过该结构体指针遍历整个结构体链表可以找出所有注册过的设备的详细信息
    unsigned int                    drivnum;    //总的注册设备数量
}los_driv_module;

driver层自身初始化


/*******************************************************************************
function     :the device module entry
parameters   :
instruction  :call this function to initialize the device module here
              load the static init from section os_device
*******************************************************************************/
/* 主要目的就是为了注册那些我们采用宏定义注册的设备。可以往下看“设备注册函数”,再回头看这里 */
bool_t  los_driv_init()
{
    bool_t ret = false;

    ret = osal_mutex_create(&s_los_driv_module.lock);
    if(false == ret)
    {
        goto EXIT_MUTEX;
    }

    //load all the static device init
    osdriv_load_static();

EXIT_MUTEX:
    return ret;
}

设备注册函数


设备注册有两种方式,一种是直接调用注册函数注册,另一种是调用宏定义让设备的合适的时候自动注册,先说第一种。

/*******************************************************************************
function     :bsp developer use this function to add a device to the system
parameters   :
instruction  :NULL if failed else return the device handle
*******************************************************************************/

los_driv_t los_driv_register(os_driv_para_t *para)    //事先我们需要实现好一个os_driv_para_t结构体,其中包含设备的各项参数,并将指针传入该函数中
{
    struct driv_cb  *driv = NULL;

    if((NULL == para->name)||(NULL == para->op))    //如果参数有误,直接返回
    {
        goto EXIT_PARAS;
    }

    driv = osal_malloc(sizeof(struct driv_cb));    //为这个设备结构体分配内存
    if(NULL == driv)
    {
        goto EXIT_MALLOC;
    }
    (void) memset(driv,0,sizeof(struct driv_cb));    //清空内存中的数据

    //do the member initialize
    driv->name = para->name;                        //注册设备名、设备相关操作函数、设备私有数据等等
    driv->op = para->op;
    driv->pri = para->pri;
    driv->flagmask = para->flag;

    /* 将这个设备添加到管理设备用的结构体链表中 */
    //add it to the device list if no device with the same name exsit
    if(false == osal_mutex_lock(s_los_driv_module.lock))        //加锁,防止其他进行同时访问,导致出错
    {
        goto EXIT_MUTEX;
    }

    if(NULL != __driv_match(para->name))                        //查询是否该设备已经添加过
    {
        goto EXIT_EXISTED;
    }

    driv->nxt = s_los_driv_module.drivlst;                    //挂载指针,向链表的头部添加
    s_los_driv_module.drivlst = driv;                         //挂在指针,向链表的头部添加
    (void) osal_mutex_unlock(s_los_driv_module.lock);         //解锁,恢复访问

    s_los_driv_module.drivnum++;                              //设备总数加1

    return driv;


EXIT_EXISTED:
    (void) osal_mutex_unlock(s_los_driv_module.lock);
EXIT_MUTEX:
    osal_free(driv);
    driv = NULL;
EXIT_MALLOC:
EXIT_PARAS:
    return driv;
}

/* 这是自动注册的宏定义,调用之后它会在合适的时候自动帮我们注册设备,推荐使用这个 */
#define OSDRIV_EXPORT(varname,drivname,operate,pridata,flagmask)      \
    static const os_driv_para_t varname __attribute__((used,section("osdriv")))= \
    {                           \
        .name   = drivname,     \
        .op     = operate,      \
        .pri    = pridata,      \
        .flag   = flagmask,     \
    }

/* 该函数初始化driver层时被调用,用于注册我们采用OSDRIV_EXPORT宏注册的设备,
    本质原理就是我们在调用宏注册设备时,会通过链接器将这个设备结构体放到一
    块特定的空间中,这个函数就去这块特定的空间中寻找需要被注册的设备结构体。
    并进行注册操作
 */
static void osdriv_load_static(void){

    os_driv_para_t *para;
    unsigned int num = 0;
    unsigned int i = 0;
#if defined (__CC_ARM)    //you could add other compiler like this
    num = ((unsigned int)&osdriv$$Limit-(unsigned int)&osdriv$$Base)/sizeof(os_driv_para_t);
    para = (os_driv_para_t *) &osdriv$$Base;
#elif defined(__GNUC__)

    para = (os_driv_para_t *)&__osdriv_start;
    num = ((unsigned int )(uintptr_t)&__osdriv_end - (unsigned int)(uintptr_t)&__osdriv_start)/sizeof(os_driv_para_t);
#endif
    for(i =0;i<num;i++)
    {
        (void) los_driv_register(para);
        para++;
    }

    return;
}

/* 这组宏定义就是定义了一块空间用于存放我们采用OSDRIV_EXPORT宏注册的设备结构体 */
#ifdef __CC_ARM /* ARM C Compiler ,like keil,options for linker:--keep *.o(osdriv)*/
    extern unsigned int osdriv$$Base;
    extern unsigned int osdriv$$Limit;

#elif defined(__GNUC__)
    extern unsigned int __osdriv_start;    //这个符号在os.ld文件中
    extern unsigned int __osdriv_end;
#else
    #error("unknown compiler here");
#endif

设备取消注册函数


/*******************************************************************************
function     :bsp developer use this function to remove a device from the system
parameters   :
instruction  :if the device has been refered,then will fail here
*******************************************************************************/
/* 该函数本质上就是将我们需要取消注册的设备从设备链表中删除 */
bool_t los_driv_unregister(const char *name)
{
    struct driv_cb *tmp = NULL;
    struct driv_cb *pre = NULL;
    bool_t         ret = false;
    if(NULL == name)
    {
        return ret;
    }

    if(osal_mutex_lock(s_los_driv_module.lock))
    {
        tmp = s_los_driv_module.drivlst;        //将设备链表的指针赋值给tmp
        pre = NULL;
        while(NULL != tmp)
        {
            if( 0 == strcmp(name,tmp->name))    //找到需要被删除的设备结构体
            {
                break;
            }
            /* 找到找到需要被删除的设备结构体 */
            pre = tmp;                            //遍历结构体
            tmp = tmp->nxt;
        }
        if(NULL != tmp)  //find the device here
        {
            if(pre == NULL)                    //说明要被删除的是第一个
            {
                s_los_driv_module.drivlst = tmp->nxt;
            }
            else
            {
                pre->nxt = tmp->nxt;
            }

            osal_free(tmp);            //释放这块内存
            ret = true;

            s_los_driv_module.drivnum--;    //设备总数减一
        }

        (void) osal_mutex_unlock(s_los_driv_module.lock);
    }

    return ret;
}

PS:还有三个函数没说,读设备函数、写设备函数、发现设备函数,由于帖子有字数限制,所以我放到下一篇帖子中来说。

举报
分享

分享文章到朋友圈

分享文章到微博

采纳成功

您已采纳当前回复为最佳回复

Jasonchenbj

发帖: 132粉丝: 10

发消息 + 关注

发表于2020年05月15日 22:01:02
直达本楼层的链接
沙发
显示全部楼层

github中 LiteOS   和 LiteOS_Lab 有啥区别?

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

樊心昊

发帖: 172粉丝: 139

发消息 + 关注

发表于2020年05月16日 02:51:13
直达本楼层的链接
板凳
显示全部楼层

回复:Jasonchenbj 发表于 2020-5-15 22:01 github中 LiteOS 和 LiteOS_Lab 有啥区别?

你好,请参考这篇帖子https://bbs.huaweicloud.com/forum/thread-53401-1-1.html

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

Jasonchenbj

发帖: 132粉丝: 10

发消息 + 关注

发表于2020年06月23日 06:50:10
直达本楼层的链接
地板
显示全部楼层

感谢分享

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

我是卤蛋

发帖: 121粉丝: 292

级别 : 版主,版块专家

发消息 + 关注

发表于2020年06月26日 11:12:27
直达本楼层的链接
5#
显示全部楼层

感谢分享,持续学习~

点赞 评论 引用 举报

游客

富文本
Markdown
您需要登录后才可以回帖 登录 | 立即注册

结贴

您对问题的回复是否满意?
满意度
非常满意 满意 一般 不满意
我要反馈
0/200