当前位置:首页 >> 信息与通信 >>

P (郭天祥)学ARM和学单片机一样简单


学ARM和学单片机一样简单

第一讲: TX44B0系统概况

学ARM和学单片机一样简单

主要内容:
一、开发平台硬件资源 二、开发平台软件资源及文档 三、开发平台硬件安装 四、开发平台软件安装 五、linux开发环境的安装

学ARM和学单片机一样简单
主板硬件资源:

r />1、核心控制芯片S3C44B0。 2、2M字节FLASH SST39VF1601。 3、8M字节SDRAM HY57V641620。 4、160个引脚全部引出,方便外扩实验。 5、AMS1117-3.3和AMS1117-2.5 电压转换芯片。 6、大小端及总线位数选择跳线。 7、系统复位按键。 8、14PIN JTAG调试下载接口。 9、主板10M晶振。

学ARM和学单片机一样简单 扩展板硬件资源:
1、海量存储器:16M,NAND Flash; 2、键盘:4 键数字小键盘; 3、显示:320×240 象素、26万色LCD,四线电阻式触摸屏; 4、USB 接口:HOST:CH375B USB 1.1 标准DEVICE:D12 芯片,USB 1.1 标准; 5、串口:2 路、最高波特率115200 bps; 6、网络接口:RTL8019,10M 以太网;

学ARM和学单片机一样简单
7、A / D:ARM 自带8 路10 位A /D,满量程2.5V 8、LED: 3 个,共阴极; 9、音频输出:IIS 总线,CS4334芯片,44.1KHz 音频; 10、配套JTAG 仿真器、电源适配器、通信线缆若干。 11、LM35温度传感器 12、SD卡接口:一个; 13、片内实时时钟; 14、PS2鼠标键盘接口1个;

学ARM和学单片机一样简单 二、开发平台软件资源及文档
1) ADS1.20 安装程序; 2) 支持 ADS1.20 和 SDT2.51 的 JTAG调试软件H-JTAG 3) 烧写FLASH的工具软件 FLASHPGM2.2.4以及适用于 S3C44B0X的 OCD 配置文件; 4) 工具软件dnw.exe、tftp.exe; 5) LCD图片转换和字模转换工具软件; 6) u-boot-1.1.1源代码;

学ARM和学单片机一样简单
7)TX44B0测试程序(ADS1.20 的项目文件,包含全部源代码 ),具有如下功能测试及源码: 内存 SDRAM读写自测试 外部中断4567测试 Hold模式测试 D12测试 IDE硬盘测试 USBhost测试 PS2鼠标测试 看门狗测试 独立按键测试 Slow模式测试 板上简单led测试 Nandflash测试 LCD和触摸屏测试 PS2键盘测试 IIS音频测试

内部ad测试采集LM35温度传感器 SD卡测试

学ARM和学单片机一样简单
8) UC/OS-II V2.52测试(ADS1.20 的项目文件,10个实 验,提供全部源码); 9) UCLINUX 源码包和编译器等; 10) TX44B0原理图(pdf格式); 11) TX44B0开发板使用手册(pdf格式) ;

学ARM和学单片机一样简单
三、开发平台硬件安装
TX44B0开发平台是一个完整的开发系统,硬件安装比较简单。 1、电源适配器为系统提供5V 稳压直流电源,输出端插入平台电源插 口。板上有电源开关,请注意不要带电插拔CPU 核心板、通信电缆 2、用并行电缆连接PC 机并口和简易JTAG 仿真器,仿真器的JTAG 电缆 插入核心板的JTAG 接口。 3、用串口电缆连接PC 机串口和平台的UART0 接口,主要用于监视平台 运行情况。 4、需要时用USB 电缆连接PC 机主USB 口和平台的从USB 设备口,用于 USB通信。 5、需要时用对等网线连接PC 机网口和平台网口,用于网络通信。

学ARM和学单片机一样简单
四、开发平台软件安装
1、编译环境的安装 TX44B0 嵌入式开发平台支持ARM SDT2.5 和ADS1.2 两个编译环 境,下面我带着大家安装编译环境; 2、超级终端 超级终端是Windows 自带程序,在附件中的通讯目录下。如果没有 ,则在控制面板中选择添加或删除程序,选择添加/删Windows 组件 ,选择附件和工具一项,单击详细信息,可以在通讯一项中找到超级 终端,确定添加后即可。这里超级终端的作用是使平台与PC 机进行 通信,在PC 机上监视系统状态或控制系统运行。 3、JTAG 驱动程序的安装

学ARM和学单片机一样简单

五、linux开发环境的安装
1、VMWARE安装 2、 red hat linux安装 3、 minicom配置及使用 4、交叉编译工具安装

学ARM和学单片机一样简单

第二讲:
? 开发板测试程序演示 ? 启动代码分析

学ARM和学单片机一样简单

一、开发板测试程序演示

学ARM和学单片机一样简单

二、启动代码分析
学习ARM,都不可避免地要了解系统引导,引导程序是系统加 电后运行的第一段软件代码,本章將以S3C44B0X为目标来分 析引导程序 与 BOOT 相关硬件:FLASH ROM 系统启动以后, S3C44B0X的编址方式如下图:

学ARM和学单片机一样简单

学ARM和学单片机一样简单

二、启动代码分析
和S3C4510B?同,S3C44B0X处?器?支持Memory Remap. 从上面的地址映射图可以看出 BANK0 的起始地址为 0x0, 44B0X在系统复位的时候, 处理器从0x0 地址开始执行程 序.所以,44B0X系统加电后运行的第一段软件代码就是映 射在BANK0的 ROM里存放的代码.

学ARM和学单片机一样简单

二、启动代码分析
S3C44B0X 自身不具有 ROM,因此必须外接 ROM 器件来 存放掉电后仍需要保存的代码和数据,比如 Boot ROM image,那么怎样挂接BOOT ROM? 在S3C44B0X芯片上有 8个管脚nGCS[7:0]用来做外接 RO M芯片选择,如果 ROM 芯片的使能管脚 nCE 和S3C44B0X 的管脚 nGCS0 相连(见下图), 就相当于把 ROM映射在 S3C44B0X 的bank0地址空间(见下面的电路图).

学ARM和学单片机一样简单

学ARM和学单片机一样简单 二、启动代码分析
我们要做的就是: 把FLASH ROM如上图接在 BANK0 把BOOT image烧 在 FLASH ROM 这样一个裸设备就可启动. Boot ROM 的数据总线宽度(Data Bus Width)由S3C44B0X的管脚 OM[1:0]的取值来决定:

学ARM和学单片机一样简单

学ARM和学单片机一样简单 BOOT的主要功能:
建立中断异常矢量表 关Watchdog Timer 屏蔽所有的中断 初始化PLL和时钟 初始化RAM 初始化堆栈 复制RW到DRAM,将Zi段清零 跳转到C语言程序,开始第二阶段的初始化和系统引导

学ARM和学单片机一样简单

初始化分析:
看门狗计时器 晶振电路 锁相环(PHASE-LOCKED-LOOP)PLL

学ARM和学单片机一样简单

关闭看门狗计时器watch dog timer
Register: WTCON Address: 0x01D30000 ldr r0,=WTCON ;watch dog disable ldr r1,=0x0 str r1,[r0]

学ARM和学单片机一样简单

屏蔽所有中断
为中断提供服务通常是 OS 设备驱动程序的责任. 因此在 Boot Loader 的执行全过程可以不必响应任何中断. Register: INTMSK Address: 0x01E0000C ldr r0,=INTMSK ldr r1,=0x07ffffff ;all interrupt disable str r1,[r0]

学ARM和学单片机一样简单

配置时钟
相关寄存器: Register :PLLCON(PLL configuration Register) Address: 0x01D80000 Register: CLKCON(Clock generator control) Address: 0x01D80004 Register: LOCKTIME (PLL lock time count ) Address: 0x01D8000C

学ARM和学单片机一样简单

设置主频
PLLCON Register用来配置主频,可以通过修改 这个Register的内容达到修改配置主频的目的, 下面这个代码把主频配置为60MHz

学ARM和学单片机一样简单

主频计算公式:
PLLCON[19:12]= M_DIV( Main divider control) PLLCON[9:4]= P_DIV(Pre-divider control) PLLCON[1:0]= S_DIV(Post divider control) Fpllo = (m * Fin) / (p * 2**s) m = (MDIV + 8), p = (PDIV + 2), s = SDIV 代入上面的值: m=0x34+8=0x3c,p=0x3+2=5,s=1 Fpllo=(0x3c*10)/(5*2)=0x3c=60MHZ

学ARM和学单片机一样简单

给外设提供时钟
S3C44B0X的电源管理模块包含五个模式: Normal mode Slow mode Idle mode Stop mode SL Idle mode for LCD,

学ARM和学单片机一样简单

初始化RAM
CPU使用一组专用的特殊功能寄存器来控 制外部存储器的读/写操作,通过对该组特 殊功能寄存器编程,可以设定外部数据总线 宽度,访问周期,定时的控制信号(例如RAS 和CAS)等参数.

学ARM和学单片机一样简单
这主要通过设置13 个从1c80000(BWSCON)开始的特 殊寄存器来设置. 0x1c80000为特殊寄存器的首地址. BWSCON(0x1c80000): 配置总线宽度 data Bus Width :8-bit,16-bit, 32-bit BANKCON0-7(0x1c80004-0x1c80020): 访问周期[10:8] Access cycle 存储器的类型[16:15](Bank6,Bank7) 定时Trcd的控制信号

学ARM和学单片机一样简单
;******************************************** ;* Set memory control registers * ;******************************************** ldr r0,=SMRDATA ldmia r0,{r1-r13} ldr r0,=0x01c80000 ; BWSCON Address stmia r0,{r1-r13}

学ARM和学单片机一样简单

初始化堆栈
ARM7TDMI支持6种Operation Mode User Mode FIQ Mode IRQ Mode Supervisor Mode Abort Mode Undefined Mode

学ARM和学单片机一样简单

如何定义堆栈地址
方法是改变状态寄存器(CPSR)内的状态位,使 处理器切换到不同的状态,然后给SP赋值。下面 为具体的代码分析:

学ARM和学单片机一样简单
复制RW到DRAM,将Zi段清零

学ARM和学单片机一样简单

学ARM和学单片机一样简单
到这里,BOOT程序的分析算是告一段落.我们基本 了解了BOOT的基本流程和相关知识,为下一步的 初始化和跳转到操作系统打下了一个坚实的基础.

学ARM和学单片机一样简单

第三讲:
一、S3C444B0X I/O口 二、中断的处理

学ARM和学单片机一样简单 S3C44B0X有71个通用可编程多功能 输入输出引脚,可分为以下7类端口:
两个8位输入输出端口(PortD和PortG) 两个9位输入输出端口(PortE和PortF) 一个10位输出端口(PortA) 一个11位的输出端口(PortB) 一个16位输入输出端口(PortC)

学ARM和学单片机一样简单
每个端口都可以通过软件设置来满足各种各样 的系统设置和设计要求。每个端口的功能通常 都要在主程序开始被定义。如果一个引脚的多 功能没有使用,那么这个引脚将被设置为I/O端 口。在引脚配置以前,需要对引脚的初始化状 态进行设定来避免一些问题的出现。

学ARM和学单片机一样简单

二、中断处理

学ARM和学单片机一样简单
中断源 外界有很多很多的中断源,S3C44B0X用一个中断 控制器来管理各种原因产生的中断。 如下图:

学ARM和学单片机一样简单
当外部中断源产生中断时,他会触发与中断控制器的那根信 号线, 中断控制器收到这个信号后会检查一下这个中断是 否被允许和是否被屏蔽,如果没有的话,就给她排一个处理 的优先级,当轮到这个中断时,触发ARM的中断信号通知 ARM内核.然后ARM内核就会去FLASH访问中断向量 表. ARM要求异常向量表必须放置在从0x0地址开始、 连续 8×4字节的空间内,每当一个中断发生后,处理器会自动 跳转到从0x0地址开始的异常中断矢量表中的某个位置( 由异常类型来确定)读取指令然后运行.

学ARM和学单片机一样简单

中断处理模式
S3C44B0X的中断控制器支持两个中断处理模式: 1、普通中断模式(NON-VECTORED INTERRUPT MODE) 2、向量中断模式(vectored interrupt mode) 我们可 以通过配置中断控制寄存器

学ARM和学单片机一样简单
INTCON(0x01E00000)选择使用那一个模式: INTCON[2]: This bit disables/enables vector mode for IRQ 1: = Non-vectored interrupt mode 0: = Vectored interrupt mode 下面代码可以配置为向量中断模式 INTCON=0x1 不同的中断模式会有不同的处理方式. 如图所示:

