行测 Appuim环境搭建 细胞因子 js快速排序 linux创建文件 Yarn jsp bash plot 网络营销推广 Pure CSS vue案例 河南普通话报名 js键值对数组 linux查看mysql进程 linux超级用户 android调试工具 matlab中axis python相对路径怎么写 python基础教程 python中re模块 python程序代码 python处理json文件 java最新框架 java环境部署 java目录 java系统时间 java框架学习 beatedit 跳一跳脚本 网络文件服务器 天正建筑2007 万能播放器电脑版 重复文件查找软件 js动态添加元素 vs2012中文旗舰版下载 qq农场图标 Mapper 迅雷单机游戏下载 网易云听歌识曲电脑版
当前位置: 首页 > 学习教程  > 编程语言

sylixos下spi总线驱动框架

2020/8/11 19:54:39 文章标签:

下面以imx6q处理器自带spi总线驱动为例,讲解sylixos下spi总线驱动框架。

1. 驱动入口位置

要为系统添加一个spi总线,可以在bsp工程中,直接集成到系统镜像中,也可以通过内核模块动态添加至系统。
如果是集成到系统镜像中,则代码应位于bspInit.c文件的halBusInit函数中开始调用。如果是通过内核模块实现,则自然是在module_init函数中调用。

2. 初始化 spi 组件库

spi总线驱动需要调用的第一个函数应当是系统api函数 API_SpiLibInit,该函数初始化 spi 组件库,为spi总线驱动和spi设备驱动做支持。

static VOID  halBusInit (VOID)
{
    /*
     * SPI 总线初始化
     */
    API_SpiLibInit();                                                   /*  初始化 spi 组件库           */
    spiBusCreate(0);
    spiBusCreate(1);
}

查看API_SpiLibInit函数源码,我们可以看到,里面只是创建了一个二值信号量,这个信号量会保护每次spi总线的调用不被重入。

/*********************************************************************************************************
** 函数名称: API_SpiLibInit
** 功能描述: 初始化 spi 组件库
** 输 入  : NONE
** 输 出  : ERROR CODE
*********************************************************************************************************/
LW_API  
INT  API_SpiLibInit (VOID)
{
    if (_G_hSpiListLock == LW_OBJECT_HANDLE_INVALID) {
        _G_hSpiListLock = API_SemaphoreBCreate("spi_listlock", 
                                               LW_TRUE, LW_OPTION_WAIT_FIFO | LW_OPTION_OBJECT_GLOBAL,
                                               LW_NULL);
    }
    
    if (_G_hSpiListLock) {
        return  (ERROR_NONE);
    } else {
        return  (PX_ERROR);
    }
}

3.创建各个通道的spi总线

初始化 spi 组件库后,就可以创建各个通道的spi总线了,这里调用的是spiBusCreate函数,其有一个参数可以指定是要创建哪个通道的总线。spiBusCreate函数是在driver/ecspi/ecspi.c文件中实现的。如果SOC中只有一个通道的spi,该函数也可以设计为无参数的。

4.创建spi总线的系统接口

接下来看spiBusCreate函数的具体实现。

