sam9x60 USB驱动适配(二)
前文最后说到代码能编译却不能跑。通过单步调试的笨方法查看(毕竟是刚添加usb host layer就跑不动,首先就该怀疑它的初始化嘛)
发现是运行到tpl list初始化的for循环时出错了,第二次运行的时候就卡死了。暂停调试就直接跳到了data_abort_irq_handler。
for 循环的data abort 错误,当然是考虑越界了。那就看看递增上限 hostObj->nTPLEntries 的配置。
/* Initialize all drivers in TPL List */
for ( tplEntryCount = 0 ; tplEntryCount < hostObj->nTPLEntries ; tplEntryCount++ )
{
tplEntry = &(hostObj->tpl[tplEntryCount]);
(( USB_HOST_CLIENT_DRIVER *)tplEntry->hostClientDriver)->initialize( tplEntry->hostClientDriverInitData );
}
其实就在for循环前面就有一句赋值行为。
hostObj->tpl = hostInit->tplList ;
hostObj->nTPLEntries = (int8_t)hostInit->nTPLEntries;
而 hostInit 的赋值在这整个初始化函数的开头
hostInit = ( USB_HOST_INIT * ) initData ;
而这个initdata的传参,是在initialize.c文件里面给进去的。
sysObj.usbHostObject0 = USB_HOST_Initialize (( SYS_MODULE_INIT *)& usbHostInitData );
usbHostInitData 这个东西就是罪魁祸首。真正应该先做检查再确定数量的地方是这个结构体的成员 nTPLEntries 。
const USB_HOST_INIT usbHostInitData =
{
.nTPLEntries = 3, // 错误:应该是1
.tplList = (USB_HOST_TPL_ENTRY *)USBTPList,
.hostControllerDrivers = (USB_HOST_HCD *)&hcdTable
};
在 d:/project/FT5426G/src/config/default/usb_host_init_data.c 文件中,
定义了一个TPL(Target Peripheral List)数组 USBTPList[3] ,但实际上只初始化了第一个元素(用于MSD类驱动):
static const USB_HOST_TPL_ENTRY USBTPList[3] =
{
TPL_INTERFACE_CLASS_SUBCLASS_PROTOCOL
(
USB_MSD_CLASS_CODE,
USB_MSD_SUBCLASS_CODE_SCSI_TRANSPARENT_COMMAND_SET,
USB_MSD_PROTOCOL,
NULL,
USB_HOST_MSD_INTERFACE
),
// 其余两个元素未初始化
};
可以理解,tpl的全称是 Target Peripheral List 。这个结构体里一般会添加msd(U盘),cdc(usb接口外设)等不同类型的usb接口设备的驱动入口。
但是你直接默认3个不做检查,却又要让我自己添加3个就离谱,万一我不止3个那不还是报错。
而且我回头在mplab的mcc里面找了下,tpl entry数量直接灰色的3,不让更改。what?我还得在代码文件里改,以后每次mcc有所改动并生成代码的时候,都得手动跳过这个冲突以免覆盖。
这就导致在前面提到的 for 循环中,当 tplEntryCount 增加到1和2时,程序尝试访问未初始化的内存,从而触发了 data_abort 异常。
离谱的是它这里就没被编译器发现,这个xc32编译器真是神一下鬼一下的。
总结
TPL是USB主机层的设备驱动管理机制,通过USBTPList数组定义支持的设备类型和驱动程序,nTPLEntries控制有效条目数量,tplFLAGS调整匹配行为。这些组件协同工作,确保USB主机能正确识别和加载适合的设备驱动程序。
之前的问题正是由于nTPLEntries与实际初始化的条目数量不匹配,导致主机层尝试访问未初始化的内存,触发了data_abort异常。
学习
TPL的工作流程
- 初始化阶段 :USB主机层读取TPL数组和nTPLEntries值
- 设备连接 :当USB设备插入时,主机层获取设备描述符
- 驱动匹配 :遍历TPL列表,使用每个条目的匹配条件(结合tplFLAGS)与设备属性比较
- 驱动加载 :找到匹配项后,调用对应驱动的initialize函数进行初始化
- 设备使用 :匹配的驱动程序接管设备的通信和功能实现
1. TPL(Target Peripheral List)的核心概念
TPL是USB主机层的设备驱动注册表,用于定义:
- 系统支持哪些类型的USB设备
- 每种设备类型应使用哪个客户端驱动程序
- 驱动程序的匹配条件和初始化参数
2. USBTPList(TPL数组)
USBTPList是TPL的具体实现,是一个 USB_HOST_TPL_ENTRY结构体数组 ,每个元素代表一个驱动匹配规则.
每个条目包含:
- 设备匹配条件(通过宏定义设置)
- 驱动初始化数据指针
- 客户端驱动程序接口指针
3. nTPLEntries(TPL条目数量)
nTPLEntries是 USB_HOST_INIT结构体 的字段,用于指定TPL列表中 实际有效的条目数量。
这个值直接控制主机层初始化驱动的循环次数。
4. tplFLAGS(TPL匹配标志)
tplFLAGS是 USB_HOST_TPL_ENTRY结构体 中的一个位字段,用于控制设备匹配行为。
这些标志决定了主机层如何匹配设备,例如:
- ignoreClass = true :匹配时忽略设备类字段
- driverType :指定使用VID/PID还是类/子类/协议进行匹配
typedef struct
{
union
{
struct { uint16_t vid; uint16_t pid; } vid_pid;
struct { uint8_t classCode; uint8_t subClassCode; uint8_t protocolCode; } cl_sc_p;
} id;
uint16_t pidMask;
struct {
uint8_t driverType :1; // 驱动类型(VID/PID或类/子类/协议)
uint8_t ignoreClass :1; // 是否忽略设备类
uint8_t ignoreSubClass :1; // 是否忽略子类
uint8_t ignoreProtocol :1; // 是否忽略协议
// ...其他标志
} tplFlags;
void * hostClientDriverInitData;
void * hostClientDriver;
} USB_HOST_TPL_ENTRY;

浙公网安备 33010602011771号