学ARM和学单片机一样简单

普通中断模式:

学ARM和学单片机一样简单 向量中断模式 :

学ARM和学单片机一样简单

和向量中断模式比较, 非向量中断多运行了一段 IsrIRQ代码来判断中断源,计算中断服务程序的起 始地址 BOOT为啥要这样作啊?

学ARM和学单片机一样简单
因为所有的非向量中断发生时,都跳到了0x18处: b HandlerIRQ BOOT不知道究竟发生了那个中断,所以为了找到 准确的中断服务程序,必须要用点功夫计算一下.

学ARM和学单片机一样简单
I_ISPR register记录了当前的中断,所以BOOT在 IsrIRQ代码中借用I_ISPR register和BOOT程序 中的数据段定义成功地获得中断服务程序的地址. IsrIRQ通过分析I_ISPR register 来判断中断源,然 后根据中断源算出中断服务程序的起始地址. 注:I_ISPR 记录了当前的中断源

学ARM和学单片机一样简单
主讲:冯 坤 出品:天祥电子 网址:www.txmcu.com
www.tx-power.com

学ARM和学单片机一样简单

第四讲:
一、AD温度采集显示 二、音频接口 三、NAND flash 读写

学ARM和学单片机一样简单 一、AD温度采集显示
1、A/D 转换器
A/D 转换器是模拟信号源和CPU 之间联系的接口,它 的任务是将连续变化的模拟信号转换为数字信号,以便计算 机和数字系统进行处理、存储、控制和显示。在工业控制和 数据采集及许多其他领域中,A/D 转换是不可缺少的。A/D 转换器有以下类型:逐位比较型、积分型、计数型、并行比 较型、电压-频率型,主要应根据使用场合的具体要求,按 照转换速度、精度、价格、功能以及接口条件等因素来决定 选择何种类型。常用的有以下两种:

学ARM和学单片机一样简单
1)双积分型的A/D 转换器
双积分式也称二重积分式,其实质是测量和比较两个积分的时间,一个 是对模拟输入电压积分的时间T0,此时间往往是固定的;另一个是以充电后 的电压为初值,对参考电源Vref反向积分,积分电容被放电至零所需的时间 T1。模拟输入电压Vi 与参考电压VRef 之比,等于上述两个时间之比。由于 VRef 、T0 固定,而放电时间T1 可以测出,因而可计算出模拟输入电压的大 小(VRef 与Vi 符号相反)。由于T0、VRef 为已知的固定常数,因此反向积分 时间T1 与输入模拟电压Vi 在T0 时间内的平均值成正比。输入电压Vi 愈高, VA 愈大,T1 就愈长。在T1 开始时刻,控制逻辑同时打开计数器的控制门 开始计数,直到积分器恢复到零电平时,计数停止。则计数器所计出的数字 即正比于输入电压Vi 在T0 时间内的平均值,于是完成了一次A/D 转换。

学ARM和学单片机一样简单
1)双积分型的A/D 转换器
由于双积分型A/D 转换是测量输入电压Vi 在T。时间 内的平均值,所以对常态干扰(串模干扰)有很强的抑制作 用,尤其对正负波形对称的干扰信号,抑制效果更好。 双积分型的A/D 转换器电路简单,抗干扰能力强,精 度高,这是突出的优点。但转换速度比较慢,常用的A/D 转换芯片的转换时间为毫秒级。因此适用于模拟信号变化 缓慢,采样速率要求较低,而对精度要求较高,或现场干 扰较严重的场合。例如在数字电压表中常被采用。

学ARM和学单片机一样简单
2)逐次逼近型的A/D 转换器
逐次逼近型(也称逐位比较式)的A/D 转换器,应用比积分型更为广 泛,主要由逐次逼近寄存器SAR、D/A 转换器、比较器以及时序和控 制逻辑等部分组成。它的实质是逐次把设定的SAR 寄存器中的数字量 经D/A 转换后得到电压Vc 与待转换模拟电压V。进行比较。比较时, 先从SAR 的最高位开始,逐次确定各位的数码应是“1”还是“0”,其工 作过程如下:

学ARM和学单片机一样简单
2)逐次逼近型的A/D 转换器
转换前,先将SAR 寄存器各位清零。转换开始时,控制逻辑电路 先设定SAR 寄存器的最高位为“1”,其余位为“0”,此试探值经D/A 转 换成电压Vc,然后将Vc 与模拟输入电压Vx 比较。如果Vx≥Vc,说 明SAR 最高位的“1”应予保留;如果Vx<Vc,说明SAR 该位应予清零 。然后再对SAR 寄存器的次高位置“1”,依上述方法进行D/A 转换和 比较。如此重复上述过程,直至确定SAR 寄存器的最低位为止。过程 结束后,状态线改变状态,表明已完成一次转换。最后,逐次逼近寄 存器SAR 中的内容就是与输入模拟量V 相对应的二进制数字量。显然 A/D 转换器的位数N 决定于SAR 的位数和D/A 的位数。转换结果能否 准确逼近模拟信号,主要取决于SAR 和D/A 的位数。位数越多,越能 准 确 逼 近 模 拟 量 , 但 转 换 所 需 的 时 间 也 越 长 。

学ARM和学单片机一样简单
2)逐次逼近型的A/D 转换器
逐次逼近式的A/D 转换器的主要特点是:
转换速度较快,在1—100/μs 以内,分辨率可以达18 位,特别适 用于工业控制系统。转换时间固定,不随输入信号的变化而变化。抗 干扰能力相对积分型的差。例如,对模拟输入信号采样过程中,若在 采样时刻有一个干扰脉冲迭加在模拟信号上,则采样时,包括干扰信 号在内,都被采样和转换为数字量,这就会造成较大的误差,所以有 必要采取适当的滤波措施。

学ARM和学单片机一样简单
2.A/D 转换的重要指标
1)分辨率(Resolution):
分辨率反映A/D 转换器对输入微小变化响应的能力,通常用数字 输出最低位(LSB)所对应的模拟输入的电平值表示。n 位A/D 能反应 1/2^n 满量程的模拟输入电平。由于分辨率直接与转换器的位数有关, 所以一般也可简单地用数字量的位数来表示分辨率,即n 位二进制数, 最低位所具有的权值,就是它的分辨率。值得注意的是,分辨率与精 度是两个不同的概念,不要把两者相混淆。即使分辨率很高,也可能 由于温度漂移、线性度等原因,而使其精度不够高。

学ARM和学单片机一样简单
2)精度(Accuracy)
精度有绝对精度(Absolute Accuracy)和相对精度(Relative Accuracy) 两种表示方法。

学ARM和学单片机一样简单
3)转换时间(Conversion Time)
转换时间是指完成一次A/D 转换所需的时间,即由发出启动转 换命令信号到转换结束信号开始有效的时间间隔。 转换时间的倒数称为转换速率。例如AD570 的转换时间为25us, 其转换速率为40KHz。

学ARM和学单片机一样简单
4)电源灵敏度(power supply sensitivity)
电源灵敏度是指A/D 转换芯片的供电电源的电压发生变化时,产 生的转换误差。一般用电源电压变化1%时相当的模拟量变化的百分 数来表示。

学ARM和学单片机一样简单

5)量程
量程是指所能转换的模拟输入电压范围,分单极性、 双极性两种类型。 例如: 单极性 量程为0~+5V,0~+10V,0~+20V; 双极性 量程为-5~+5V,-10~+10V。

学ARM和学单片机一样简单
6)输出逻辑电平
多数A/D 转换器的输出逻辑电平与TTL 电平兼容。在考虑数字 量输出与微处理的数据总线接口时,应注意是否要三态逻辑输出,是 否要对数据进行锁存等。

学ARM和学单片机一样简单
7)工作温度范围
由于温度会对比较器、运算放大器、电阻网络等产生影响,故只 在一定的温度范围内才能保证额定精度指标。一般A/D 转换器的工作 温度范围为(0~70C),军用品的工作温度范围为(-55~+125C)。

学ARM和学单片机一样简单
S3C440BX 芯片自带一个8路10 位A/D 转换器,该转换器 可以通过软件设置为Sleep 摸式,可以节电减少功率损失 ,最大转换率为500K,非线性度为正负1位.ARM 芯片与 A/D 功能有关的引脚为以下几个,其中AIN[7:0]为8 路模 拟采集通道,AREFT 为参考正电压,AREFB 为参考负 电压,AVCOM 为模拟共电压。

学ARM和学单片机一样简单
A/D 功能有关的引脚

在电路中,对上述引脚需要按照下图加上电容。

学ARM和学单片机一样简单
与AD 相关的寄存器主要是如下三个:
⑴ ADCPSR:采样比率寄存器。其地址和意义参见下表: 采样比率寄存器

通过设置该寄存器,可以设置采样率,最后得到的除数因子=2 (寄存器值+1)。

学ARM和学单片机一样简单
⑵ ADCCON:采样控制寄存器。其地址和意义参见下表:
采样控制寄存器

学ARM和学单片机一样简单
该寄存器的0 位是转换使能位,写1表示转换开始。1位是读操 作使能转换,写1表示转换在读操作时开始。2、3、4 位是通道号。5 位为睡眠模式设定,6 位为转换标志位(只读) 通过该寄存器设置A/D 转换开始可以参见下例: rADCCON=0x01(通道0开始转换)

⑶ ADCDAT:转换结果数据寄存器。
该寄存器的十位表示转换后的结果,全为1时为满量程2.5 伏。

学ARM和学单片机一样简单
4.AD 转换器在开发平台的接法如下:

学ARM和学单片机一样简单 二、音频接口
1、WAV 音乐格式

所谓在 Windwos 环境下,大部分的多媒体文件都依循着一种 结构来存放信息,这种结构称为“资源互换文件格式”( resources interchange file format , 简称 RIFF)。例如声音的 WAV 文件、视 频的 AVI 文件等等均是由此结构衍生出来的。RIFF 可以看作是 一种树状结构,其基本构成单位为 chunk ,犹如树状结构中的节 点,每个 chunk 由“辨别码”、“数据大小”及“数据”所组成。 辨别 码由4 个ASCII码所构成, 数据大小则标示出紧跟其后数据的长 度(单位为Byte),而数据大小本身也用掉 4 个 Byte,所以事实上一 个chunk 的长度为数据大小加 8。

学ARM和学单片机一样简单
一般而言,chunk 本身并不允许内部再包含 chunk,但有两种例 外,分别为以“RIFF”及“LIST”为辨别码的 chunk 。而针对这两 种 chunk,RIFF 又从原先的“数据”中切出 4个 Byte。这 4 个 Byte 称为“格式辨别码”,然而 RIFF 又规定文件中仅能有一个以 “RIFF”为辨别码的 chunk。只要是依循这一结构的文件, 我们均 称之为 RIFF 文档。这种结构提供了一种系统化的分类。

学ARM和学单片机一样简单
如果和 MS-DOS 文件系统作比较,“RIFF”chunk 就好比是一台硬 盘的根目录,其格式辨别码便是此硬盘的逻辑代码(C:或 D:), 而“LIST”chunk 即为其下的子目录,其他的 chunk 则为一般的 文件。至于在 RIFF 文件的处理方面,微软提供了相关的函数。 视窗下的各种多媒体文件格式就如同在磁盘上规定仅能放怎样 的目录,而在该目录下仅能放何种数据。

学ARM和学单片机一样简单
WAV 为 waveform(波形)的缩写。声音文件的结构如下表 所示, “RIFF”的格式辨别码为“WAVE”。整个文件由两个 chunk 所组成:辨 别码“fmt ”(注意,最后一个是空白字符!)及“data”。

学ARM和学单片机一样简单

WAV 文件结构

学ARM和学单片机一样简单
在“fmt”的 chunk 下包含了一个 PCMWAVE-FORMAT 数据结 构,其定义如下: typedef struct tWAVEFORMATEX { U16 wFormatTag; U16 nChannels; U32 nSamplesPerSec; U32 nAvgBytesPerSec; U16 nBlockAlign; U16 wBitsPerSample; U16 cbSize; }

学ARM和学单片机一样简单
其意义分别为:
wFormat Tag:记录着此声音的格式代号,例如 WAVE FORMAT- PCM,WAVE- FORAM- AD-PCM 等等。 nChannels:记录声音的通道数。 nSamplesPerSec:记录每秒取样数。 nAvgBytesPerSec:记录每秒的数据量。 nBlockAlign:记录区块的对齐单位。 wBitsPerSample:记录每个取样所需的位数。

学ARM和学单片机一样简单

