vxWorks6.6下基于VxBus架构的Can控制器驱动编写 |
|
|
目录
1 VxBus下驱动的架构...1
1.1 WorkBench3.0的认识...1
1.2 VxBus下驱动的结构以及编译...1
1.3 VxBus下驱动的编译...4
2 Can控制器的驱动编写...5
2.1 Can总线的认识...5
2.2 SJA1000T的认识...5
2.3 驱动的编写...6
2.3.1 驱动模板的选择...6
2.3.2 添加驱动所需的资源...6
2.3.3 驱动功能的完善...7
3 驱动调试...9
3.1 调试环境的搭建...9
3.2 调试中用到的方法...9
3.3 调试过程中遇到问题...9
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。
驱动源码结构
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);
}
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
CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由研发和生产汽车电子产品著称的德国BOSCH公司开发了的,并最终成为国际标准(ISO118?8)。是国际上应用最广泛的现场总线之一。在我看来,CAN其实就是一种通信的模式。使用如下
SJA1000T是一款CAN控制器的芯片,要编写它的驱动,首先必须熟悉它的工作模式。有两种工作模式:BasicCAN(兼容PCA82C20)和PeliCAN。通过阅读SJA1000T的DataSheet,来熟悉它的寄存器的布局,以及它的工作模式。
VxBus下的驱动按类进行区分,选择一个合适的驱动模板很重要,我选择的是adt7461(温度传感器)的驱动,然后进行修改,删除一些无用的代码,最终只有一个空的架子,然后按前面讲的方法进行编译,编译通过后,在WorkBench3.0中选择合适的BSP创建VxWorks Image Projects,在第三方驱动组件下添加所编写的驱动组件,若不能添加检查驱动中的driverName.cdf文件是否正确。若正确后则可以添加该组件如下图所示
打开相应的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命令就可以看到加载后的驱动。若没有看到,检查驱动中设备名字是否正确。
经过以上步骤,驱动的基本架构已经基本搭建完成,下来就是根据具体的CAN控制器芯片来按部就班的实现相应的功能。
总体来说SJA1000T的初始话比较简单,主要是如下步骤
1) 通过配置模式寄存器(MOD)进入复位模式,因为进入复位模式后,一些寄存器才允许配置。
2) 配置时钟分频寄存器,选择PeliCAN模式,根据具体的电路,是否使能时钟等。
3) 配置模式寄存器,选择合适工作模式,是单滤波还是双滤波等。
4) 配置验收代码/屏蔽寄存器,配置一些初始值,一般配置全部接收,这个允许用户配置。
5) 配置总线定时器,选择合适的波特率,这些允许用户重新配置。
6) 通过配置模式寄存器(MOD)进入正常模式,此时SJA1000T就可以工作了。
详见驱动中的函数:
CanControllerInit(VXB_DEVICE_IDpDev);
发送数据采用查询模式。每次发送之前查询状态寄存器的发送缓冲器状态是否释放。注意发送前允许用户配置一下帧格式的信息,如扩展帧还是标准帧,是否是远程帧,帧ID等信息。
详见驱动中的函数:
CanControllerTransmit(VXB_DEVICE_IDpDev, unsigned char *TXdata, int len);
驱动的结构中分配存储数据的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);
CAN和串口不一样,并不是单纯的发送接收数据,它有自己的帧格式,如ID等,这些都需要用户设置,用户根据所需的数据设置相应的屏蔽码,确定哪些想接收,哪些想屏蔽,所以驱动必须提供用户可配置函数接口。如ioctl函数
详见驱动中的函数:
hwmonIoctl(HWMON_DEV_HDR*pDevHdr, int func, int arg);
至此为止CAN控制器SJA1000T的驱动完成,下来进入关键的一步调式。
调试要用到USBCAN-2I调试工具,CAN发送和接收的应用程序。连接好硬件电路,就可以调试了。
以下是我用到的方法:
7) 在驱动中创建全局变量,然后在应用程序调用时打印。系统加载驱动时,一些信息没办法看见,可以先将保存到全局变量中,最后在打印,这样就可知道了。
8) 利用logMsg进行打印。
9) 示波器的使用,在没有任何输出时,可以使用示波器在电路级联的地方进行波形的检测。
遇到的问题有以下几个
1) WorkBench3.0中无法添加驱动的组件,最后检查是driverName.cdf中的文件目录不正确。
正确的:_CHILDREN FOLDER_3RD_DRIVERS
2) 驱动加载后,在命令行用VxBusShow查看驱动的信息时,最后检查是驱动文件中的名字不匹配。