/*********************************************************************************************************
** 函数名称: spiBusCreate
** 功能描述: 创建 spi 总线并获取驱动程序
** 输    入: iCh         通道号
** 输    出: 错误码
*********************************************************************************************************/
INT  spiBusCreate (INT  iCh)
{
    CHAR           spiBusName[64];
    LW_SPI_FUNCS  *hSpiFunc;
    static  SPI_CHANNEL_ST  spiChannels[ECSPI_CHAN_COUNT] = {
        [0] = {
            .funcs = {
                .SPIFUNC_pfuncMasterXfer = spiMasterXfer,
                .SPIFUNC_pfuncMasterCtl  = spiMasterCtl,
            },
            .iCh          = 0,
            .uPhyBase     = ECSPI1_BASE_ADDR,
            .uVmmBase     = ECSPI1_BASE_ADDR,
            .uVector      = IMX_INT_ECSPI1,
            .uBaudRate    = CFG_DEFAULT_BAUDRATE,
            .uOption      = LW_SPI_M_CPHA_0 | LW_SPI_M_CPOL_1,
        },
        [1] = {
            .funcs = {
                .SPIFUNC_pfuncMasterXfer = spiMasterXfer,
                .SPIFUNC_pfuncMasterCtl  = spiMasterCtl,
            },
            .iCh          = 1,
            .uPhyBase     = ECSPI2_BASE_ADDR,
            .uVmmBase     = ECSPI2_BASE_ADDR,
            .uVector      = IMX_INT_ECSPI2,
            .uBaudRate    = CFG_DEFAULT_BAUDRATE,
            .uOption      = LW_SPI_M_CPHA_0 | LW_SPI_M_CPOL_1,
        },
        [2] = {
            .funcs = {
                .SPIFUNC_pfuncMasterXfer = spiMasterXfer,
                .SPIFUNC_pfuncMasterCtl  = spiMasterCtl,
            },
            .iCh          = 2,
            .uPhyBase     = ECSPI3_BASE_ADDR,
            .uVmmBase     = ECSPI3_BASE_ADDR,
            .uVector      = IMX_INT_ECSPI3,
            .uBaudRate    = CFG_DEFAULT_BAUDRATE,
            .uOption      = LW_SPI_M_CPHA_0 | LW_SPI_M_CPOL_1,
        },
        [3] = {
            .funcs = {
                .SPIFUNC_pfuncMasterXfer = spiMasterXfer,
                .SPIFUNC_pfuncMasterCtl  = spiMasterCtl,
            },
            .iCh          = 3,
            .uPhyBase     = ECSPI4_BASE_ADDR,
            .uVmmBase     = ECSPI4_BASE_ADDR,
            .uVector      = IMX_INT_ECSPI4,
            .uBaudRate    = CFG_DEFAULT_BAUDRATE,
            .uOption      = LW_SPI_M_CPHA_0 | LW_SPI_M_CPOL_1,
        },
        [4] = {
            .funcs = {
                .SPIFUNC_pfuncMasterXfer = spiMasterXfer,
                .SPIFUNC_pfuncMasterCtl  = spiMasterCtl,
            },
            .iCh          = 4,
            .uPhyBase     = ECSPI5_BASE_ADDR,
            .uVmmBase     = ECSPI5_BASE_ADDR,
            .uVector      = IMX_INT_ECSPI5,
            .uBaudRate    = CFG_DEFAULT_BAUDRATE,
            .uOption      = LW_SPI_M_CPHA_0 | LW_SPI_M_CPOL_1,
        },
    };

    if (iCh >= ECSPI_CHAN_COUNT) {
        return  (PX_ERROR);
    }

    spiInit(&spiChannels[iCh]);

    hSpiFunc = (LW_SPI_FUNCS  *)&spiChannels[iCh];
    sprintf(spiBusName, "/bus/spi/%d", iCh);

    return  (API_SpiAdapterCreate(spiBusName, hSpiFunc));
}

spiBusCreate函数中定义了spi总线子类实体集合,对相应通道进行了初始化操作,最后调用系统接口创建spi总线。

创建spi总线的真正接口其实是系统api函数API_SpiAdapterCreate,它需要两个参数:

  • CPCHAR pcName:spi总线名称,即shell命令buss显示的名称,也是spi设备驱动创建时需要制定的总线名称。
  • PLW_SPI_FUNCS pspifunc:spi操作函数组,是SPI总线底层操作的函数集合,也是spi总线对象子类的父类。

5.SPI总线底层操作的函数集合

sylixos的spi驱动框架定义了SPI总线底层操作的函数集合,也就是PLW_SPI_FUNCS 结构体,该结构体定义如下:

/*********************************************************************************************************
  SPI 总线传输函数集
*********************************************************************************************************/

typedef struct lw_spi_funcs {
    INT                    (*SPIFUNC_pfuncMasterXfer)(PLW_SPI_ADAPTER   pspiadapter,
                                                      PLW_SPI_MESSAGE   pspimsg,
                                                      INT               iNum);
                                                                        /*  适配器数据传输              */
    INT                    (*SPIFUNC_pfuncMasterCtl)(PLW_SPI_ADAPTER   pspiadapter,
                                                     INT               iCmd,
                                                     LONG              lArg);
                                                                        /*  适配器控制                  */
} LW_SPI_FUNCS;
typedef LW_SPI_FUNCS        *PLW_SPI_FUNCS;

sylixos驱动层对spi总线api接口的调用,最终都会间接调用到PLW_SPI_FUNCS 里面注册的函数。

SPIFUNC_pfuncMasterXfer回调函数是必须要实现的,它负责控制硬件来完成spi传输。它有三个参数:

  • PLW_SPI_ADAPTER pSpiAdapter :SPI总线适配器指针,所有的spi总线设备信息都从这里获取。
  • PLW_SPI_MESSAGE pspimsg :SPI设备需要传输的消息结构体首地址指针,描述spi传输信息,是一个结构体数组的首地址,至于数组有多少个成员,也就是有多少个spi传输帧由iNum 给出。
  • INT iNum :为传输的消息个数。
  • 执行失败时返回错误号(为负值),执行成功返回实际完成的消息个数。

基于SPIFUNC_pfuncMasterXfer的功能和输入输出参数,该函数的大体结构也是固定的,如下:

/*********************************************************************************************************
** 函数名称: spiMasterXfer
** 功能描述: spi 传输函数
** 输    入: pSpiAdapter     spi 适配器
**           pSpiMsg         spi 传输消息
**           iNum            消息数量
** 输    出: 完成传输的消息数量
*********************************************************************************************************/
static INT spiMasterXfer (PLW_SPI_ADAPTER  pSpiAdapter, PLW_SPI_MESSAGE  pSpiMsg, INT  iNum)
{
    INT  i;

    SPI_CHANNEL_ST *pSpi = (SPI_CHANNEL_ST *)pSpiAdapter->SPIADAPTER_pspifunc;

    for (i = 0; i < iNum; i++) {
        //TODO  此处是进行spi传输的实际寄存器操作

        if (pSpiMsg->SPIMSG_pfuncComplete) {                            /*  传输完成上层回调函数        */
            pSpiMsg->SPIMSG_pfuncComplete(pSpiMsg->SPIMSG_pvContext);
        }
        pSpiMsg++;
    }

    return  (iNum);
}