“data”Chunk 包含着真正的声音数据。Windows目前 仅提供 WAVE- FORMAT- PCM 一种数据格式,所代 表的意义是脉冲编码调制(pulse code modulation)。

学ARM和学单片机一样简单
2.IIS 音频接口 IIS 音频接口总线共有四根线:串行数据输入 (IISDI) 、串行数据输出(IISDO) 、左右声道选 择(IISLRCK)和串行位时钟(IISCLK) 。由主控 设备提供 IISLRCK和 IISCLK。

学ARM和学单片机一样简单
三、NAND flash 读写
NAND FLASH存储器件有非易失、容量大、功耗低、易擦除等特点,这 里就涉及到FLASH的种类问题,NORFLASH和NANDFLASH区别在于: NOR的特点是芯片内执行,即应用程序可以直接在flash闪存内运行,不必再 把代码读到系统RAM中。但是工艺复杂,价格比较贵。NAND结构能提供极 高的单元密度,可以达到高存储密度,大存储容量,而且便宜。缺点,就是 无法寻址直接运行程序,只能存储数据。另外NAND FLASH 非常容易出现 坏区,所以需要有校验的算法。在NAND闪存中每个块的最大擦写次数是一 百万次,而NOR的擦写次数是十万次。NAND存储器除了具有10比1的块擦除 周期优势,典型的NAND块尺寸要比NOR器件小8倍。

学ARM和学单片机一样简单
FLASH内存有8位和16位两种组织形式,其I/O接口可用于控制命令和 地址的输入,也可用于数据的输入输出。其有如下特性:出厂时或者 使用过程中容易出现坏块;操作方式按页写按块擦除;写操作只能在 空或者已擦除的单元进行;块的擦除寿命有次数限制;块擦除时间与 页读时间相比十分长。

学ARM和学单片机一样简单

学ARM和学单片机一样简单

第五讲:
USB主从设备
USB是什么呢?

USB到底是什么呢?

学ARM和学单片机一样简单
USB是通用串行总线(Universal Serial Bus)的 简写,它已经有了10多年的历史了。USB协议 出现过的版本有USB1.0,USB1.1,USB2.0。

学ARM和学单片机一样简单
USB具有很多优点,例如即插即用,容易使用,方便携带 ,传输速度快,可扩展性强,标准统一,价格便宜等等。 目前流行的USB设备有移动硬盘,数码相机,MP3,U盘 ,USB鼠标、键盘、游戏杆,USB MIDI键盘,USB摄相 头,USB打印机,USB扫描仪,USB声卡,USB话筒, USB网卡,USB显示器,USB电话,具有USB口的各种仪 表仪器等等,只要是能跟电脑打交道的,就基本上可以通 过USB来实现,足见USB功能的强大。 当然USB也有一些缺点,例如传输距离短,开发、调试较 困难等等。

学ARM和学单片机一样简单
USB的拓扑模型

学ARM和学单片机一样简单
它主要包括了 USB 连接、USB host controller 和 USB device 三个部分。而 USB device 还包 括了 hub 和功能设备,也就是上图里的Func

学ARM和学单片机一样简单

什么是 USB controller?

学ARM和学单片机一样简单

那么 hub 是什么?

学ARM和学单片机一样简单
而USB 连接指的就是连接 device 和 host(或hub)的四线 电缆。电缆中包括 VBUS(电源线) 、GND(地线)还 有两根信号线。在USB OTG中,又增加了一种mini接口 ,使用的是5根线,比标准的USB多了一根身份识别(ID)线 。 USB 系统就是通过 VBUS 和 GND 向设备提供电源的 。USB设备可以使用主机提供的电源,也可以使用外接电 源供电。

学ARM和学单片机一样简单
USB的电气特性
5V电源线(Vbus),差分数据线负(D-),差分数据线正(D+), 地(Gnd)。USB使用的是差分传输模式,有两根数据线, 分别是D+和D-。

学ARM和学单片机一样简单
由于USB是主从模式,设备与设备之间、主机与主机之间 不能互连,为了解决这个问题,扩大USB的使用范围,就 出现了USB OTG(On The Go)。USB OTG的做法是,同 一个设备,在不同的场合下可以在主机或从机之间切换。 在USB1.0和USB1.1版本中,只支持1.5Mbps的低速(Low Speed)模式和12Mbps的全速(Full Speed)模式。在USB2.0 中,又加入了480Mbps的高速(High Speed)模式。

学ARM和学单片机一样简单
USB 通信最基本形式是通过 USB 设备里一个叫 endpoint ,而主机和 endpoint之间的数据传输是通过 pipe。 endpoint 就是通信的发送或者接收点,你要发送数据,那 你只要把数据发送到正确的端点那里就可以了。端点是有 方向的,从 usb 主机到设备称为out 端点,从设备到主机 称为 in 端点。

学ARM和学单片机一样简单
USB endpoint 有四种类型,也就分别对应了四种不同的数 据传输方式。它们是控制传输(Control Transfers) ,中 断传输(Interrupt Data Transfers) ,批量传输(Bulk Data Transfers),等时传输(Isochronous Data Transfers) 。控制传输用来控制对 USB设备不同部分的访问,通常用 于配置设备,获取设备信息,发送命令到设备,或者获取 设备的状态报告。总之就是用来传送控制信息的,每个 USB 设备都会有一个 endpoint 0的控制端点。

学ARM和学单片机一样简单
协议里规定,所有的 USB 设备必须具有端点 0,它可以作 为 in端点,也可以作为 out端点,USB 系统软件利用它来 实现缺省的控制管道,从而控制设备。端点也是限量供应 的,不是想有多少就有多少的,除了端点 0,低速设备最 多只能拥有 2 个端点,高速设备也最多只能拥有 15 个 in 端点和 15 个 out 端点。这些端点在设备内部都有唯一的 端点号,这个端点号是在设备设计时就已经指定的。

学ARM和学单片机一样简单
中断传输用来以一个固定的速率传送少量的数据, USB 键盘和 USB 鼠标使用的就是这种方式,USB 的触摸屏也是,传输的数据包含了坐 标信息。 批量传输用来传输大量的数据,确保没有数据丢失,并不保证在特定 的时间内完成。U 盘使用的就是批量传输,咱们用它备份数据时需要 确保数据不能丢,而且也不能指望它能在一个固定的比较快的时间内 拷贝完。 等时传输同样用来传输大量的数据,但并不保证数据是否到达,以稳 定的速率发送和接收实时的信息,对传送延迟非常敏感。显然是用于 音频和视频一类的设备,这类设备期望能够有个比较稳定的数据流, 比如你在网上 QQ 视频聊天,肯定希望每分钟传输的图像/声音速率是 比较稳定的,不能说这一分钟对方看到你在向她向你深情表白,可是 下一分钟却看见画面停滞在那里,只能看到你那傻样一动不动,你说 这不浪费感情嘛。

学ARM和学单片机一样简单
管道,实际上只是为了让我们能够找到端点,就相当于我 们日常说的邮编地址,pipe另一端是 usb 主机,即前面说 的那个 host,pipes 代表着一种在主机和设备上的端点之 间移动数据能力

学ARM和学单片机一样简单
USB设备的枚举过程 系统上电识别USB设备过程 : 首先是主机对设备的枚举,该过程是USB协议软件自 动完成的。当USB连接时,主机将使用缺省控制管道向其 发出标准USB设备请求,为识别设备,USB设备部分需要 通过编写固件代码或者芯片内部自动实现响应请求功能。 主机部分需提供相应设备驱动程序和INF文件。该部分资 源芯片厂商会提供实例程序和相应的INF文件,可在此基 础上进行修改。 详细过程分为以下六步:

学ARM和学单片机一样简单
把USB设备连接至某集线器的下行端口,集线器马上会 提供电源。 集线器监视其D-D+线上的电压,当达到一 定值时,认为有设备连接。 集线器使用中断输入管道 向主机报告其USB设备的连接,主机会向集线器发出 GetPortStatus请求以了解更多信息。为确保连接的机 械特性和电气特性稳定,主机至少要等待100ms,USB 设备在接收到任何请求前,其电源已经稳定工作了 100ms。 主机向集线器发出SetPortStatus请求,以复位 这个USB设备。并通过检测USB设备连接在D-D+上的 上拉电阻来确定设备的传输速度:低速、全速或高速。 为确保USB设备复位操作的完成,主机需提供10ms的 复位恢复时间。

学ARM和学单片机一样简单
自动枚举并配置设备 USB总线驱动程序有一个专门的功能:集线器驱动程序, 它负责监视集线器下行端口状态的变化,发现USB设备连 接后,就会使用非USB主机软件和USB设备驱动程序识别 和配置设备。配置分为以下三种: 1) 设备配置:建立设备的所有USB参数,并为其分配总 线资源。该操作通过设置USB设备的配置值完成。 2) USB配置:客户软件获取USB设备的管道信息,如传 输速率、传输类型等,已与该设备进行正确的数据传输

学ARM和学单片机一样简单
3) 功能配置:从USB的角度来看,在设备配置和USB配 置完成后,客户软件就可以和指定的设备通信了,但有些 USB设备还需要设备类或供应商的特定设置操作。 设备配 置过程首先读取USB设备的设备描述符,然后请求每种配 置的配置信息,最后USB设备驱动程序会从中选择一个作 为当前配置。设备配置之后,配置软件会把USB设备接口 信息返回给客户软件,使其拥有一组可进行USB数据传输 的管道,USB配置对这些管道进行初始化,如设置服务间 隔、最大数据包长度等。之后界面应用程序就可以使用这 些管道和USB设备的功能单元进行数据传输了。

学ARM和学单片机一样简单
主机向设备发出GetDescriptor(Device)请求,以取得缺省 管道所支持的最大数据包长度。该长度包含在设备描述符 bMaxPacketSize0字段中,其地址偏移量为7,所以这时主 机只需要读取该描述符的前8字节。 主机向设备发出SetAddress请求,为其分配一个唯一的设 备地址。之后它不再使用缺省设备地址,而将使用这个新 的地址和主机进行通信,该地址在USB设备断开或系统断 电时丢失。 主机使用新地址向设备发出GetDescriptor(Device)请求, 并读取其设备描述符的全部字段,以了解该设备的总体信 息,如供应商ID,产品ID等。

学ARM和学单片机一样简单
主机向设备循环发出GetDescriptor(Configuration)请求 ,以取得其全部配置信息(其个数由设备描述符 bNumConfiguration字段指出),包括配置描述符、接 口描述符、端点描述符以及各种设备类定义描述符和供 应商自定义描述符等。

学ARM和学单片机一样简单
主机根据USB设备的配置信息,如供应商ID,产品ID等 ,寻找到主机中相应INF文件(标识出设备驱动程序名 称和位置的文件)为其选择一个合适的USB设备驱动程 序。它通常需要由开发人员自己编写,但有时也可以使 用设备类或供应商提供的通用驱动程序。 在加载了USB 设备驱动程序后,主机将发出SetConfiguration(x)请求 为该设备选择一个合适的配置。为USB设备选择一个配 置值、一个接口和一个可替换设置值,并确定相应端点 特性,如所支持的传输类型,最大数据包常数等。USB 系统软件会判断当前USB是否有足够的帧时间来满足该 配置的总线带宽请求,不满足,主机尝试使用其他配置 值。满足,配置成功。USB设备可从USB总线获取其配 置描述符中所指出的最大总线电流,并可以和客户软件 进行数据传输。

学ARM和学单片机一样简单

学ARM和学单片机一样简单
#define USB_REQUEST_GET_STATUS #define USB_REQUEST_CLEAR_FEATURE #define USB_REQUEST_SET_FEATURE #define USB_REQUEST_SET_ADDRESS #define USB_REQUEST_GET_DESCRIPTOR #define USB_REQUEST_SET_DESCRIPTOR #define USB_REQUEST_GET_CONFIGURATION #define USB_REQUEST_SET_CONFIGURATION #define USB_REQUEST_GET_INTERFACE #define USB_REQUEST_SET_INTERFACE 0x00 0x01 0x03 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B

学ARM和学单片机一样简单
USB的描述符
一个USB设备有一个设备描述符,设备描述符里面决定了 该设备有多少种配置,每种配置描述符对应着配置描述 符;而在配置描述符中又定义了该配置里面有多少个接 口,每个接口有对应的接口描述符;在接口描述符里面又 定义了该接口有多少个端点,每个端点对应一个端点描述 符;端点描述符定义了端点的大小,类型等等。由此我们 可以看出,USB的描述符之间的关系是一层一层的,最上 一层是设备描述符,下面是配置描述符,再下面是接口描 述符,再下面是端点描述符。在获取描述符时,先获取设 备描述符,然后再获取配置描述符,根据配置描述符中的 配置集合长度,一次将配置描述符、接口描述符、端点描 述符一起一次读回。其中可能还会有获取设备序列号,厂 商字符串,产品字符串等。

