1 VxBus下驱动的架构

1.1 WorkBench3.0的认识

Workbench3.0是VxWorks 6.x的集成开发环境,而VxWorks 5.5是采用Tornado2.2来进行开发的。Workbench3.0相比Tornado2.2来说提供了更为强大的功能,在Workbench3.0可以根据需要创建各种工程,常用的有以下几种VxWorks Image Projects,Boot Loader/BSP Projects,VxWorks Real-time Process Projects,VxWorks Downloadable Kernel ModuleProjects.

在进行驱动开发时需要创建VxWorks Image Projects。基于VxBus架构模型驱动在开发环境Workbench3.0中是以组件的形式体现的,这样的话就方便开发人员根据需要进行驱动的添加,重新编译VxWorks image后就可将驱动编进内核。开发人员只需将精力集中在驱动源码的编写上了。VxWorks5.5中没有VxBus架构,它的驱动的调用直接在BSP中的sysLIib.c中调用即可,这样开发的驱动可移植性不够好,当更换BSP时,又得重新移植。

关于Workbench3.0 的使用详见wr_workbench_vxworks_users_guide_3.0.pdf。

1.2 VxBus下驱动的结构以及编译

驱动源码结构

VxBus下驱源码主要由以下几个文件组成:
  • README
  • Makefile
  • driverName.cdf
  • driverName.dr
  • driverName.dc
  • driverName.c

1 README

关于驱动的说明文件

2 Makefile

驱动的编译规则

3 driverName.cdf

驱动的描述文件,里边包括的该驱动依赖的组件,驱动的位置,父目录以及子目录,在Workbench3.0下的说明信息等,很重要,若不正确,在Workbench3.0无法添加。

4 driverName.dr

向VxBus进行注册的函数。

5 driverName.dc

向VxBus进行注册的函数的声明。

6 driverName.c

驱动的核心文件,驱动源码基本结构如下(用CAN1000t.c来举例)

A)芯片自身的数据结构,在驱动开发的过程中逐步完善,必需有这个结构VXB_DEVICE_ID,以便同VxBu进行通信。

typedef struct can1000tHwmonCtrl
{

VXB_DEVICE_ID _pDev;

*;

} CAN1000T_HWMON_CTRL;

B)三个基本的必需的函数,这几个函数在系统初始化的不同阶段进行调用

LOCAL voidcan1000tHwmonInstInit(VXB_DEVICE_ID);
LOCAL void can1000tHwmonInstInit2(VXB_DEVICE_ID);
LOCAL voidcan1000tHwmonInstConnect(VXB_DEVICE_ID);

C)驱动所提供的方法结构声明

LOCAL device_method_tcan1000tHwmon_methods[] =
{

DEVMETHOD(HwmonSendData,can1000tHwmonSendData),

DEVMETHOD(HwmonRecvData,can1000tHwmonRecvData),

DEVMETHOD_END

};

D)三个基本的必需的函数的声明

LOCAL struct drvBusFuncs can1000tHwmonFuncs=
{

can1000tHwmonInstInit,    /* devInstanceInit */

can1000tHwmonInstInit2,   /* devInstanceInit2 */

can1000tHwmonInstConnect  /* devInstanceConnect */

};

E)向VxBus注册的结构,包含了以上C,D。

LOCAL DRIVER_REGISTRATIONcan1000tHwmonDevRegistration =
{

NULL,                             /* pNext */

VXB_DEVID_DEVICE,       /* devID */

VXB_BUSID_PLB,          /* busID = Processor Local Bus */

VXBUS_VERSION_3,        /* busVer 1 */

"can1000t",         /* drvName */

&can1000tHwmonFuncs,     /* pDrvBusFuncs */

can1000tHwmon_methods,   /* pMethods */

NULL                    /* devProbe */

};

F)向VxBus注册的函数

void can1000tHwmonRegister(void)
{

vxbDevRegister((struct vxbDevRegInfo*)&can1000tHwmonDevRegistration);

}
1.3 VxBus下驱动的编译

install:指vxWorks的安装目录

1) 在cmd下运行

wrenv.exe -p vxworks-6.6

2) 进入如下目录

cd installDir\vxworks-6.x\target\config\comps\src\hwif

3) 运行下列命令 make vxbUsrCmdLine.c

注意:若已经存在vxbUsrCmdLine.c的话则手工删除掉

4) 进入下列目录

cd installDir\vxworks-6.x\target\config\comps\vxWorks

5) 运行下列命令

Del CxrCat.txt

6) 运行下列命

make

7) 进入如下目录

cd installDir\vxworks-6.x\target\3rdparty\vendor\driver

8) 运行下列命令

make CPU=cpuName TOOL=tool

注意:cpuName是所选BSP对应处理器的型号,如PPC32;

tool是对应的编译工具,如sfdiab 和 gnu

至此就可以在对应的库目录下看见刚编译生成的库件

installDir\vxworks-6.x\target\lib\ppc\PPC32\common\下

此时在workbench中创建image工程,在kernel configuration下就可以看到刚才编译的

当然也可以把这些命令写成一个脚本直接点运行即可。也可以进入所写的驱动目录下单独编译该驱动命令如:

makeCPU=PPC32 TOOL=sfidab

2 Can控制器的驱动编写

2.1 Can总线的认识

CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由研发和生产汽车电子产品著称的德国BOSCH公司开发了的,并最终成为国际标准(ISO118?8)。是国际上应用最广泛的现场总线之一。在我看来,CAN其实就是一种通信的模式。使用如下

2.2 SJA1000T的认识

SJA1000T是一款CAN控制器的芯片,要编写它的驱动,首先必须熟悉它的工作模式。有两种工作模式:BasicCAN(兼容PCA82C20)和PeliCAN。通过阅读SJA1000T的DataSheet,来熟悉它的寄存器的布局,以及它的工作模式。