SPIFUNC_pfuncMasterCtl回调函数是用于操作一些spi属性的,可以不实现,注册为空函数或空指针。
调用格式和一般的ioctrl函数相似,操作对象是SPI总线适配器。系统已经定义了设置波特率的命令(LW_SPI_CTL_BAUDRATE),驱动开发者根据具体情况也可以定义自己需要要的命令。

/*********************************************************************************************************
** 函数名称: spiMasterCtl
** 功能描述: spi 控制函数
** 输    入: pSpiAdapter     spi 适配器
**           iCmd            spi 命令
**           lArg            spi 参数
** 输    出: 错误号
*********************************************************************************************************/
static INT  spiMasterCtl (PLW_SPI_ADAPTER  pSpiAdapter, INT  iCmd, LONG  lArg)
{
    SPI_CHANNEL_ST *pSpi = (SPI_CHANNEL_ST *)pSpiAdapter->SPIADAPTER_pspifunc;

    switch (iCmd) {
    case LW_SPI_CTL_BAUDRATE://设置波特率
        pSpi->uBaudRate = lArg;
        spiConfig(pSpi);
        break;
    default:
        break;
    }

    return  (ERROR_NONE);
}

6.定义spi总线对象子类

往往一款SOC芯片中的spi总线控制器会有相同的多个,他们的寄存器和硬件特性是完全一样的,只有寄存器基址,向量号,使用的管脚等不一样,具体应用时波特率和总线模式等也可能不一样,但他们肯定都使用同一个驱动。相同的驱动程序在服务不同的通道操作时就需要基于不同的spi总线对象了。

定义spi总线对象其实就是把驱动中需要记录的,各通道间差异化的数据或方法集中到一个结构体的,具体都要有哪些成员是要看具体的硬件特性和驱动实现方式的。需要注意的是,第一项必须是LW_ SPI_ FUNCS 结构体对象,即要以LW_SPI_FUNCS作为具体spi总线对象的父类。

因为在spiBusCreate函数在注册spi总线时,要把spi总线对象作为父类(LW_SPI_FUNCS)传入驱动框架,后面SPIFUNC_pfuncMasterXfer 函数被调用时传入的LW_SPI_FUNCS对象其实就是注册时转入的。所以定义spi总线对象要修饰为静态变量,SPIFUNC_pfuncMasterXfer 和SPIFUNC_pfuncMasterCtl 函数中也可以将LW_SPI_FUNCS强转为具体的spi总线对象来使用。

本驱动中定义的spi总线对象如下:

/*********************************************************************************************************
  spi 通道结构定义
*********************************************************************************************************/
typedef struct {
    LW_SPI_FUNCS        funcs;                                          /*  必须为第一个                */
    INT                 iCh;                                            /*  通道号                      */
    UINT                uPhyBase;                                       /*  物理基地址                  */
    UINT                uVmmBase;                                       /*  虚拟基地址                  */
    UINT                uVector;                                        /*  中断向量号                  */

    UINT                uClock;                                         /*  模块时钟频率                */
    UINT                uBaudRate;                                      /*  目标波特率                  */
    UINT                uOption;                                        /*  硬件选项                    */
    UINT8              *pTxBuf;                                         /*  当前发送缓存                */
    UINT8              *pRxBuf;                                         /*  当前接收缓存                */
    UINT                uiLength;                                       /*  收发字节长度                */
    UINT                uiIndex;                                        /*  当前收发字节索引            */

    spinlock_t          slLock;                                         /*  自旋锁                      */
    LW_OBJECT_HANDLE    hSemBTx;                                        /*  发送完成同步信号量          */
} SPI_CHANNEL_ST;

总结

对于spi如何进行初始化,如何进行传输操作,是否使用中断这都是spi硬件特性和操作方式的问题,每个应用情景都不一样,这里就不再祥说了。

抛去所有可能不需要的操作,spi总线的实现步骤只包含以下几步:

  1. 调用API_SpiLibInit 函数,初始化 spi 组件库。
  2. 调用API_SpiAdapterCreate函数,创建spi总线适配器,需要传入总线名称和LW_SPI_FUNCS 结构体对象。
  3. 需要提前实例化LW_SPI_FUNCS 结构体对象,其中SPIFUNC_pfuncMasterXfer 函数必须实现。
  4. SPIFUNC_pfuncMasterXfer 函数的基本框架如下,其中TODO部分是单次spi传输需要实现的寄存器操作。注意参数pSpiAdapter->SPIADAPTER_pspifunc指向的地址就是调用API_SpiAdapterCreate数传入的pspifunc,所以可以将其强转为实际的子类对象。

本文链接: http://www.dtmao.cc/news_show_100238.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?