学ARM和学单片机一样简单
在控制管道发起USB设备请求,其中很常见的请求是 USB_REQUEST_GET_ DESCRIPTOR,即请求USB设备 回答设备或者管道描述符。在请求描述符时, bmRequestType可以指定是针对设备还是针对管道的。当 请求设备描述符后,设备会回答主机该设备的设备描述符 ,设备描述符是一种固定的数据结构

学ARM和学单片机一样简单
1.设备描述符 //定义标准的设备描述符结构 typedef struct _DEVICE_DCESCRIPTOR_STRUCT { BYTE blength; //设备描述符的字节数大小 BYTE bDescriptorType; //设备描述符类型编号 WORD bcdUSB; //USB版本号 BYTE bDeviceClass; //USB分配的设备类代码 BYTE bDeviceSubClass; //USB分配的子类代码 BYTE bDeviceProtocol; //USB分配的设备协议代码 BYTE bMaxPacketSize0; //端点0的最大包大小 WORD idVendor; //厂商编号 WORD idProduct; //产品编号 WORD bcdDevice; //设备出厂编号 BYTE iManufacturer; //设备厂商字符串的索引 BYTE iProduct; //描述产品字符串的索引 BYTE iSerialNumber; //描述设备序列号字符串的索引 BYTE bNumConfigurations; //可能的配置数量 } DEVICE_DESCRIPTOR_STRUCT, * pDEVICE_DESCRIPTOR_STRUCT;

学ARM和学单片机一样简单
每个设备有一个或多个配置描述符,它们描述了设备能实 行的各种配置方式

学ARM和学单片机一样简单
2.配置描述符 //定义标准的配置描述符结构 typedef struct _CONFIGURATION_DESCRIPTOR_STRUCT { BYTE bLength; //配置描述符的字节数大小 BYTE bDescriptorType; //配置描述符类型编号 WORD wTotalLength; //此配置返回的所有数据大小 BYTE bNumInterfaces; //此配置所支持的接口数量 BYTE bConfigurationValue; //Set_Configuration命令所需要的参数值 BYTE iConfiguration; //描述该配置的字符串的索引值 BYTE bmAttributes; //供电模式的选择 BYTE MaxPower; //设备从总线提取的最大电流 } CONFIGURATION_DESCRIPTOR_STRUCT, * pCONFIGURATION_DESCRIPTOR_STRUCT;

学ARM和学单片机一样简单
每个配置有一个或多个接口描述符,它们描述了设备提供 功能的接口。

学ARM和学单片机一样简单
2.接口描述符 //定义标准的接口描述符结构 typedef struct _INTERFACE_DESCRIPTOR_STRUCT { BYTE bLength; //接口描述符的字节数大小 BYTE bDescriptorType; //接口描述符的类型编号 BYTE bInterfaceNumber; //该接口的编号 BYTE bAlternateSetting; //备用的接口描述符编号 BYTE bNumEndpoints; //该接口使用的端点数,不包括端点0 BYTE bInterfaceClass; //接口类型 BYTE bInterfaceSubClass; //接口子类型 BYTE bInterfaceProtocol; //接口遵循的协议 BYTE iInterface; //描述该接口的字符串索引值 } INTERFACE_DESCRIPTOR_STRUCT, * pINTERFACE_DESCRIPTOR_STRUCT;

学ARM和学单片机一样简单

接口可以没有或有多个端点描述符,它们描述 了处理事务的端点

学ARM和学单片机一样简单
4.端点描述符 //定义标准的端点描述符结构 typedef struct _ENDPOINT_DESCRIPTOR_STRUCT { BYTE bLegth; //端点描述符字节数大小 BYTE bDescriptorType; //端点描述符类型编号 BYTE bEndpointAddress; //端点地址及输入输出属性 BYTE bmAttributes; //端点的传输类型属性 WORD wMaxPacketSize; //端点收、发的最大包大小 BYTE bInterval; //主机查询端点的时间间隔 } ENDPOINT_DESCRIPTOR_STRUCT, * pENDPOINT_DESCRIPTOR_STRUCT;

学ARM和学单片机一样简单
下面是一个描述符集合的定义:
USB设备的软件设计主要包括两部分:一是USB设备端的 单片机软件,主要完成USB协议处理与数据交换以及其它 应用功能程序(比如A/D转换、MP3解码等)。二是PC端 的程序,由USB通信程序和用户服务程序两部分组成,用 户服务程序通过USB通信程序与系统USBDI(USB Device Interface)通信,由系统完成USB协议的处理与数据传输。 PC端程序的开发难度比较大,程序员不仅要熟悉USB协 议,还要熟悉Windows体系结构并能熟练运用DDK工具。

学ARM和学单片机一样简单
单片机软件设计主要注意以下几点: 中断处理后要用读上次传输状态寄存器清除中断寄存器中 对应位(D0-D5)。 PDIUSBD12对内部寄存器的读写没有边界限制,程序设 计中一定不要读写超过端点深度的数据。特别对于描述符 请求,由于其长度大于Control IN 深度(16 Bytes),要 分几个数据周期传输。 描述符一定要设置正确,并且注意USB协议中所有字数据 均定义为低字节在前传输(LSB),例如Phlips的ID为 471H,要根据大小端定义不同的iDVendor。 协议的处理一定要按USB规范要求进行,对无效请求,用 Set Endpoint Status指令将Control IN和Control OUT端点 Stall即可。

学ARM和学单片机一样简单

学ARM和学单片机一样简单

第六讲:
uC/OS-II的简单应用

学ARM和学单片机一样简单
(一) uC/OS-II 简介
uC/OS-II是一种基于优先级的可抢先的硬实时内核。自从92年 发布以来,在世界各地都获得了广泛的应用,它是一种专门为嵌入 式设备设计的内核,目前已经被移植到40多种不同结构的CPU上 ,运行在从8位到64位的各种系统之上。尤其值得一提的是,该 系统自从2.51版本之后,就通过了美国FAA认证,可以运行在诸如 航天器等对安全要求极为苛刻的系统之上。鉴于uC/OS-II可以免费 获得代码,对于嵌入式RTOS而言,选择uC/OS无疑是最经济的选 择。

学ARM和学单片机一样简单
(二) uC/OS-II应用程序基本结构
应用uC/OS-II,自然要为它开发应用程序,下面论述基于uC/OSII的应用程序的基本结构以及注意事项。

学ARM和学单片机一样简单
kernel提供给用户一些功能函数,使得用户的系统建立更加方便,但是 kernel内部不会处理用户的工作,对于整个系统的具体应用工作还得需 要用户自己去考虑,如何利用好这些功能服务函数就成为一个比较重要 的问题.

1. main函数的结构
void main (void) { 初始化系统的硬件; OSInit(); 任务的建立,消息机制的建立; OSStart(); } 这里需要的是在OSStart()执行之前不得启动中断,硬件系统还不能 工作.必须先让软件系统进入工作状态后才行.

学ARM和学单片机一样简单
2 .任务的结构
每一个uC/OS-II应用至少要有一个任务。而每一个任务必须被写 成无限循环的形式。以下是推荐的结构: void task ( void* pdata ) { INT8U err; InitTimer(); // 可选 For( ;; ) { // 你的应用程序代码 ……. …….. OSTimeDly(1); // 可选 } }

学ARM和学单片机一样简单
以上就是基本结构,在任务启动函数执行完后,系统会 切换到最高优先级的任务去执行,此时,可以将系统硬件部 分的启动放在该任务的最前边,仅仅是启动时执行一次,主 要是启动系统的节拍中断,或者一些必须在多任务系统调 度后才能初始化的部分,使系统的真正开始工作,达到软件 硬件的基本同步.

学ARM和学单片机一样简单
至于为什么要写成无限循环的形式?那是因为系统会 为每一个任务保留一个堆栈空间,由系统在任务切换的时 候换恢复上下文,并执行一条reti 指令返回。如果允许任 务执行到最后一个花括号(那一般都意味着一条ret指令) 的话,很可能会破坏系统堆栈空间从而使应用程序的执行 不确定。换句话说,就是“跑飞”了。所以,每一个任务必 须被写成无限循环的形式。

学ARM和学单片机一样简单
现在来谈论上面程序中的InitTimer()函数,这个函数应该由系统 提供,程序员有义务在优先级最高的任务内调用它而且不能在for循环 内调用。注意,这个函数是和所使用的CPU相关的,每种系统都有自 己的Timer初始化程序。在uC/OS-II的帮助手册内,作者特地强调绝 对不能在OSInit()或者OSStart()内调用Timer初始化程序,那会破坏 系统的可移植性同时带来性能上的损失。所以,一个折中的办法就是 象上面这样,在优先级最高的程序内调用,这样可保证当OSStart()调 用系统内部函数OSStartHighRdy()开始多任务后,首先执行的就是 Timer初始化程序。或者专门开一个优先级最高的任务,只做一件事 情,那就是执行Timer初始化,之后通过调用OSTaskSuspend()将自 己挂起来,永远不再执行。不过这样会浪费一个TCB空间。对于那些 RAM吃紧的系统来说,还是不用为好。

学ARM和学单片机一样简单 (三) 一些重要的uC/OS-II API介绍
任何一个操作系统都会提供大量的API供程序员使用,uC/OS-II也 不例外。由于uC/OS-II面向的是嵌入式开发,并不要求大而全,所以 内核提供的API也就大多和多任务息息相关。主要的有以下几类: 1. 任务类 2. 同步和消息类 3. 时间类 4. 临界区

学ARM和学单片机一样简单
1.任务类
1) 函数 OSTaskCreate函数
这个函数应该至少再main函数内调用一次,在OSInit函数调用之 后调用。作用就是创建一个任务。目前有四个参数,分别是任务的入 口地址,任务的参数,任务堆栈的首地址和任务的优先级。调用本函 数后,系统会首先从TCB空闲列表内申请一个空的TCB指针,然后将 会根据用户给出参数初始化任务堆栈,并在内部的任务就绪表内标记 该任务为就绪状态。最后返回,这样一个任务就创建成功了。

学ARM和学单片机一样简单
OSTaskSuspend函数
这个函数很简单,一看名字就该明白它的作用,它可以将指定的任务挂 起。如果挂起的是当前任务的话,那么还会引发系统执行任务切换先导函数 OSShed来进行一次任务切换。这个函数只有一个参数,那就是指定任务的优 先级。那为什么是优先级呢?事实上在系统内部,优先级除了表示一个任务 执行的先后次序外,还起着分别每一个任务的作用,换句话说,优先级也就 是任务的ID。所以uC/OS-II不允许出现相同优先级的任务。

OSTaskResume函数
这个函数和上面的函数正好相反,它用于将指定的已经挂起的函数恢复 成就绪状态。如果恢复任务的优先级高于当前任务,那么还为引发一次任务 切换。其参数类似OSTaskSuspend函数,为指定任务的优先级。需要特别说 明是,本函数并不要求和OSTaskSuspend函数成对使用。

学ARM和学单片机一样简单
2)任务状态
睡眠态: 任务驻留在ROM或RAM中但是还没有交给uC/OS-II来管理。 就绪态: 任务一旦建立就立即进入就绪态,准备运行。任务的建立可以是多任 务运行之前,也可以是多任务运行中。任务中也可以建立另一个任务 ,如果被建立的任务的优先级高于建立它的任务,它立刻进入运行态 。 运行态: 多任务建立后,可以调用OSStart()开始运行多任务,该函数只能在启 动时调用一次。CPU只有一个,任何时刻只有一个任务处于运行态( 掌握CPU的使用权)。uC/OS-II是基于优先级调度的,所以要一个任 务处于运行态,那么就需要所有优先级高于该任务的任务处于等待状 态或者这些高优先级任务被删除了。

学ARM和学单片机一样简单
等待态:
一种情况是为了避免高优先级的任务称霸CPU,那么就需要 周期性的把这个任务挂起让其他较低的优先级的任务有机会被执行 。可以通过调用OSTimeDly()和OSTimeDlyHMSM()来将任务自身 延迟一段时间。 另外一种情况是当运行着的任务需要等待某一个事件的发生的 时候,那么此时它占着CPU什么也不干太浪费,趁着等待的时刻让 其他低优先级的任务运行运行就非常不错了。那么这个高优先级的 任务可以根据自己具体的需要调用以下函数是自己处于等待状态: OSFlagPend()、OSSemPend()、OSMutexPend()、OSMboxPend() 或OSQPend()。总结一下,都是一些Pend函数。