2.3 驱动的编写
2.3.1 驱动模板的选择

VxBus下的驱动按类进行区分,选择一个合适的驱动模板很重要,我选择的是adt7461(温度传感器)的驱动,然后进行修改,删除一些无用的代码,最终只有一个空的架子,然后按前面讲的方法进行编译,编译通过后,在WorkBench3.0中选择合适的BSP创建VxWorks Image Projects,在第三方驱动组件下添加所编写的驱动组件,若不能添加检查驱动中的driverName.cdf文件是否正确。若正确后则可以添加该组件如下图所示

2.3.2 添加驱动所需的资源

打开相应的BSP中的文件hwconf.c添加驱动所需的资源,寄存器基地址和中断号,以便在驱动初始化时使用添加如下:

1 中断资源

#ifdefDRV_HWMON_ZKHXET_CAN1000T

          { EPIC_VEC_EXT_IRQ0,         "can1000t",      0,     0 },

          { EPIC_VEC_EXT_IRQ0,         "can1000t",      1,     0 },

#endif/*DRV_HWMON_ZKHXET_CAN1000T*/

2 寄存器等资源

#ifdefDRV_HWMON_ZKHXET_CAN1000T

const structhcfResource can1000t1Resources[] = {

{VXB_REG_BASE,   HCF_RES_INT,    { (void *)(0xEE000000) } },

{"irq",            HCF_RES_INT,   {(void *)EPIC_VEC_EXT_IRQ0}  },

{ "busno",         HCF_RES_INT,    { (void *)(0) } },

};

#definecan1000t1Num NELEMENTS(can1000t1Resources)

#endif /*DRV_HWMON_ZKHXET_CAN1000T */

3 验证驱动是否加载成功

修改hwconf.c文件后,在WorkBench3.0创建Boot Loader/BSP Projects,选择相应BSP进行编译,编译完成后,更新要添加驱动板子的boot。加载编译了驱动的内核映像,加载后,输入VxBusShow命令就可以看到加载后的驱动。若没有看到,检查驱动中设备名字是否正确。

2.3.3 驱动功能的完善

经过以上步骤,驱动的基本架构已经基本搭建完成,下来就是根据具体的CAN控制器芯片来按部就班的实现相应的功能。

2.3.3.1 SJA1000T的初始化总体来说SJA1000T的初始话比较简单,主要是如下步骤

  1. 通过配置模式寄存器(MOD)进入复位模式,因为进入复位模式后,一些寄存器才允许配置。
  2. 配置时钟分频寄存器,选择PeliCAN模式,根据具体的电路,是否使能时钟等。
  3. 配置模式寄存器,选择合适工作模式,是单滤波还是双滤波等。
  4. 配置验收代码/屏蔽寄存器,配置一些初始值,一般配置全部接收,这个允许用户配置。
  5. 配置总线定时器,选择合适的波特率,这些允许用户重新配置。
  6. 通过配置模式寄存器(MOD)进入正常模式,此时SJA1000T就可以工作了。

详见驱动中的函数: CanControllerInit(VXB_DEVICE_IDpDev); 2.3.3.2 SJA1000T发送数据

发送数据采用查询模式。每次发送之前查询状态寄存器的发送缓冲器状态是否释放。注意发送前允许用户配置一下帧格式的信息,如扩展帧还是标准帧,是否是远程帧,帧ID等信息。

详见驱动中的函数: CanControllerTransmit(VXB_DEVICE_IDpDev, unsigned char *TXdata, int len); 2.3.3.3 SJA1000T接收数据

驱动的结构中分配存储数据的buffer,buffer包括帧的全部信息。

1) 中断处理

当有接收中断时,中断处理函数负责将数据存到分配的buffer中,相应的标志进行记录。

详见驱动中的函数:

Can1000tHwmonRecvInt(VXB_DEVICE_IDpDev); 2) 接收数据

当应用程序读取数据时,驱动只需将buffer中的数据传给应用层,相应的标志进行记录。

详见驱动中的函数:

can1000tHwmonRecvData(VXB_DEVICE_IDpDev, int* id, int* ext_flag, int * rtr_flag, int * time_stamp, int* data, intbuf_len); 2.3.3.4 SJA1000T的配置

CAN和串口不一样,并不是单纯的发送接收数据,它有自己的帧格式,如ID等,这些都需要用户设置,用户根据所需的数据设置相应的屏蔽码,确定哪些想接收,哪些想屏蔽,所以驱动必须提供用户可配置函数接口。如ioctl函数

详见驱动中的函数:

hwmonIoctl(HWMON_DEV_HDR*pDevHdr, int func, int arg);

至此为止CAN控制器SJA1000T的驱动完成,下来进入关键的一步调式。

3 驱动调试

3.1 调试环境的搭建

调试要用到USBCAN-2I调试工具,CAN发送和接收的应用程序。连接好硬件电路,就可以调试了。

3.2 调试中用到的方法

以下是我用到的方法:

  1. 在驱动中创建全局变量,然后在应用程序调用时打印。系统加载驱动时,一些信息没办法看见,可以先将保存到全局变量中,最后在打印,这样就可知道了。
  2. 利用logMsg进行打印。
  3. 示波器的使用,在没有任何输出时,可以使用示波器在电路级联的地方进行波形的检测。
3.3 调试过程中遇到问题<、h5>

遇到的问题有以下几个

  1. WorkBench3.0中无法添加驱动的组件,最后检查是driverName.cdf中的文件目录不正确。
  2. 正确的:_CHILDREN FOLDER_3RD_DRIVERS
  3. 驱动加载后,在命令行用VxBusShow查看驱动的信息时,最后检查是驱动文件中的名字不匹配。