最近想研究一个U盘,而后顺便熟悉一下USB协议。由于USB协议比较复杂, 经常使用的复杂外设除了WiFi,Ethernet,SDIO和USB这些就是USB了,学习USB的时候确定要拿一个东西下手,因此简单了解以后准备了下列资料:html
1.《圈圈教你玩USB》。这本书比较经典,可是拿的芯片比较老了,在淘宝上搜索发现这本书配套的PDIUSBD12有现成的独立模块使用。由于手头上正好有一个STM32开发板,能够用来对接它。STM32以前用来对接红外线后来被闲置(参考这篇http://www.cnblogs.com/tanhangbo/p/4740702.html), 这时候正好用上。linux
左边是淘宝买的模块,中间是圈圈的书本,右边是买书送的PCB,没有用上。ubuntu
2.《USB开发大全》,《linux那些事儿》这两本书买了备用windows
3.USB一些相关资料包,和USB的系列spec,算做储备。前期已经了解了一些USB的基本架构,学习起来能够再回顾下。架构
4.USB的抓包软件,抓包硬件。抓包软件抓到的包可能会漏掉一些东西,并且会加上一些系统调用,使用会模糊不清。而抓包软件能够抓到真实的包,就像wifi的sniffer同样,必定要看到真实的包才有标准答案,否则学习起来会迷路。USB的抓包器相对于逻辑分析仪比较昂贵,购买USB1.1协议的便可。学习
5.准备好你的耐心,由于这里涉及多个协议,这是最重要的。ui
由于选择了STM32做为主体而不是51单片机,因此代码不免须要移植一下,这是一个初期的障碍,可是评估起来问题不大。由于须要移植的是GPIO相关的东西,因此只要注意一些细节就能够了。移植过程当中遇到的一个比较大的问题是,D12并口的GPIO不要给它拉高,不然通信会出错。我以前使用的时候发现一直没有通(经过并口读取D12的硬件ID),后来加入了逻辑分析仪以后居然能够了,因而想到GPIO的硬件问题,最后把上拉改为悬空就能够了。由于STM32的GPIO的API有点绕,因此要仔细对待。spa
移植到STM32的HAL代码:3d
/** Init GPIOA as input or Output */ void GPIOA_Init(int input) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; if (input == 1) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_IN_FLOATING else GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO_Mode_Out_PP GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStructure); } void HAL_GPIO_Data_AS_Input() { //GPIO_DeInit(GPIOA); GPIOA_Init(1); } void HAL_GPIO_Data_AS_Output() { //GPIO_DeInit(GPIOA); GPIOA_Init(0); } u8 HAL_GPIO_Read_Data() { return GPIO_ReadInputData(GPIOA) & 0xFF; } /** GPIOB_6 = RD -- Output GPIOB_7 = WR -- Output GPIOB_8 = INT -- Input GPIOB_9 = A0 -- Output */ void GPIOB_Init() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /** Output */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOB, &GPIO_InitStructure); /** Input */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOB, &GPIO_InitStructure); } /** Write one byte to GPIOA0~GPIO7 */ void HAL_GPIO_Write_Data(unsigned char byte) { #if 0 GPIO_WriteBit(GPIOA, GPIO_Pin_0, (byte & 0x01)); GPIO_WriteBit(GPIOA, GPIO_Pin_1, (byte & (0x01 << 1))); GPIO_WriteBit(GPIOA, GPIO_Pin_2, (byte & (0x01 << 2))); GPIO_WriteBit(GPIOA, GPIO_Pin_3, (byte & (0x01 << 3))); GPIO_WriteBit(GPIOA, GPIO_Pin_4, (byte & (0x01 << 4))); GPIO_WriteBit(GPIOA, GPIO_Pin_5, (byte & (0x01 << 5))); GPIO_WriteBit(GPIOA, GPIO_Pin_6, (byte & (0x01 << 6))); GPIO_WriteBit(GPIOA, GPIO_Pin_7, (byte & (0x01 << 7))); #else uint16_t data = GPIO_ReadOutputData(GPIOA) & 0xFF00; //read high 8 byte GPIO_Write(GPIOA, (data|byte)); //write high&low byte #endif } void HAL_GPIO_Write_RD(int val) { GPIO_WriteBit(GPIOB, GPIO_Pin_6, val); } void HAL_GPIO_Write_WR(int val) { GPIO_WriteBit(GPIOB, GPIO_Pin_7, val); } void HAL_GPIO_Write_A0(int val) { GPIO_WriteBit(GPIOB, GPIO_Pin_9, val); } int HAL_GPIO_Read_INT() { return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8); } void HAL_GPIO_Init() { GPIOA_Init(0); GPIOB_Init(); } /** PA0 ~ PA7 test PASS */ void GPIOA_Test() { int i = 0; GPIOA_Init(0); while (1) { printf("Read = %08x\r\n", GPIOB, HAL_GPIO_Read_Data()); //GPIOA_Write_Byte(0x55); //delay_ms(1000); ///GPIOA_Write_Byte(0xaa); os_sleep(1); } }
圈圈代码里面的中文太多了,出于强迫症给它整理格式、加入自定义的打印而且加上颜色。code
使用的时候HID和USB-TTL的例子都没问题,后面的U盘里面就有一些问题了,这里有一些SCSI命令的响应是没有的,手动给它添加上去,试过ubuntu14.04和win10都没有通,常常卡在文件系统的Read(10)这里,猜想是速度上不去,后面更换STM32自带的USB试试看。目前成功的例子是linux2.6内核的板子成功给它挂载上去而且读取到里面的TXT文件。
对于U盘来讲有包括SCSI指令和FAT文件系统层的东西,这些都须要去了解,巩固。
1.USB协议自己主要是要实现一些基本的描述符,告诉主机本身是谁,有哪些参数(相似SDIO的CCCR/FBR/CIS),实际的数据传输流程是为上层协议作准备的。一开始可能对USB数据包自己比较感兴趣,后续更多的问题存在于应用协议。
2.对于U盘来讲要在USB基础上了解SCSI和FAT文件系统协议协议格式,重头戏都是在这里的。
3.USB可玩性比较高,能够实现标准的HID或者自定义单各类设备。好比USB网卡和CH340这些模块都是自定义的vendor specific设备。
目前仍是有一些问题须要解决的
1.将圈圈的代码写死二进制的方式所有改掉(包括USB的标准请求/SCSI指令/FAT格式),换成结构体的表示,手头上有linux内核代码和ecos的代码,能够移植过来。光跑别人的代码可能理解不深,本身重写一遍才能深入理解。
2.在STM32上跑通U盘的例子。目前STM32有了现成的U盘历程,能够先移植过来看看效果,若是效果很差就将D12的代码移植过来。
3.在windows和ubuntu上面调通U盘
4.STM32做为SDIO host,作一个USB读卡器。
5.总结整理文档,造成本身的USB代码库
6.准备好耐心一步步积累吧