学ARM和学单片机一样简单
中断服务态:
正在运行的任务被中断打断的状态。这里需要注意的是,当中 断返回后不一定就返回到被打断的任务,因为在uC/OS-II中始终都是 以任务的优先级来判定哪个任务该执行。

学ARM和学单片机一样简单
2. 同步和消息类
1)信号量
?C/OS-II中的信号量由两部分组成:一个是信号量的计数值,它 是一个16位的无符号整数(0 到65,535之间);另一个是由等待该信号 量的任务组成的等待任务表。用户要在OS_CFG.H中将OS_SEM_EN开 关量常数置成1,这样?C/OS-II才能支持信号量。

学ARM和学单片机一样简单
建立一个信号量, OSSemCreate(INT16U cnt ) 等待一个信号量, OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err ) 发送一个信号量, OSSemPost(OS_EVENT *pevent ) 无等待地请求一个信号量, OSSemAccept(OS_EVENT *pevent ) 查询一个信号量的当前状态, OSSemQuery()

学ARM和学单片机一样简单
2)邮箱
邮箱是?C/OS-II中另一种通讯机制,它可以使一个任务或者中断 服务子程序向另一个任务发送一个指针型的变量。该指针指向一个包 含了特定“消息”的数据结构。为了在?C/OS-II中使用邮箱,必须将 OS_CFG.H中的OS_MBOX_EN常数置为1。

学ARM和学单片机一样简单
建立一个邮箱,OSMboxCreate() 等待一个邮箱中的消息,OSMboxPend() 发送一个消息到邮箱中,OSMboxPost() 无等待地从邮箱中得到一个消息, OSMboxAccept() 查询一个邮箱的状态, OSMboxQuery()

学ARM和学单片机一样简单
如果用户只需要二值信号量和邮箱,这样做可以节省代码空 间。这时可以将OS_SEM_EN设置为0,只使用邮箱就可以了。

学ARM和学单片机一样简单
3)消息队列
消息队列实际上是邮箱的集合

学ARM和学单片机一样简单
建立一个消息队列 OS_EVENT *OSQCreate (void **start, INT16U size) 等待一个消息队列中的消息,OSQPend() 向消息队列发送一个消息(FIFO),OSQPost() 向消息队列发送一个消息(后进先出LIFO),OSQPostFront() 无等待地从一个消息队列中取得消息, OSQAccept() 查询一个消息队列的状态,OSQQuery()

学ARM和学单片机一样简单
3. 时间类
OSTimeDly函数
这应该调用最多的一个函数了,这个函数完成功能很简单,就是 先挂起当起当前任务,然后进行任务切换,在指定的时间到来之后, 将当前任务恢复为就绪状态,但是并不一定运行,如果恢复后是优先 级最高就绪任务的话,那么运行之。简单点说,就是可以任务延时一 定时间后再次执行它,或者说,暂时放弃CPU的使用权。一个任务可 以不显式的调用这些可以导致放弃CPU使用权的API,但那样多任务 性能会大大降低,因为此时仅仅依靠时钟机制在进行任务切换。一个 好的任务应该在完成一些操作主动放弃使用权!

学ARM和学单片机一样简单
4. 中断类
OS_ENTER_CRITICAL宏
很多人都以为它是个函数,其实不然,仔细分析一下OS_CPU.H文件,它 和下面马上要谈到的OS_EXIT_CRITICAL都是宏。他们都是涉及特定CPU 的实现。一般都被替换为一条或者几条嵌入式汇编代码。由于系统希望向上 层程序员隐藏内部实现,故而一般都宣称执行此条指令后系统进入临界区。 其实,它就是关个中断而已。这样,只要任务不主动放弃CPU使用权,别的 任务就没有占用CPU的机会了,相对这个任务而言,它就是独占了。所以说 进入临界区了。这个宏能少用还是少用,因为它会破坏系统的一些服务,尤 其是时间服务。并使系统对外界响应性能降低。

OS_EXIT_CRITICAL宏
这个是和上面介绍的宏配套使用另一个宏,它在系统手册里的说明是退 出临界区。其实它就是重新开中断。需要注意的是,它必须和上面的宏成对 出现,否则会带来意想不到的后果。最坏的情况下,系统会崩溃。我们推荐 程序员们尽量少使用这两个宏调用,因为他们的确会破坏系统的多任务性能

学ARM和学单片机一样简单 (四)多任务机制
前面已经说过,uC/OS-II是一种基于优先级的可抢先的多任务内 核。那么,它的多任务机制到底如何实现的呢?了解这些原理,可以 帮助我们写出更加健壮的代码来。 首先我们来看看为什么多任务机制可以实现?其实在单一CPU的 情况下,是不存在真正的多任务机制的,存在的只有不同的任务轮流 使用CPU,所以本质上还是单任务的。但由于CPU执行速度非常快, 加上任务切换十分频繁并且切换的很快,所以我们感觉好像有很多任 务同时在运行一样。这就是所谓的多任务机制。

学ARM和学单片机一样简单
由上面的描述,不难发现,要实现多任务机制,那么目标CPU必 须具备一种在运行期更改PC的途径,否则无法做到切换。不幸的使 ,直接设置PC指针,目前还没有哪个CPU支持这样的指令。但是一 般CPU都允许通过类似JMP,CALL这样的指令来间接的修改PC。我 们的多任务机制的实现也正是基于这个出发点。事实上,我们使用 CALL指令或者软中断指令来修改PC,主要是软中断。

学ARM和学单片机一样简单
回想一下你在微机原理课程上学过的知识,当发生中断的时候 ,CPU保存当前的PC和状态寄存器的值到堆栈里,然后将PC设置为 中断服务程序的入口地址,再下来一个机器周期,就可以去执行中断 服务程序了。执行完毕之后,一般都是执行一条RETI指令,这条指 令会把当前堆栈里的值弹出恢复到状态寄存器和PC里。这样,系统 就会回到中断以前的地方继续执行了。那么设想一下?如果再中断的 时候,人为的更改了堆栈里的值,那会发生什么?或者通过更改当前 堆栈指针的值,又会发生什么呢?如果更改是随意的,那么结果是无 法预料的错误。因为我们无法确定机器下一条会执行些什么指令,但 是如果更改是计划好的,按照一定规则的话,那么我们就可以实现多 任务机制。事实上,这就是目前几乎所有的OS的核心部分。不过他 们的实现不像这样简单罢了。

学ARM和学单片机一样简单
下面,我们来看看uC/OS-II再这方面是怎么处理的。在uC/OS-II 里,每个任务都有一个任务控制块(Task Control Block),这是一个比 较复杂的数据结构。在任务控制快的偏移为0的地方,存储着一个指 针,它记录了所属任务的专用堆栈地址。事实上,再uC/OS-II内,每 个任务都有自己的专用堆栈,彼此之间不能侵犯。这点要求程序员再 他们的程序中保证。一般的做法是把他们申明成静态数组。而且要申 明成OS_STK类型。当任务有了自己的堆栈,那么就可以将每一个任 务堆栈再那里记录到前面谈到的任务控制快偏移为0的地方。以后每 当发生任务切换,系统必然会先进入一个中断,这一般是通过软中断 或者时钟中断实现。然后系统会先把当前任务的堆栈地址保存起来, 仅接着恢复要切换的任务的堆栈地址。由于哪个任务的堆栈里一定也 存的是地址(还记得我们前面说过的,每当发生任务切换,系统必然 会先进入一个中断,而一旦中断CPU就会把地址压入堆栈),这样, 就达到了修改PC为下一个任务的地址的目的。

学ARM和学单片机一样简单

第七讲: u-boot移植和分析

学ARM和学单片机一样简单
1.Bootloader 及u-boot简介
Bootloader 代码是芯片复位后进入操作系统之前执行的一段代 码,主要用于完成由硬件启动到操作系统启动的过渡,从而为操作系 统提供基本的运行环境,如初始化CPU、 堆栈、存储器系统等。 Bootloader代码与CPU芯片的内核结构、具体型号、应用系统的配置 及使用的操作系统等因素有关,其功能类似于PC机的BIOS程序。由 于Bootloader和CPU及电路板的配置情况有关,因此不可能有通用的 Bootloader ,开发时需要用户根据具体情况进行移植。嵌入式Linux 系统中常用的Bootloader有armboot、redboot、blob、u-boot等,其 中u-boot是当前比较流行,功能比较强大的Bootloader ,可以支持 多种体系结构,但相对也比较复杂。

学ARM和学单片机一样简单
u-boot是sourceforge网站上的一个开放源代码的项目。它可对 PowerPC、MPC5xx、 MPC8xx、 MPC82xx、 MPC7xx、 MPC74xx、 ARM(ARM7、 ARM9、StrongARM、Xscale)、MIPS(4kc、5kc)、X86 等处理器提供支持,支持的嵌入式操作系统有LINUX、Vx-Works、 NetBSD、QNX、RTEMS、ARTOS、LynxOS等,主要用来开发嵌入式 系统初始化代码Bootloader 。软件的主站点是: http//sourceforge.net/projects/ u-boot。 u-boot最初是由denx www.denx.de的PPC-boot发展而来的,它对 PowerPC系列处理器的支持最完善,对Linux 操作系统的支持最好。源 代码开放的u-boot软件项目经常更新,是学习硬件底层代码开发的很好 样例。

学ARM和学单片机一样简单
为什么我们需要 u-boot?显然可以将 uCLinux 直接烧入 flash,从而 不需要额外的引导装载程序(bootloader)。但是从软件升级的角度 以及程序修补的来说,软件的自动更新非常重要。事实上,引导装载 程序(bootloader)的用途不仅如此,但仅从软件的自动更新的需要 就说明我们的开发是必要的。同时,u-boot 移植的过程也是一个对嵌 入式系统包括软硬件以及操作系统加深理解的一个过程。

学ARM和学单片机一样简单
2. u-boot 移植的框架
移植 u-boot 到新的开发板上仅需要修改和硬件相关的部分。在代 码结构上: 在board 目录下创建TXMCU 目录,创建TX44B0.c 以及 flash.c,memsetup.S,u-boot.lds等。不需要从零开始,可选择一个相似 的目录,直接复制过来,修改文件名以及内容。由于 u-boot 已经包 含基于 s3c44b0 开发板目录,作为参考,可以复制相应的目录。选择 的是 B2 目录。 在 include/configs 目录下添加TX44B0.h,在这里放上全局的宏定义 等。 找到 u-boot 根目录下 Makefile 修改加入 B2_config:unconfig @./mkconfig $(@:_config=) arm s3c44b0 B2 dave 后面加上这部分: TX44B0_config:unconfig @./mkconfig $(@:_config=) arm s3c44b0 TX44B0 TXMCU 运行 make TX44B0_config,如果没有错误就可以开始硬件相关代码移

学ARM和学单片机一样简单
3. u-boot 系统启动流程
大多数bootloader都分为stage1和stage2两大部分, u-boot也不例外。 依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在 stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这 样可以实现复杂的功能,而且有更好的可读性和移植性。

学ARM和学单片机一样简单
1) stage1 (start.s代码结构)
u-boot的stage1代码通常放在start.s文件中,它用汇编语言写成,其 主要代码部分如下: (1) 定义入口 。由于一个可执行的Image必须有一个入口点,并且只能有 一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必 须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来 完成。 (2)设置异常向量(Exceptionvector)。 (3)设置CPU的速度、时钟频率及中断控制寄存器。 (4)初始化内存控制器 。 (5)将ROM中的程序复制到RAM中。 (6)初始化堆栈 。 (7)转到RAM中执行,该工作可使用指令ldrpc来完成。

学ARM和学单片机一样简单
2) stage2 C语言代码部分
libarm/board.c中的startarmboot是C语言开始的函数,也是整个启 动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该 函数主要完成如下操作: (1)调用一系列的初始化函数。 (2)初始化Flash设备。 (3)初始化系统内存分配函数。 (4)如果目标系统有显示设备,则初始化该类设备。 (5)初始化相关网络设备,填写IP、MAC地址等。 (6)进入命令循环(即整个Boot的工作循环),接受用户从串口输入的命 令,然后进行相应的工作。

学ARM和学单片机一样简单
4 . 总体结构
u-boot 是一个层次式结构。从上图也可以看出,做移植工作的软件人员应当 提供串口驱动(UART Driver) ,以太网驱动(Ethernet Driver),Flash 驱动( Flash 驱动) ,USB 驱动(USB Driver)。目前,通过 USB口下载程序显得 不是十分必要,所以暂时没有移植 USB 驱动。驱动层之上是 u-boot 的应用 command 通过串口提供人机界面。我们可以使用一些命令做一些常用的工作, 比如内存查看命令 md。Kermit 应用主要用来支持使用串口通过超级终端下载 应用程序。TFTP 则是通过网络方式来下载应用程序,例如 uclinux 操作系统。

