USB的“JoyStickMouse”源代码分析01<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
一、c源文件和头文件
1、c文件的组织
我们一般用C语言编程的时候都比较讲究模块化、层次化,以及数据和操作分开的原则。
模块化最明显的表现是我们把常用的、某一个具体功能实现封装在一个函数中,我们所要操作的数据以参数的方式提供给函数,函数通过代码处理过后,再把结果回馈个调用者。
层次化表现在比较复杂的功能实现都要分几层来实现,有时候也是为了提高可移植性。比如文件系统的代码实现大致要分这么几层:
磁盘操作层实现磁盘扇区的读取、写入、控制;
文件分配管理函数和目录项操作函数;
文件的打开、读取、写入函数,目录的建立、删除函数等;
用户应用操作函数,如文件的查找、具体数据的写入等等。
一个具体USB功能的实现也能分成几层,比如我们的Stm32 JoyStickMouse,我们也可以这样分层:
硬件操作层:寄存器操作和内存操作。
协议通用层:设备枚举的控制传输实现,对所有类协议都一样。
具体协议层:比如HID协议的描述符,类特定请求实现。
这样一划分,一个具体的功能实现就需要比较多的C源文件。
2、c文件和h文件
一个c文件一般是几个函数组成,这几个函数可能一部分存在依存关系,而有一些函数要对外引出,而同时它还可能要引用外部函数。解决这个相互关联的是“包含头文件”。
一个c文件很多时候都存在一个与它同名的“h文件”,对c文件的意义主要有以下几个:
(1)提供常量定义,#define。
(2)提供一些类型定义,为了实现c文件数据类型的编译器无关性,一般都要将该编译器提供的数据类型重新定义,用tpyedef来实现。
(3)结构体、联合体、枚举类型的定义。
(4)带参数的宏定义。
(5)外部函数声明。
(6)外部变量声明。
(7)本身实现函数的声明:起始这个声明对c文件本身意义不大,主要是其它c文件要使用本c文件实现的函数时,包含同名“h文件”,意义更清楚一些。
3、h文件的相互包含
头文件对c源文件的意义比较清晰。但实际有时候“头文件”也会存在依存关系,相互包含。
比如有个头文件使用数据类型“u8”,那么它必须先包含定义这个数据类型的头文件。
有时候为了让c文件包含的头文件数目不至于过多,一般采用这样的方法:用一个h文件包含所有相关的头文件,然后每个c文件包含这个头文件就行了。这个以usb的库函数组织最为典型。
编程时,用到库函数的源文件只要包含“stm<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />32f10x_lib.h”就行了,USB操作库函数也采用了类似的方式。
二、USB函数库分析
1、usb_regs.h
(1)寄存器定义
#define CNTR ((volatile unsigned *)(RegBase + 0x40))
我觉得这是c语言指针的魅力所在,通过这样的定义可以直接操作寄存器。*CNTR = Value。特别是含有多个寄存器的设备,可以将首地址结构体指针化,通过指针间接访问结构体,实际上就访问了各个寄存器。
(2)标志位、屏蔽位定义
#define ISTR_CTR (0x8000) /* Correct TRansfer (clear-only bit) */
#define CLR_CTR (~ISTR_CTR) /* clear Correct TRansfer bit */
有了这个定义,我们可以以比较明了的方式对相关位进行操作。
(3)寄存器操作宏定义
#define _SetCNTR(wRegValue) (*CNTR = (u16)wRegValue)
这样定义以后,用户对寄存器的操作调用宏就行了,意义清晰。
#define _SetEPTxStatus(bEpNum,wState) {\
register u16 _wRegVal; \
_wRegVal = _GetENDPOINT(bEpNum) & EPTX_DTOGMASK;\
/* toggle first bit ? */ \
if((EPTX_DTOG1 & wState)!= 0) \
_wRegVal ^= EPTX_DTOG1; \
/* toggle second bit ? */ \
if((EPTX_DTOG2 & wState)!= 0) \
_wRegVal ^= EPTX_DTOG2; \
_SetENDPOINT(bEpNum, _wRegVal); \
} /* _SetEPTxStatus */
宏可以代表一个变量,也可以是一个表达式,也可以是一个代码块。
(4)声明外部变量
extern volatile u16 wIstr; /* ISTR register last read value */
(5)对外声明同名c文件实现的函数
u16 GetCNTR(void);
它在这里使用了“u16”类型定义,所以必须先定义。
2、usb_regs.c
主要是调用宏,实现寄存器操作,但我看上层函数也很少调用这些函数,直接就使用宏了。
tengjingshu_112148725 2010-4-23 09:19