学ARM和学单片机一样简单
5.内存分布
在 flash rom 中内存分布图 TX44B0 的 flash 大小 2MBytes, 现在将 0-40000 共 256k 作为 u-boot 的存储空间。由于u-boot 中 有 一 些 环 境 变量,例如 ip 地址,引导文件名等,可在命令行通过 setenv 配置好,通过 saveenv 保存在 40000-50000 (共 64k)这段 空间里。如果存在保存好的环境变量, u-boot 引导将直接使用这些 环境变量。正如从代码分析中可以看到,我们会把 flash 引导代码 搬移到SDRAM中运行。引导代码u-boot将从0x00000000 处搬移到 0x0C700000 处。特别注意的由于TX44B0 uclinux 中断向量程序 地址在0x0c00 0000 处,所以不能将程序下载到 0x0c00 0000,通 常下载到 0x0c008000 处。

学ARM和学单片机一样简单
6. start.S 代码结构
1) 定义入口 一个可执行的 Image 必须有一个入口点并且只能有一个唯一的全局 入口,通常这个入口放在 flash的 0x0 地址。 例如 start.S 中的 .globl_start: 值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连 器脚本文件(lds)。 2) 设置异常向量(Exception Vector) 异常向量表,也可称为中断向量表,必须是从 0 地址开始,连续的存 放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预 去指令错误(Pabort),数据错误(Dabort),保留,以及 IRQ,FIQ 等。注意这里 的值必须与 uclinux 的vector_base 一致。这就是说如果uclinux 中 vector_base(在 include/armnommu/proc-armv/system.h)定义为 0x0c00 0000, 则HandleUndef 应该在 0x0c00 0004。

学ARM和学单片机一样简单
3) 初始化 CPU 相关的 pll,clock,中断控制寄存器 依次为关闭 watch dog timer,关闭中断,设置 LockTime, PLL(phaselock loop),以及时钟。这些值(除了 LOCKTIME)都可从 Samsung 44b0的手册中查到。

学ARM和学单片机一样简单
4) 初始化内存控制器 内存控制器,主要通过设置 13 个从 1c80000 开始的寄存器来设置, 包括总线宽度,8 个内存 bank,bank 大小,sclk,以及两个 bank mod。

学ARM和学单片机一样简单
5) 将 rom 中的程序复制到 RAM 中 首先利用 PC 取得 bootloader 在 flash 的起始地址,再通过标号之差 计算出这个程序代码的大小。这些标号,编译器会在连接(link)的时 候生成正确的分布的值。取得正确信息后,通过寄存器(r3 到 r10)做为 复制的中间媒介,将代码复制到 RAM 中。

学ARM和学单片机一样简单
6) 初始化堆栈

学ARM和学单片机一样简单
7) 转到 RAM 中执行 使用指令: ldr pc, _start_armboot _start_armboot: .word start_armboot跳转到C函数地址。 这里的start_armboot就是lib_arm/board.c中的start_armboot()函数, 由此U-Boot开始执行C语言部分的代码。

学ARM和学单片机一样简单
7. 系统初始化部分
1) 串口部分 串口的设置主要包括初始化串口部分,值得注意的串口的 Baudrate 与时钟 MCLK 有很大关系,是通过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 )计算得出。这可以在手册中查到。其他的函数包 括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完 成。例如,接收函数: while(!(rUTRSTAT0 & 0x1)); //Receive data read return RdURXH0(); 2) 时钟部分 实现了延时函数 udelay。这里的 get_timer 由于没有使用中断,是使 用全局变量来累加的。 3) flash 部分 flash 作为内存的一部分,读肯定没有问题,关键是 flash 的写部。 Flash 的写必须先擦除,然后再写。

学ARM和学单片机一样简单

第八讲: ? uClinux启动分析 ? 字符驱动编程

学ARM和学单片机一样简单
uClinux启动分析

学ARM和学单片机一样简单
字符驱动编程
嵌入式系统通常有许多设备用于与用户交互,像触摸屏、小键盘、滚 动轮、传感器、RS232 接口、LCD 等等。除了这些设备外,还有许多 其它专用设备,包括闪存、USB、GSM、GPS 等。内核通过所有这些 设备各自的设备驱动程序来控制它们,包括 GUI 用户应用程序也通过访 问这些驱动程序来访问设备。uClinux 的驱动程序库不可能包括实际项 目系统中所有外围硬件的驱动,所以在应用开发中,编写驱动程序是一个 重要步骤,驱动程序设计的好坏直接影响系统运行的稳定性和运行效率。

学ARM和学单片机一样简单
Linux内核把驱动程序划分为3种类型:字符设备、块设备和网络设备 。在uClinux 内核编写驱动程序并不像其他操作系统那么复杂,实际上,所 要做的只是为相应的设备编写几个基本函数并向VFS(virtual file system) 注册即可。当上层应用要使用该设备时,VFS 就会调用相应的设备函数。 设备驱动程序通常可归为以下3 类: a) 块设备(block) ,以块为单位,允许随机访问,多用缓存技术; b) 字符设备(char) ,以字节为单位,只能按顺序访问,不用缓存; c) 网络接口(net) 。

学ARM和学单片机一样简单
系统用主设备号(MAJOR)和次设备号(MINOR)来唯一标识一 般设备;相同主设备号表示同一类设备,次设备号表示同类设备的 个数。所有设备在适当的目录(通常是/dev目录)下必须有相应的 文件,这样字符设备和块设备都可以通过文件操作的系统调用完成 。不同的是,块设备操作经常和缓冲区联系在一起。

学ARM和学单片机一样简单
字符设备的驱动程序通过在device_struct数据结构的chrdevs向量中增 加一项的方法来向内核注册自己。然后对这个设备的所有调用都用这 个设备号来实现; 块设备和字符设备都需要定义功能函数:对于每一个驱动函数来说,都 有一些和此设备密切相关的功能函数,就最常用的字符设备来说,都存 在着诸如open()、read()、write()、ioctrol( ) 这一类的操作。 当系统调用这些操作时,将自动的使用file-operations 结构中对应的函 数来实现具体的操作;

学ARM和学单片机一样简单
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); #ifdef MAGIC_ROM_PTR int (*romptr) (struct file *, struct vm_area_struct *); #endif /* MAGIC_ROM_PTR */ };

学ARM和学单片机一样简单
这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用 系统调用在对设备文件进行诸如read/write操作时,系统调用通过设 备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构 相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程 序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就 是编写子函数,并填充file_operations的函数指针。

学ARM和学单片机一样简单
下面以一个最简单的例子io驱动的例子给大家讲解char device驱动: 以GPIO的驱动来介绍一下驱动程序编译进内核的过程

学ARM和学单片机一样简单
1.在uClinux-dist/linux2.4.x/drivers/char 目录下编写驱动程序

学ARM和学单片机一样简单
2.修改uClinux-dist/linux2.4.x/dirvers/char/Makefile 在适当 的位置添加一行:obj-$(CONFIG_GPIO) += gpio.o

学ARM和学单片机一样简单
3.修改 uClinux-dist/linux2.4.x/dirvers/char/Config.in, 在适当 的位置添加一行: bool ‘gpiotest support’ CONFIG_GPIO 在make menuconfig 时可以进行配置。(注意这一句的 单引号,最好自已输入,不要复制,复制的单引号不一样)

学ARM和学单片机一样简单
4.修改 uClinux-dist/linux2.4.x/dirvers/char/mem.c 在适当的 位置添加: #ifdef CONFIG_GPIO extern int gpio_init(void); #endif 在chr_dev_init()函数中添加: #ifdef CONFIG_GPIO gpio_init();//在内核启动的时候对GPIO驱动程序进行注册 #endif

学ARM和学单片机一样简单
5.修改uClinux-dist/vendors/Samsung/44B0/Makefile,建立设 备节点,在DEVICE部分添加如下内容:gpio,c,126,0

学ARM和学单片机一样简单
6.在配置内核时选择character devices回车选中gpio device, 进行编译,下载内核,运行之后,输入cat /proc/devices可以 看到gpio 126这一项。

学ARM和学单片机一样简单
7.添加应用程序测试驱动程序

学ARM和学单片机一样简单

第九讲: 网络驱动编程

学ARM和学单片机一样简单
网络设备
linux网络子系统可以分为硬件层、设备驱动层、网络 协议层和应用层。可以看出,它的实现也采用了分层的思 想。其中网络协议层得到的数据包通过设备驱动的发送函 数被发送到具体的通信设备上,通信设备传来的数据也在 设备驱动程序的接收函数中被解析并组成相应的数据包传 给网络协议层。要实现一个网络设备驱动程序的主要工作 只是根据具体的硬件设备向它的高层提供服务而已,这和 字符设备、块设备的思路都是一样的。

学ARM和学单片机一样简单
网络驱动的框架
Linux 的设计者们为了简化物理网络设备的多样性, 对所有的设备进行了抽象并定义了一个统一的接口。所有 对网络硬件的访问都是通过这一接口进行的,接口为所有 类型的硬件提供了一个一致化的操作集合。任意一个网络 接口均可看成一个发送和接收数据包的实体。在 linux 中 这个统一的接口就是 device 结构,它操作的数据对象-数 据包是通过结构 sk_buff来封装的。整个网络设备驱动程 序工作原理如图

学ARM和学单片机一样简单

学ARM和学单片机一样简单
在 Linux/uclinux中,整个网络接口驱动程序的框架可分为 四层,从上到下分别为协议接口层、网络设备接口层、提 供实际功能的设备驱动功能层、以及网络设备和网络媒介 层。这个框架在内核网络模块中已经搭建好了,我们在设 计网络驱动程序时,要做的主要工作就是根据上层网络设 备接口层定义的 device结构和底层具体的硬件特性,完成 设备驱动的功能。

学ARM和学单片机一样简单
一个一般变态的结构struct net_device 为了屏蔽网络环境中物理网络设备的多样性,Linux对所有 的物理网络设备进行抽象并定义了一个统一的概念, 称之 为接口(Interface)。每个接口在内部都表现为一个这样 的结构体。以下为该结构体的定义

学ARM和学单片机一样简单
struct net_device { /* 以下为该结构体的可见成员,用户可从Space.c等文件中查看到 它们*/ char name[IFNAMSIZ]; /* 网络设备的名称*/ unsigned long rmem_end; /* shmem "recv" end */ unsigned long rmem_start;/* shmem "recv" start */ unsigned long mem_end; /* 设备发送和接收数据时需要占用 内存空间,该值为内存区域的结束地址*/ unsigned long mem_start; /* 共享内存区域的起始地址*/ unsigned long base_addr; /* 设备I/O操作的基地址,由驱动程 序设置,该成员可通过ifconfig来查看或更新*/ unsigned int irq; /* 设备的中断号,该成员可通过ifconfig来 查看或更新*/

学ARM和学单片机一样简单
/ * 下列成员只用于部分设备,它们不会出现在Space.c文件中*/ unsigned char if_port; /* 端口传输特性,用于多端口设备,可选值如下: enum { IF_PORT_UNKNOWN = 0, //未知方式 IF_PORT_10BASE2, //10兆同轴方式 IF_PORT_10BASET, //10兆双绞方式 IF_PORT_AUI, IF_PORT_100BASET, IF_PORT_100BASETX, IF_PORT_100BASEFX };*/ unsigned char dma; /* 设备的DMA通道,通常只在与外部总线通讯时才有意 义,通过ifconfig可查看该成员*/ unsigned long struct net_device state; *next; /* 设备的当前状态*/ /* 指向全局设备链表中下一个设备的指针*/ /* 设备的初始化函数,仅需调用一次 */

int (*init)(struct net_device *dev);

/* 至此,在Space.c文件中可见的成员介绍完毕 */

学ARM和学单片机一样简单
struct net_device *next_sched; /* 设备索引,唯一的设备标识 */ int ifindex; int iflink; struct net_device_stats* (*get_stats)(struct net_device *dev); struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); /*用于处理无线传输的函数,ioctl的替代品,详情查看 <net/iw_handler.h>*/ struct iw_handler_def * wireless_handlers; struct ethtool_ops *ethtool_ops;

学ARM和学单片机一样简单
/* 至此,该结构体的可见成员已介绍完毕,以下的所有成员都属于系统底层 * 而且可能会有不定期变动。*/ /* 在未来处理网络设备掉电的代码中也许会用到以下成员:*/ unsigned long 表示)*/ unsigned long unsigned short unsigned short unsigned short unsigned short unsigned mtu; last_rx; /* 进行最后一次接收操作的时间*/ flags; gflags; priv_flags; /* 设备标记。可用标记查看<linux/if.h>*/ /* 全局标记(?)*/ /* 类似于flags,但用户空间不可见 */ trans_start; /* 进行最后一次发送操作的时间(以jiffies

unused_alignment_fixer; /* Because we need priv_flags,* and we want to be 32-bit aligned.*/ /* 每次传输的最大数据量(即ETH_DATA_LEN),缺 省值为1500字节,该成员可通过ifconfig更改*/

学ARM和学单片机一样简单
unsigned short type; /* 设备类型。(ARP)地址解析协议根据该 成员来判断接口支持何种硬件地址。对于以太网设备 来说,合适的值为ARPHRD_ETHER。在 <linux/if_arp.h>中有所有设备类型。该成员通过ether_setup设置*/ unsigned short hard_header_len;/* 设备报头长度。即位于发送 数据包中IP报头之前的数据长度(即ETH_HLEN), void 以太网设备的该值为14。*/ *priv; /* 相当于filp->private_data。通过alloc_netdev

设置,通过netdev_priv访问*/ struct net_device *master; /* 该指针指向当前设备组中该设备所 属的主设备*/

学ARM和学单片机一样简单
/* 设备地址相关信息 */ unsigned char broadcast[MAX_ADDR_LEN]; ether_setup 进行分配*/ unsigned char /* 广播地址。由

dev_addr[MAX_ADDR_LEN]; /* 设备地址 (MAC地址),通过读取设备信息获得*/ addr_len; /* 设备地址长度(MAC地址),通常为6*/ *mc_list; /* 用于多点传送的MAC地址列表 */ */

unsigned char struct dev_mc_list int int int int

mc_count; /* 多点传送时MAC地址列表中的元素个数 promiscuity; allmulti;

watchdog_timeo; /* 该成员是一个以jiffies为单位的时间数。 表示的是网络层判定传输超时的最小时间标准*/ struct timer_list watchdog_timer; /* 用来为超时计数的定时器(?)*/

学ARM和学单片机一样简单
/* Protocol specific pointers */ void *atalk_ptr; /* AppleTalk link */ void *ip_ptr; /* IPv4 specific data */ void *dn_ptr; /* DECnet specific data */ void *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ void *ax25_ptr; /* AX.25 specific data */ struct list_head poll_list; /* Link to poll list */ int quota; int weight; struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct Qdisc *qdisc_ingress; struct list_head qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ /* ingress path synchronizer */ spinlock_t ingress_lock;

学ARM和学单片机一样简单
/* hard_start_xmit synchronizer */ spinlock_t xmit_lock; /* 该锁用来防止同一时间内对 hard_start_xmit函数进行多次调用*/ /* cpu id of processor entered to hard_start_xmit or – 1, if nobody entered there */ int xmit_lock_owner; /* 该成员指的是获得了xmit_lock的CPU 的 ID号。-1表示没有当前CPU获得 xmit_lock*/ /* device queue lock */ spinlock_t queue_lock; /* Number of references to this device */ atomic_t refcnt; /* The flag marking that device is unregistered, but held by an user */ int deadbeaf;

学ARM和学单片机一样简单
/* 网络设备的特性 */
int features; #define NETIF_F_SG 1 /* 分散/整合的I/O操作。标志设备可以将分散

的数据包整合后再发送 */ #define NETIF_F_IP_CSUM 2 /* 只对TCP/UDP包进行校验 */ #define NETIF_F_NO_CSUM 4 /* 设备无需进行包校验操作 */ #define NETIF_F_HW_CSUM 8 /* 校验所有类型的数据包 */ #define NETIF_F_HIGHDMA 32 /* 可直接存取内存中的高地址 */ #define NETIF_F_FRAGLIST 64 /* 分散/整合的I/O操作。标志设备可以 在接收到分散的数据后将其整合*/ #define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration #define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration #define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN #define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle #define NETIF_F_TSO #define NETIF_F_LLTX VLAN packets */ 2048 /* Can offload TCP/IP segmentation */ 4096 /* LockLess TX */

学ARM和学单片机一样简单
/* 当设备与网络断开时调用该函数 */ void (*uninit)(struct net_device *dev); /* 设备的析构函数,当设备的所有外部引用都消失后调用该函数 */ void (*destructor)(struct net_device *dev); /********** 设备操作函数指针 ***********/ int (*open)(struct net_device *dev); /*设备的打开函数。 当ifconfig激活设备后设备就会被打开, 该函数的工作是注册设备所需的系统资源 (I/O端口,中断号及DMA通道等), 打开硬件设备并进行设备所需的其他置 (*stop)(struct net_device *dev); /*设备的终止函数。当接

int

口被析构时设备就会被停止。该函数执行与open函数相反的操作。*/

学ARM和学单片机一样简单
int (*hard_start_xmit) (struct sk_buff *skb, /*发送一个完整 的数据 struct net_device *dev); 包*/ #define HAVE_NETDEV_POLL int (*poll) (struct net_device *dev, int *quota); /*由NAPI驱动程序提供的以轮询 int (*hard_header) (struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); int (*rebuild_header)(struct sk_buff *skb); 方式工作 的设备操作函数*/ /*该函数根据源设备及目标设备的

地址组合一 个设备报头。*/ /*以太网设备的该函数默认为eth_header。*/ /*该函数在调用hard_start_xmit之前被调用。*/

/*该函数负责重建设备报头以完

善其信息,它在地址解析完成后,发送首个数据包之前被调用 #define HAVE_MULTICAST void (*set_multicast_list)(struct net_device *dev); /*当设备多点传送的地址列

表和设备标志被更改时调用该函数。*/

学ARM和学单片机一样简单
#define HAVE_SET_MAC_ADDR int (*set_mac_address)(struct net_device *dev, void *addr); #define HAVE_PRIVATE_IOCTL int (*do_ioctl)(struct net_device *dev, /*实现设备特有的ioctl命令。 /*在设备支持的情况下

设置设备的MAC地址

struct ifreq *ifr, int cmd);不需要时可将参数1设为NULL。*/ #define HAVE_SET_CONFIG int (*set_config)(struct net_device *dev, /*旧有的更改设

struct ifmap *map);备设置的函数。目前的设备不需要该函数。*/

学ARM和学单片机一样简单
#define HAVE_HEADER_CACHE int (*hard_header_cache)(struct neighbour *neigh, /*将地址解析的 结果数据填充到hh_cache结构体中。*/ struct hh_cache *hh); void /*以太网设备的该函数默认为eth_header_cache /*当地址解析的结果变更 时更新 hh_cache结构体中的信息。 */ struct net_device *dev, /*以太网设备的默认函数是 eth_header_cache_update。*/ unsigned char * haddr); #define HAVE_CHANGE_MTU int (*change_mtu)(struct net_device *dev, int new_mtu); 输的最大数据量*/ /*改变设备单次传 (*header_cache_update)(struct hh_cache *hh,

学ARM和学单片机一样简单
#define HAVE_TX_TIMEOUT void (*tx_timeout) (struct net_device *dev); /*当一个数据包 发送失败时(可能是错过一次中断或设备被锁), 该函数负责处理相应问题并恢复传输。*/ (*vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); (*vlan_rx_add_vid)(struct net_device *dev, unsigned short vid); (*vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid); (*hard_header_parse)(struct sk_buff *skb, /*该函数从skb包 含的数据包中取出源地址,将其复制到haddr指向*/ unsigned char *haddr); /*的缓冲区中,并返回源地址长度。 以太网设备的默认函数为 eth_header_parse* int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); int (*accept_fastpath)(struct net_device *, struct dst_entry*);

void void void int

学ARM和学单片机一样简单
#ifdef CONFIG_NETPOLL int netpoll_rx; #endif #ifdef CONFIG_NET_POLL_CONTROLLER void (*poll_controller)(struct net_device *dev); #endif /* bridge stuff */ struct net_bridge_port *br_port; #ifdef CONFIG_NET_DIVERT /* this will get initialized at each interface type init routine?*/ struct divert_blk *divert; #endif /* CONFIG_NET_DIVERT */ };

/*当禁用中断

时,该函数用于请求驱动程序检查设备事件状*/

学ARM和学单片机一样简单
编程序
初始化(initialize)
驱动程序必须有一个初始化方法。在把驱动程序载入系统的时候会调用这个 初 始化程序。它做以下几方面的工作。检测设备。在初始化程序里你可以根 据硬件的特征检查硬件是否存在,然后决定是否启动这个驱动程序。配置和 初始化硬件。在初始化程序里你可以完成对硬件资源的配置,比如即插即用 的硬件就可以在这个时候进行配置(Linux内核对PnP功能没有很好的支持,可 以在驱动程序里完成这个功 能)。配置或协商好硬件占用的资源以后,就可以 向系统申请这些资源。有些资源是可以和别的设备共享的,如中断。有些是 不能共享的,如IO、DMA。接下来你要初始 化device结构中的变量。最后,你可以让硬件正式开始工作。

学ARM和学单片机一样简单
打开(open)
open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用 (即设备状 态由down-->up)。所以实际上很多在initialize中的工作可以放 到这里来做。比如资 源的申请,硬件的激活。如果dev->open返回非 0(error),则硬件的状态还是down。 open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止 模块卸载时 设备处于打开状态。在open方法里要调用 MOD_INC_USE_COUNT宏。

学ARM和学单片机一样简单
关闭(stop)
close方法做和open相反的工作。可以释放某些资源以减少系统 负担。close是在 设备状态由up转为down时被调用的。另外如果是做 为模块装入的驱动程序,close里应该调用MOD_DEC_USE_COUNT ,减少设备被引用的次数,以使驱动程序可以被卸载。 另外close方法 必须返回成功(0==success)。

学ARM和学单片机一样简单
发送(hard_start_xmit)
所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程 序的xmit时,发送的数据放在一个sk_buff结构中。一般的驱动程序把 数据传给硬件发出去。 也有一些特殊的设备比如loopback把数据组成 一个接收数据再回送给系统,或者 dummy设备直接丢弃数据。如果 发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功)。 如果设备暂时无法处理,比如硬件忙,则返回1。这时如果dev->tbusy 置为非0,则系统认为硬件忙,要等到dev->tbusy置0以后才会再次发 送。tbusy的置0任务一般由中断 完成。硬件在发送结束后产生中断, 这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送 。在发送不成功的情况下,也可以不置dev->tbusy为非0,这样系统会 不断尝试重发。如果hard_start_xmit发送不成功,则不要释放sk_buff 。传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送 方法里不需要再填充硬件帧头,数据可以直接提交给硬件发送。 sk_buff是被锁住的(locked),确保其他程序不会存取它。

学ARM和学单片机一样简单
接收
驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系 统的。一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序 申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来填 充sk_buff中 的一些信息。skb->dev = dev,判断收到帧的协议类型,填入 skb->protocol(多协 议的支持)。把指针skb->mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull) 。还要设置skb->pkt_type,标明第二层(链路层)数据类型。可以是以下类型 : PACKET_BROADCAST : 链路层广播 PACKET_MULTICAST : 链路层组播 PACKET_SELF : 发给自己的帧 PACKET_OTHERHOST : 发给别人的帧(监听模式时会有这种帧) 最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后 返回,真正的处理是在中断返回以后,这样可以减少中断时间。调用 netif_rx()以后,驱动程序就不能再存取数据缓冲区skb。

学ARM和学单片机一样简单
硬件帧头(hard_header)
硬件一般都会在上层数据发送之前加上自己的硬件帧头,比如以太网 (Ethernet) 有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的 前面的。驱动程序提供一个hard_header方法,协议层(ip、ipx、arp 等)在发送数据之前会调用这段程序。硬件帧头的长度必须填在dev>hard_header_len,这样协议层回在数据之前保留好硬件帧头的空间 。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就 可以了。在协议层调用hard_header时,传送的参数包括(2.0.xx):数 据的sk_buff,device指针,protocol,目的地址(daddr),源地址 (saddr),数据长度(len)。

学ARM和学单片机一样简单
常用的系统支持 include/linux/kernel.h里声明了kmalloc()和kfree()。用于 在内核模式下申 请和释放内存。 void *kmalloc(unsigned int len,int priority); void kfree(void *__ptr); 与用户模式下的malloc()不同,kmalloc()申请空间有 大小限制。长度是2的整次方。可以申请的最大长度也有 限制。另外kmalloc()有priority参数,通常使用 时可以为 GFP_KERNEL,如果在中断里调用用GFP_ATOMIC参 数,因为使用GFP_KERNEL

学ARM和学单片机一样简单
则调用者可能进入sleep状态,在处理中断时是不允许的。 kfree()释放的内存必须是kmalloc()申请的。如果知道内存 的大小,也可以用kfree_s()释放

学ARM和学单片机一样简单
request_irq()、free_irq()
这是驱动程序申请中断和释放中断的调用

学ARM和学单片机一样简单
时钟
时钟的处理类似中断,也是登记一个时间处理函数,在预定的时 间过后,系统 会调用这个函数。在include/linux/timer.h里声明。 struct timer_list { struct timer_list *next; struct timer_list *prev; unsigned long expires; unsigned long data; void (*function)(unsigned long); }; void add_timer(struct timer_list * timer); int del_timer(struct timer_list * timer); void init_timer(struct timer_list * timer);

学ARM和学单片机一样简单
使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始 化。time_list结构里expires是标明这个时钟的周期,单位采用jiffies的 单位。jiffies是Linux一个全局变量,代表时间。它的单位随硬件平台 的不同而不同。系统里定义了一个常数HZ,代表每秒种最小时间间 隔的数目。这样jiffies的单位就是1/HZ。Intel平台jiffies的单位是1/100 秒,这就是系统所能分辨的最小时间间隔了。所以expires/HZ就是以 秒为单位的这个时钟的周期。 function就是时间到了以后的回调函数,它的参数就是timer_list 中的data。data这个参数在初始化时钟的时候赋值,一般赋给它设备 的device结构指针。在预置时间到系统调用function,同时系统把这个 time_list从定时队列里清除。所以如果需要一直使用定时函数,要在 function里再次调用add_timer()把这个timer_list加进定时队列。

学ARM和学单片机一样简单
I/O端口的存取使用:
inline unsigned int inb(unsigned short port); inline unsigned int inb_p(unsigned short port); inline void outb(char value, unsigned short port); inline void outb_p(char value, unsigned short port);

学ARM和学单片机一样简单
如果使用模块(module)方式加载驱动程序,需要在模块初始化时把设 备注册到系统设备表里去。不再使用时,把设备从系统中卸除。定义 在drivers/net/net_init.h 里的两个函数完成这个工作。 int register_netdev(struct device *dev); void unregister_netdev(struct device *dev); dev就是要注册进系统的设备结构指针。在register_netdev()时,dev结 构一 般填写前面11项,即到init,后面的暂时可以不用初始化。最重 要的是name指针和 init方法。name指针空(NULL)或者内容为‘’或者 name[0]为空格(space),则系统 把你的设备做为以太网设备处理。以太网设备有统一的命名格式, ethX。对以太网这么特别对待大概和Linux的历史有关。init方法一定 要提供,register_netdev()会调用这个方法让你对硬件检测和 设置。register_netdev()返回0表示成功,非0不成功。

学ARM和学单片机一样简单
sk_buff
Linux网络各层之间的数据传送都是通过sk_buff。sk_buff提供一套管理 缓冲 区的方法,是Linux系统网络高效运行的关键。每个sk_buff包括一些控 制方法和一 块数据缓冲区。控制方法按功能分为两种类型。一种是控制整个 buffer链的方法, 另一种是控制数据缓冲区的方法。sk_buff组织成双向链表 的形式,根据网络应用 的特点,对链表的操作主要是删除链表头的元素和添 加到链表尾。sk_buff的控制方法都很短小以尽量减少系统负荷。(translated from article written by Alan Cox) 常用的方法包括: .alloc_skb() 申请一个sk_buff并对它初始化。返回就是申请到的sk_buff。 .dev_alloc_skb()类似alloc_skb,在申请好缓冲区后,保留16字节的帧头空 间。主要用在Ethernet驱动程序。 .kfree_skb() 释放一个sk_buff。 .skb_clone() 复制一个sk_buff,但不复制数据部分。 .skb_copy()完全复制一个sk_buff。 .skb_dequeue() 从一个sk_buff链表里取出第一个元素。返回取出的sk_buff, 如果链表空则返回NULL。这是常用的一个操作。

学ARM和学单片机一样简单
skb_queue_head() 在一个sk_buff链表头放入一个元素。 .skb_queue_tail() 在一个sk_buff链表尾放入一个元素。这也是常用的一个 操作。网络数据的处理主要是对一个先进先出队列的管理,skb_queue_tail() 和skb_dequeue()完成这个工作。 .skb_insert() 在链表的某个元素前插入一个元素。 .skb_append() 在链表的某个元素后插入一个元素。一些协议(如TCP)对没按 顺序到达的数据进行重组时用到skb_insert()和skb_append()。 .skb_reserve() 在一个申请好的sk_buff的缓冲区里保留一块空间。这个空间 一般是用做下一层协议的头空间的。 .skb_put() 在一个申请好的sk_buff的缓冲区里为数据保留一块空间。在 alloc_skb以后,申请到的sk_buff的缓冲区都是处于空(free)状态,有一个 tail指针指向free空间,实际上开始时tail就指向缓冲区头。skb_reserve() 在free空间里申请协议头空间,skb_put()申请数据空间。见下面的图。 .skb_push() 把sk_buff缓冲区里数据空间往前移。即把Head room中的空间移 一部分到Data area。 .skb_pull() 把sk_buff缓冲区里Data area中的空间移一部分到Head room中。

学ARM和学单片机一样简单
网卡驱动程序的加载方法
linux目前网络设备驱动程序的加载有两种方式。一种是系统启动时, 由内核自动检测并静态加载,我们称之为“启动初始化方式”,另一种 是通过模块化机制在系统运行过程中根据需要由用户或系统进程以动 态加载,我们称之为“模块初始化方式”

学ARM和学单片机一样简单

学ARM和学单片机一样简单
这两种方法实现的途径虽然不一样,但最终的结果却是相同 的。他们都是向内核的网络接口管理表(由指针 dev_base 指向 的一个链表)加入一个device 结构。dev_base 指针是系统运行 过程中管理网络设备的接口,所有成功注册的网络设备都是 这个链表的一个节点。

学ARM和学单片机一样简单

第十讲: USB驱动编程

学ARM和学单片机一样简单
Linux下USB系统文件节点:同其他外设一样,上层应用软件对连接在系统地 USB设备访问是通过文件系统的形式进行的。每个连接到系统总线上的USB 设备可以同时对应有一个或者多个驱动程序。即一个USB设备可以在Linux系 统上形成一个或多个设备节点,以供应用程序使用。在Linux系统上,每个设 备节点都有其相关的主设备号和次设备号。 USB主机驱动结构:Linux USB主机驱动由三部分组成: 1) USB主机控制器驱动(HCD):是USB主机驱动程序中直接与硬件交互 的软件模块,其主要功能有:主机控制器硬件初始化;为USBD层提供相应的 接口函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数 据传输等。 2) USB驱动(USBD):是整个USB主机驱动的核心,其主要实现的功能有 :USB总线管理、USB总线设备、USB总线带宽管理、USB的4种类型数据传 输、USB HUB驱动、为USB设备类驱动提供相关接口、提供应用程序访问的 USB系统的文件接口等。

学ARM和学单片机一样简单
3) USB设备类驱动:是最终与应用程序交互的软件模块,其主要实现的功有 :访问特定的USB设备、为应用程序提供访问接口等。 应用程序首先通过文件系统(POSIX)接口来访问相应的USB设备类驱 动程序和USBD;USB设备类驱动程序则通过USBD提供的相关接口将数据请 求包传递给USBD;USBD通过HCD提供的接口,进一步将数据包传递给 HCD;HCD最终将数据发送到USB总线上。Linux定义了通用的数据结构 URB用来在USB设备类驱动和USBD,USBD和HCD间进行数据传输。统一的 URB(Universal Request Block)结构为usb主机驱动程序的开发带来了很大 方便。 USB时序: 数据传输时序:在USB总线上,所有的数据传输都是由USB HOST发起 的。每个USB设备通过地址过滤出自己要接受的数据包,并根据数据包请求 的类型与USB HOST进行数据传输。由于数据传输的时序和总线带宽问题, 当应用程序通过设备类提供一个URB时,该数据包并不能立即被送到USB总 线上,而只能在USB总线上有足够带宽的情况下,该数据请求才会被传输。 因而,HCD层为不同类型的数据传输维护了相应的数据链,当数据链上的数 据包传输结束后,HCD通过调用与该数据包相关联的回调函数来通知设备类 驱动程序。

学ARM和学单片机一样简单
CH375B的linux主控制器驱动 CH375B 的HCD 对Linux的USB 协议栈进行补充,加入对CH375B主 设备控制器的支持。 CH375B的HCD可以分为以下功能模块: ◎ 对虚拟根hub 的支持; ◎ USB 请求队列 ◎ USB 请求安排表 ◎ 中断处理 ◎ USB 请求转化为USB 事务和包 ◎ USB 包的发送和接收 HCD支持中断,控制,和批量端点,不支持同步的端点。 HCD与Linux USB内核接口通过一系列的API 函数接口。

学ARM和学单片机一样简单
设备连接和断开

学ARM和学单片机一样简单

学ARM和学单片机一样简单
2.设备断开

学ARM和学单片机一样简单

学ARM和学单片机一样简单

学ARM和学单片机一样简单
struct usb_bus *usb_alloc_bus(struct usb_operations *op) void usb_free_bus(struct usb_bus *bus) void usb_register_bus(struct usb_bus *bus) void usb_deregister_bus(struct usb_bus *bus)

学ARM和学单片机一样简单

学ARM和学单片机一样简单

学ARM和学单片机一样简单

学ARM和学单片机一样简单

学ARM和学单片机一样简单


相关文章:
郭天祥的学ARM和学单片机一样简单视频教程1.08G
郭天祥学ARM和学单片机一样简单视频教程1.08G_计算机软件及应用_IT/计算机_专业资料。郭天祥学 ARM 和学单片机一样简单视频教程 1.08G 第一章 嵌入式开发...
答题技巧
P (郭天祥)学ARM和学单片机... 232页 8财富值 比例的意义和基本性质练习.....做刑法和民法案例时我各有一个小问,我实在是拿 不准,就简单写了一个结论,...
郭天祥学习经历--强烈推荐
郭天祥学习经历--强烈推荐_信息与通信_工程科技_专业...ARM 用的 ADS、STD 软 件等,所有上面我提到的...其实学 FPGA/CPLD 也和学单片机一样,关键是自己...
郭天祥-单片机学习心得
郭天祥-单片机学习心得_学习总结_总结/汇报_实用文档...其实学 FPGA/CPLD 也和学单片机一样,关键是自己动手...总之*p 的结果是 p 所指向的东西,这个东西有这 ...
郭天祥十天学会单片机C语言郭天祥---课后习题答案
郭天祥 十天学通单片机 TX-1C 单片机实验板 所有...最简单液晶工作原理、 如何开始对一个没 有任何概念...//52 单片机头文件 //主函数 //将单片机 P1.7...
单片机大师-郭天祥 亲述自己单片机成长经历【推荐新手必看】
者可能会购买学习板, 郭天祥决定开发 单片机学习板...ARM 用的 ADS、STD 软件所有上面我提到的这 些全...和学单片机一样关键是自己动手写程序实践不停地写...
郭天祥 十天学会单片机和C语言编程课程安排
郭天祥 十天学会单片机和C语言编程课程安排_工学_高等教育_教育专区。郭天祥51单片机视频教程安排 单片机课程安排讲次第一讲 内容学单片机预备知识、如何点亮 一个...
郭天祥_十天学通单片机_所有课后作业+答案
} 4 // 用总线操作点亮最后一个发光管. // 适用 TX-1C 单片机实验板 //...P (郭天祥)学ARM和学单片... 232页 2下载券 怎样学习单片机(郭天祥) 2...
郭天祥51单片机笔记
郭天祥51单片机笔记_电子/电路_工程科技_专业资料。STC89C52、AT89S52单片机...竖线: P1.4-P1.7。 优点:占用端口少,硬件电 路简单。 缺点:编程较复杂。 ...
《郭天祥之十天学会单片机》——序言部分
郭天祥之十天学会单片机》——序言部分_工学_高等...ARM 用的 ADS、STD 软件等,所有上面我提到的这些,...其实学 FPGA/CPLD 也和学单片机一样,关键是自己...
更多相关标签:
郭天祥arm9视频教程 | 郭天祥arm9 百度云盘 | 郭天祥arm7视频教程 | 郭天祥arm视频教程 | 郭天祥arm9光盘资料 | 郭天祥arm | 郭天祥十天学会arm | 郭天祥十天学会单片机 |