第1章 ZLG/CF驱动使用
1.1 基于 LPC2210 的 ZLG/CF 驱动使用
CF 卡有 PC 卡 I/O,MEMORY 及 True IDE 等 3 种模式,而 True IDE 模式兼容 IDE 硬盘,该模式比其它的两种模式更实用,是 3 种模式中使用较多的一种。本节中描述 CF 卡在
True IDE 模式下的应用。
使用 LPC2210 的通用可编程 I/O 口,模拟产生 ATA 设备的读写时序,实现对 CF 卡及
IDE 硬盘等 ATA 设备读写操作。使用 LPC2210 的 GPIO 功能,可以非常灵活而简单地实现
ATA 读写时序。
1.1.1 LPC2210 与 CF 卡及 IDE 硬件连接
LPC2210 的 GPIO 引脚与 CF 卡及 IDE 硬盘的硬件接线图分别如图 1.1、图 1.2 所示。
ATA_DASP
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29
30
30
31
31
32
32
33
33
34
34
35
35
36
36
37
37
38
38
39
39
40
40
41
41
42
42
43
43
44
44
45
45
46
46
47
47
48
48
49
49
50
50
J17
HEADER 25X2_CF
ATA_DASP
D4
LED
VCC
P2.24_D24
P2.25_D25
P2.26_D26
P2.27_D27
P2.28_D28
P2.29_D29
P2.30_D30
P2.31_D31
P1.20
P0.21_PWM5
P0.19_M AT1.2
P0.20_M AT1.3
VDD3.3
VDD3.3
P0.17_CAP1.2
P0.22_M AT0.0
VDD3.3
P1.25
P1.24
P2.18_D18
P2.17_D17
P2.16_D16
P1.16
P1.17
P1.18
VDD3.3
P1.19
P2.23_D23
P2.22_D22
P2.21_D21
P2.20_D20
P2.19_D19
R94
1K
图 1.1 LPC2210 与 CF 卡接线图
注意,图 1.1 中 CF 卡的 CSEL 引脚直接接地,开发板上的 CF 卡已被配置为主设备。 IDE 硬盘的主从配置,通过硬盘上的跳线选择。
GND
P2.23_D23 P2.24_D24
P2.22_D22 P2.25_D25
P2.21_D21 P2.26_D26
P2.20_D20 P2.27_D27
P2.19_D19 P2.28_D28
P2.18_D18 P2.29_D29
P2.17_D17 P2.30_D30
P2.16_D16 P2.31_D31
GND
GND
GND
P0.17_CA P1.2
P0.21_PW M 5
P0.22_MAT0.0
P0.18_CA P1.3
P0.19_MAT1.2
P0.20_MAT1.3
GND
GND
GND
1 2
3 4
5 6
7 8
9 10
11 12
13 14
15 16
17 18
19 20
21 22
23 24
25 26
27 28
29 30
31 32
33 34
35 36
37 38
39 40
J3
IDE/USER_COM
NC
R32
10K
P1.16
P1.17
P1.18
P1.19 P1.20
P1.21
P1.24
P1.25
VDD3.3
R31
10K
R33
10K
R13
10K
VDD3.3
ATA_DASP
D4
LED
VCC
ATA_DASP
R94
1K
P1.23
图 1.2 LPC2210 与 IDE 硬盘接线图
表 1.1 为 LPC2210 的 GPIO 引脚与 CF 卡及 IDE 硬盘引脚连接分配表,表中描述了各
GPIO 引脚与 CF 卡及 IDE 硬盘对应的控制信号线,根据表中的描述配置 LPC2210 相关的寄存器。
表 1.1 LPC2210 的 GPIO 引脚与 CF 卡及 IDE 硬盘连接引脚分配
LPC2210 CF 卡 IDE 硬盘 I/O LPC2210 CF 卡 IDE 硬盘 I/O
*P0.17 -RESET -RESET O *P1.17 A01 DA1 O
*P2.16~P2.31 D00~D15 DD0~DD15 I,O *P1.16 A00 DA0 O
P0.18 DMARQ I *P1.19 -CS0 -CS0 O
*P0.19 -IOWR -DIOW O P1.23 CSEL O
*P0.21 -IORD -DIOR O P1.24 -IOCS16 -IOCS16 I
P0.22 IORDY IORDY I P1.25 -PDIAG -PDIAG I
P1.21 -DMACK I *P1.18 A02 DA2 O
P0.20 INTRQ INTRQ I *P1.20 -CS1 -CS1 O
注,1.I/O 输入与输出是相对于 LPC2210 来说的,I 为 LPC2210 的输入,O 为输出。
2.表中“*”号的引脚,为使用到的引脚,其它引脚不需使用,但需要配置为适当的状态。
根据图 1.1、图 1.2 和表 1.1 所描述,为相关的引脚定义有意义的标号,如程序清单 1.1
所示。
程序清单 1.1 ATA 接口连接引脚定义
/* EeayARM2200 和 IDE 接口连接 */
#ifdef ATA_BUS_AT_8bit /*8 位总线*/
#define ATA_DATA 0x00ff0000 /* EeayARM2200 和 IDE 接口直接相连,p2.16~p2,23 */
#else /*16 位总线*/
#define ATA_DATA 0xffff0000 /* EeayARM2200 和 IDE 接口直接相连,p2.16~p2,31 */
#endif
#define IDE_A0 (1<<16) /* EeayARM2200 和 IDE 接口直接相连,p1,16 */
#define IDE_A1 (1<<17) /* EeayARM2200 和 IDE 接口直接相连,p1.17 */
#define IDE_A2 (1<<18) /* EeayARM2200 和 IDE 接口直接相连,p 1.18 */
#define IDE_CS0 (1<<19) /* EeayARM2200 和 IDE 接口直接相连,p 1.19 */
#define IDE_CS1 (1<<20) /* EeayARM2200 和 IDE 接口直接相连,p1,20 */
#define IDE_DMACK (1<<21) /* EeayARM2200 和 IDE 接口直接相连,p1.21 */
#define IDE_CSEL (1<<23) /* EeayARM2200 和 IDE 接口直接相连,p1.23 */
#define IDE_IOCS16 (1<<24) /* EeayARM2200 和 IDE 接口直接相连,p1.24 */
#define IDE_PDIAG (1<<25) /* EeayARM2200 和 IDE 接口直接相连,p1.25 */
#define IDE_RST (1<<17) /* EeayARM2200 和 IDE 接口直接相连,p0.17 */
#define IDE_DMAREQ (1<<18) /* EeayARM2200 和 IDE 接口直接相连,p0.18 */
#define IDE_WR (1<<19) /* EeayARM2200 和 IDE 接口直接相连,p0.1 9 */
#define IDE_INTRQ (1<<20) /* EeayARM2200 和 IDE 接口直接相连,p0.20 */
#define IDE_RD (1<<21) /* EeayARM2200 和 IDE 接口直接相连,p0.2 1 */
/* EeayARM2200 */
#define Addr_CS_at_P1 (IDE_A0 + IDE_A1 +IDE_A2 + IDE_CS0 + IDE_CS1) (1)
#define MASK_DATA (~ATA_DATA) (2)
1.1.2 函数接口的实现
1.ATA 设备寄存器读写函数
IDE_A0,IDE_A1,IDE_A2 地址信号引脚和 IDE_CS0,IDE_CS1 片选信号引脚都是在
LPC2210 的 P1 口上,正好由这些信号线确定 ATA 设备一个具体寄存器的地址;为了便于地址的设定,将 P1 口上同时为高电平的信号引脚定义为 Addr_CS_at_P1 宏,如程序清单 1.1(1)
所示。
为了便于数据总线的方向改变和数值输出,可定义数据输出的屏蔽值 MASK_DATA.如程序清单 1.1(2)所示。
模拟读 ATA 设备寄存器步骤如下,
(1) 关系统中断,预防在读寄存器操作中产生中断;
(2) 准备读取设备数据信号,设置 GPIO 模拟 ATA 接口数据的引脚为输入状态;
(3) 设置寄存器的相应地址;
(4) 使读 ATA 设备寄存器的信号引脚为低电平;
(5) 读取 GPIO 模拟 ATA 接口数据的引脚的电平 (这时数据线上的值应为设备寄存器中的值),并调整数据位置;
(6) 使读 ATA 设备寄存器的信号引脚为高电平;
(7) 如果 ATA 设备( CF 卡)是 8 位总线模式,并且操作的是数据寄存器( 16 位),则需重复 (3),(4),(5)步,再整合成 16 位的数据。
(8) 取消 ATA 设备寄存器地址的选择。
(9) 开系统中断。
GPIO 模拟读 ATA 设备寄存器的函数如程序清单 1.2 所示,ATA 设备各寄存器地址宏定义如程序清单 1.5 所示。
程序清单 1.2 读取 ATA 设备寄存器函数
#ifdef ATA_BUS_AT_8bit
uint16 SYS_PortIn(uint32 reg) /*8 位总线的读寄存器函数 */
{
uint16 res ;
OS_ENTER_CRITICAL(); /*关中断 */
IO2DIR = IO2DIR & MASK_DATA; /*定义输出口其它为输入 (ATA_DATA 为输入 )*/
IO1CLR = Addr_CS_at_P1; /*地址与片选信号都为低电平 */
IO1SET = reg; /*地址高电平位输出,完成地址的设置 */
/*读低字节 */
IO0CLR = IDE_RD; /*读信号脚置低 */
res = (uint8)((IO2PIN&ATA_DATA) >>16); /*读取数据 */
IO0SET = IDE_RD; /*使读信号为高 */
/*读高字节 */
if(reg==ATA_REG_DATA) /*如果读数据寄存器,读高字节 */
{
IO0CLR = IDE_RD; /*读信号脚置低 */
res += (uint16)((IO2PIN&ATA_DATA)>>8); /*读取数据 */
IO0SET = IDE_RD; /*使读信号为高 */
}
IO1SET = Addr_CS_at_P1; /*输出控制信号置高 */
OS_EXIT_CRITICAL(); /*开中断 */
return res; /*返回数值 */
}
#else
uint16 SYS_PortIn(uint32 reg) /*8 位总线的读寄存器函数 */
{
uint16 res ;
OS_ENTER_CRITICAL(); /*关中断 */
IO2DIR = IO2DIR & MASK_DATA; /*定义输出口其它为输入 (ATA_DATA 为输入 )*/
IO1CLR = Addr_CS_at_P1; /*控所硬盘引脚信号输出 (输出高电平 )*/
IO1SET = reg; /*控所硬盘引脚信号输出 (输出低电平 )*/
IO0CLR = IDE_RD; /*读信号脚置低 */
res = (uint16)(IO2PIN >>16); /*读取数据 */
IO0SET = IDE_RD;
IO1SET = Addr_CS_at_P1; /*输出控制信号置高 */
OS_EXIT_CRITICAL(); /*开中断 */
return res;
}
#endif
写 ATA 设备寄存器步骤如下,
(1) 关系统中断,预防在写寄存器操作中产生中断;
(2) 设置 GPIO 模拟 ATA 接口数据的引脚为输出状态,准备输出数据到设备数据线;
(3) 设置 ATA 设备寄存器的相应地址;
(4) 设置 GPIO 模拟 ATA 接口数据的引脚的电平为要写到设备的值;
(5) 使写 ATA 设备寄存器信号引脚为低电平;
(6) 使写 ATA 设备寄存器信号引脚为高电平;
(7) 如果 ATA 设备( CF 卡)是 8 位总线模式,并且操作的是数据寄存器( 16 位),则需重复 (3),(4),(5)步。
(8) 取消 ATA 设备寄存器地址的选择。
(9) 设置 GPIO 模拟 ATA 接口的数据总线引脚为输入状态,释放总线。
(10) 开系统中断。
LPC2210 的 GPIO 引脚模拟写 ATA 设备寄存器的函数如程序清单 1.3 所示,ATA 设备各寄存器地址宏定义如程序清单 1.5 所示。
程序清单 1.3 写 ATA 设备寄存器函数
#ifdef ATA_BUS_AT_8bit
void SYS_PortOut(uint32 reg,uint16 data) /*使用 8 位数据总线 */
{
OS_ENTER_CRITICAL(); /*关中断 */
IO2DIR = IO2DIR | ATA_DATA; /*设置数据总线为输出 */
IO1CLR = Addr_CS_at_P1; /*地址与片选信号都为低电平 */
IO1SET = reg; /*地址高电平位输出,完成地址的设置 */
/*写低字节 */
IO2CLR = ATA_DATA; /*数据总线上输出全为低电平 */
IO2SET = data<<16; /*输出数据值为 1 的位 */
IO0CLR = IDE_WR; /*写信号为低电平,保持大于 165nS*/
IO0SET = IDE_WR; /*写信号脚置高 [与低 >162ns]*/
if(reg==ATA_REG_DATA) /*如果访问的寄存器为 16 位的数据寄存器 */
{ /*写高字节 */
IO2CLR = ATA_DATA; /*数据线输出全为低电平 */
IO2SET = data<<8; /*输出高电平的数据位 */
IO0CLR = IDE_WR; /*写信号为低电平,保持大于 165nS*/
IO0SET = IDE_WR; /*写信号脚置高 [与低 >162ns]*/
}
IO1SET = Addr_CS_at_P1; /*输出控制信号置高 */
IO2DIR &= MASK_DATA; /*使数据线为输入,降低功耗 */
OS_EXIT_CRITICAL(); /*开中断 */
}
#else
void SYS_PortOut(uint32 reg,uint16 data) /*使用 16 位总线 */
{
OS_ENTER_CRITICAL(); /*关中断 */
IO2DIR = IO2DIR | ATA_DATA; /*设置数据总线为输出 */
IO1CLR = Addr_CS_at_P1; /*地址与片选信号都为低电平 */
IO1SET = reg; /*地址高电平位输出,完成地址的设置 */
IO2CLR = ATA_DATA; /*数据总线上输出全为低电平 */
IO2SET = data<<16; /*输出数据高位 */
IO0CLR = IDE_WR; /*写信号为低电平,保持大于 165nS*/
IO0SET = IDE_WR; /*写信号脚置高 [与低 >162ns]*/
IO1SET = Addr_CS_at_P1; /*输出控制信号置高 */
IO2DIR &= MASK_DATA; /*使数据线为输入,降低功耗 */
OS_EXIT_CRITICAL(); /*开中断 */
}
#endif
开中断及关中断函数如程序清单 1.4 所示。
程序清单 1.4 开、关总中断函数
#ifndef UCOSII (1)
#define NoInt 0x80
/* 关中断 */
__inline OS_ENTER_CRITICAL(void)
{
__asm
{
MRS R0,CPSR
ORR R0,R0,#NoInt
MSR CPSR_c,R0
}
}
/* 开中断 */
__inline OS_EXIT_CRITICAL(void)
{
__asm
{
MRS R0,CPSR
BIC R0,R0,#NoInt
MSR CPSR_c,R0
}
}
#endif
(1) 如果驱动用于 uC/OS-II 操作系统,则不需要编译这两个开、关中断函数,因为在
uC/OS-II 中已定义了这两个函数。
SYS_PortIn()和 SYS_PortOut()函数通过使用程序清单 1.5中定义的 ATA 设备寄存器的地址的宏作为参数,实现对 ATA 设备各寄存器的读写操作。
ZLG/CF 驱动读写 ATA 设备寄存器的函数如程序清单 1.6 所示。
程序清单 1.5 设备寄存器地址宏定义
/*GPIO 引脚影射寄存器地址*/
#define ATA_REG_DATA IDE_CS1 /*数据寄存器*/
#define ATA_REG_ERR (IDE_CS1 + IDE_A0) /*读错误寄存器*/
#define ATA_REG_FEATURE (IDE_CS1 + IDE_A0) /*写功能寄存器*/
#define ATA_REG_SECCNT (IDE_CS1 + IDE_A1) /*扇区计数器*/
#define ATA_REG_SECTOR (IDE_CS1 + IDE_A1 + IDE_A0) /*扇区号*/
#define ATA_REG_CYLI_LOW (IDE_CS1 + IDE_A2) /*柱面低 8 位*/
#define ATA_REG_CYLI_HIGH (IDE_CS1 + IDE_A2 + IDE_A0) /*柱面 高8位*/
#define ATA_REG_DEVICE_HEAD (IDE_CS1 + IDE_A2 + IDE_A1) /*选择主从,模式,磁头*/
#define ATA_REG_COMMAND (IDE_CS1 + IDE_A2 + IDE_A1 + IDE_A0) /*写命令寄存器*/
#define ATA_REG_STATUS (IDE_CS1 + IDE_A2 + IDE_A1 + IDE_A0) /*读状态寄存器*/
#define ATA_REG_CONTROL (IDE_CS0 + IDE_A2 + IDE_A1) /*写控制寄存器*/
#define ATA_REG_ASTATUS (IDE_CS0 + IDE_A2 + IDE_A1) /*读辅助状态寄存器*/
程序清单 1.6 读写设备相关寄存器接口函数
/*驱动与硬件对 ATA 设备寄存器操作接口 */
#define GetData() SYS_PortIn(ATA_REG_DATA) /*读数据寄存器,与驱动接口 */
#define SetData(x) SYS_PortOut(ATA_REG_DATA,x) /*写数据寄存器,与驱动接口 */
#define GetERR() SYS_PortIn(ATA_REG_ERR) /*读错误寄存器,与驱动接口 */
#define SetFeature(x) SYS_PortOut(ATA_REG_FEATURE,x) /*写特征寄存器,与驱动接口 */
#define GetSECCNT() SYS_PortIn(ATA_REG_SECCNT) /*读扇区计数寄存器,与驱动接口 */
#define SetSECCNT(x) SYS_PortOut(ATA_REG_SECCNT,x) /*写扇区计数寄存器,与驱动接口 */
#define GetSector() SYS_PortIn(ATA_REG_SECTOR) /*读扇区寄存器,与驱动接口 */
#define SetSector(x) SYS_PortOut(ATA_REG_SECTOR,x) /*写扇区寄存器,与驱动接口 */
#define GetCylinderLow() SYS_PortIn(ATA_REG_CYLINDER_LOW) /*读柱面低寄存器,与驱动接口 */
#define SetCylinderLow(x) SYS_PortOut(ATA_REG_CYLINDER_LOW,x)/*写柱面低寄存器,与驱动接口 */
#define GetCylinderHigh() SYS_PortIn(ATA_REG_CYLI_HIGH) /*读柱面高 8 位寄存器,与驱动接口 */
#define SetCylinderHigh(x) SYS_PortOut(ATA_REG_CYLI_HIGH,x) /*写柱面高 8 位寄存器,与驱动接口 */
#define GetDeviceHead() SYS_PortIn(ATA_REG_DEVICE_HEAD) /*读设备磁头寄存器,与驱动接口 */
#define SetDeviceHead(x) SYS_PortOut(ATA_REG_DEVICE_HEAD,x)/*写设备磁头寄存器,与驱动接口 */
#define GetStatus() SYS_PortIn(ATA_REG_STATUS) /*读状态寄存器,与驱动接口 */
#define SetCommand(x) SYS_PortOut(ATA_REG_COMMAND,x) /*写命令寄存器,与驱动接口 */
2.硬件复位函数
系统中定义了 IDE_RST 引脚控制 ATA 设备的硬件复位。如程序清单 1.7 所示。
程序清单 1.7 硬件复位函数
/*******************************************************************************************
** 函数名称,SYS_IdeHardReset
** 功能描述,ATA 设备硬件复位
** 输 入,无
** 输 出,无
** 全局变量,
** 调用模块,SYS_WaitInUS(),等待微秒函数。
*******************************************************************************************/
void SYS_IdeHardReset(void)
{
IOCLR = IDE_RST; /*复位引脚置低 */
SYS_WaitInUS(30); /*延时大于 25 微秒 */
IOSET = IDE_RST; /*复位引脚置高 */
SYS_WaitInUS(5000); /*延时大于 2 毫秒 */
}
/*******************************************************************************************
** 函数名称,SYS_WaitInUS
** 功能描述,延时等级约 1 微秒函数,该函数根据系统时间不同与不同,但不应少到 1 微秒。
** 输 入,times,延时时间等级
** 输 出,无
** 全局变量,
** 调用模块,
*******************************************************************************************/
void SYS_WaitInUS(uint32 times)
{ uint32 c;
for(;0<times;times--)
for(c=0;c<5;c++);
}
3.设备检测函数
该函数用于检测 CF 卡插入及拔出,ZLG/CF 驱动要求系统必须提供该函数,即使系统中没有设计检测 CF 卡插入及拔出功能。
4.模拟 ATA 接口初始化函数
使用 LPC2210 的 GPIO 模拟 ATA 接口,定义了各引脚为输入或输出(见表 1.1) 。在使用我们定义好的引脚前,必须对 LPC2210 相关的寄存器进行配置。详见 1.1.3 小节。
1.1.3 LPC2210 相关寄存器配置
使用 GPIO 模拟 ATA 接口相关的寄存器只有几个,PINSEL1,PINSEL2,IO0DIR、
IO1DIR,IO2DIR,IO0SET,IO0CLR,IO1SET,IO1CLR,IO2SET,IO2CLR 等。
z PINSEL1 寄存器
表 1.2 描述了 PINSEL1 相关位的配置。
表 1.2 PINSEL1 相关位的配置
PINSEL1(0xE002C004) 描述 配置值 (二进制 )
1:0 引脚 P0.16 根据其它需要定义 XX
7:2 使引脚 P0.19:17 为 GPIO 000000
13:8 使引脚 P0.22:20 为 GPIO 000000
15:14 引脚 P0.23 根据其它需要定义 XX
23:16 引脚 P0.27:24 根据其它需要定义 XXXXXXXX
31:24 引脚 P0.31:28 根据其它需要定义 XXXXXXXX
z PINSEL2 寄存器
表 1.3 描述了 PINSEL2 相关位的配置。
表 1.3 PINSEL2 相关位的配置
PINSEL2(0xE002C014) 描述 配置值 (二进制 )
1:0 保留 00
3:2 使引脚 P1.36:26 为调试端口
使引脚 P1.25 为 GPIO
01
5:4 使 P2.31:16 为非数据总线 D31:16 01
7:6 根据其它需要设置 XX
11:8 假设使用片外 RAM、FLASH,使用低 16 位总线假设使用片外 RAM、FLASH,使能 WE、CS1
X00X
15:12 根据其它需要设置 XXXX
19:16 根据其它需要设置 00XX
23:20 使能 P2.29:28,其它根据需要设置;
假设使用片外 RAM、FLASH,使能 A0
X000
27:24 假设使用片外 RAM、FLASH,使能 A1:23 XXXX
31:28 保留 0000
即,PINSEL2 = (PINSEL2 & 0x0f8cf9c0) | 0x00000014。
z IO0DIR 寄存器
根据表 1.1 的描述,配置 IO0DIR 寄存器的位,见表 1.4。
表 1.4 IO0DIR 寄存器配置
IO0DIR 描述 配置值 (二进制 )
7:0 根据其它需要设置 XXXXXXXX
15:8 根据其它需要设置 XXXXXXXX
23:16 配置引脚 P0.17、P0.19、P0.21 为输出
配置引脚 P0.18、P0.20、P0.22 为输入
X010101X
31:24 根据其它需要设置 XXXXXXXX
即,IO0DIR= (PINSEL2 & 0xff81ffff) | 0x002a0000。
z IO1DIR 寄存器
根据表 1.1 的描述,配置 IO1DIR 寄存器的位,见表 1.5。
表 1.5 IO1DIR 寄存器配置
IO1DIR 描述 配置值 (二进制 )
7:0 根据其它需要设置 XXXXXXXX
15:8 根据其它需要设置 XXXXXXXX
23:16 配置引脚 P1.16、P1.17、P1.18、P1.19、
P1.20、P1.23 输出
配置引脚 P1.21 为输入
1X011111
31:24 配置 P1.24、P1.25 为输入;
根据其它需要设置
XXXXXX00
z IO2DIR 寄存器
根据表 1.1 的描述,配置 IO2DIR 寄存器的位,见表 1.6。
表 1.6 IO2DIR 寄存器配置
IO2DIR 描述 配置值(二进制)
7:0 根据其它需要设置 XXXXXXXX
15:8 根据其它需要设置 XXXXXXXX
23:16 读操作时设为输入,写操作时设为输出,初始化时设为输入
00000000
31:24 读操作时设为输入,写操作时设为输出,初始化时设为输入
00000000
z IO0SET、IO0CLR、IO1SET、IO 1CLR、IO2SET、IO2CLR 寄存器
改变各个 GPIO 输出引脚的状态位是通过 IO0SET,IO0CLR,IO1SET,IO1CLR,IO2SET、
IO2CLR 等寄存器设置的。初始化设置如表 1.7 所示,在读写 ATA 设备寄存器函数中分别进行适当的设置(如程序清单 1.2 和程序清单 1.3 所示) 。
表 1.7 初始化时 GPIO 输出引脚电平设置
寄存器 描述 配置值(十六进制)
IO0SET 初始化 P0.17、P0.19、P0.21 输出高电平 002A0000H
IO0CLR 初始化时不需要配置 -
IO1SET P1.19、P1.20 输出高电平 00180000H
IO1CLR P1.16、P1.17、P1.18、P1.23 输出低电平 00870000H
IO2SET 初始化时引脚 P2.31:16 为输入,该寄存器不需要配置 -
IO2CLR 初始化时引脚 P2.31:16 为输入,该寄存器不需要配置 -
根据以上寄存器的描述,使用模拟 ATA 接口前需对以上寄存器进行设置,对相关寄存器的设置也就是模拟 ATA 接口总线的初始化。再根据表 1.1 描述各引脚的输入或输出特性,设置各 GPIO 引脚为相应的状态。如程序清单 1.8 所示。
程序清单 1.8 ATA 接口初始化函数
/*******************************************************************************************
** 函数名称,ATA_BusIni
** 功能描述,ATA 总线初始化
** 输 入,无
** 输 出,出错返回 0
** 全局变量,
** 调用模块,
*******************************************************************************************/
void ATA_BusIni(void)
{
PINSEL1 &= 0xffffc003; /*使用到的 P0 口 GPIO 引脚设置 */
PINSEL2 = (PINSEL2 & 0x0f8cf9c0) | 0x00000014; /*使用到的 P1 及 P2 口 GPIO 引脚设置 */
IO0DIR &= (~(IDE_DMAREQ + IDE_INTRQ)); /*P0 相关输入引脚初始化 */
IO0DIR |= (IDE_RST + IDE_WR + IDE_RD); /*P0相关输出引脚初始化 */
IO1DIR &= (~(IDE_DMACK + IDE_IOCS16 + IDE_PDIAG)); /*P1 相关输入引脚初始化 */
IO1DIR|= (IDE_A0+IDE_A1+IDE_A2+IDE_CS0+IDE_CS1+IDE_CSEL);/*P1 相关输出引脚初始化 */
IO2DIR &= (~ATA_DATA); /*数据总线初始化为输入 */
IO0SET = IDE_RST + IDE_WR + IDE_RD; /*CF 卡复位引脚及读写信号引脚初始输出高电平 */
IO1SET = IDE_CS0 + IDE_CS1; /*CF 卡片选初始化输入高电平 */
IO1CLR = IDE_A0 + IDE_A1 +IDE_A2 + IDE_CSEL; /*地址初始化 */
}
1.1.4 中间件使用演示
CF 卡及 IDE 硬盘等大容量设备,通常都是以一定的文件格式存贮的。常用的文件格式为 FAT12,FAT16,FAT32,ZLG/FS 文件管理系统支持以上格式。该演示程序结合 ZLG/CF
驱动及 ZLG/FS 文件管理系统,实现对 CF 卡及 IDE 硬盘等大容量存贮设备文件创建及文件存取等操作。
1.编写文件系统的低层函数
参考 ZLG/FS 文件系统驱动程序设计指南,结合 ZLG/FC 中间件很容易编写出基于
ZLG/FS 文件管理系统 CF 存储卡的低层驱动。如程序清单 1.9 所示。
程序清单 1.9 ZLG/FS 文件系统 CF 卡驱动
#include "config.h"
/*******************************************************************************************
** 函数名称,CFCammand
** 功能描述,底层驱动程序与上层的接口程序
**
** 输 入,Cammand,DISK_INIT:驱动程序初始化
** DISK_CLOSE:关闭驱动器(移除驱动程序)
** DISK_CREATE_BOOT_SECTOR:重建引导扇区
** DISK_READ_SECTOR:读扇区
** DISK_WRITE_SECTOR:写扇区
** Parameter:剩余参数
** 输 出,DISK_READ_OK:读扇区完成
** DISK_READ_NOT_OK:读扇区失败
** DISK_WRITE_OK:写扇区完成
** DISK_WRITE_NOT_OK:写扇区失败
** DISK_INIT_OK:初始化完成
** DISK_INIT_NOT_OK:初始化失败
** BAD_DISK_COMMAND:无效的命令
** 全局变量,无
** 调用模块,无
******************************************************************************************/
uint16 CFCammand(uint8 Cammand,void *Parameter)
{
uint16 rt;
Disk_RW_Parameter * Dp;
Disk_Info *DiskInfo;
Dp = (Disk_RW_Parameter *)Parameter;
switch (Cammand)
{
case DISK_INIT,/*设备初始化 */
rt = DISK_INIT_NOT_OK;
if(ATA[0].ATAIsOK = TRUE) (1)
{
DiskInfo = GetEmptyDiskInfoAddr();
if (DiskInfo != NULL)
{
DiskInfo->DiakCommand = CFCammand;
DiskInfo->RsvdForLow = GetVolumeFirstSect(0); (2)
rt = DISK_INIT_OK;
}
}
break;
case DISK_CLOSE,
rt = RETURN_OK;
break;
case DISK_READ_SECTOR,/*读扇区 */
rt = DISK_READ_NOT_OK;
if(ATA_ReadSector(0,(uint16*)Dp->Buf,Dp->SectorIndex + Dp->RsvdForLow,1)) (3)
{
rt = DISK_READ_OK;
}
break;
case DISK_WRITE_SECTOR,/*写扇区 */
rt = DISK_WRITE_NOT_OK;
if(ATA_WriteSector(0,(uint16*)Dp->Buf,Dp->SectorIndex + Dp->RsvdForLow,1)) (4)
{
rt = DISK_WRITE_OK;
}
break;
default,
rt = BAD_DISK_COMMAND;
break;
}
return rt;
}
(1) 只有当 ATA 设备 0 可用时,才进行低层驱动初始化;
(2) 获取逻辑驱动器的起始扇区号,该函数源程序如程序清单 1.10 所示。
(3),Dp->SectorIndex”为文件管理系统要读取的逻辑扇区号,,Dp->RsvdForLow”为设备逻辑扇区的起始扇区号; ZLG/FS 读取的是逻辑扇区号,而逻辑扇区号与设备的实际扇区号可能不一致(与设备格式化有关),因此在读扇区操作时,应将逻辑扇区号转换为设备中的实际的物理扇区号;物理扇区号为逻辑扇区号与逻辑扇区起始扇区号之和。
(4) 写扇区操作也需和读扇区操作一样,应将需要写逻辑扇区号转换为设备中的实际的物理扇区号。
对于 CF 卡和 IDE 硬盘等大容量存贮器,初 FAT 文件管理系统格式化后,在逻辑盘的起始扇区前通常会包含隐藏扇区;包含隐藏扇区的 CF 卡或 IDE 硬盘,在设备的物理扇区的
0 号扇区(逻辑分区表)中特定的位置记录了逻辑盘的起始的物理扇区号;而 ZLG/FS 文件系统是面向逻辑盘的操作,所以在初始化低层设备驱动时,需获取逻辑盘的起始物理扇区号,
如程序清单 1.10 所示。
程序清单 1.10 获取逻辑盘的起始扇区号
uint32 GetVolumeFirstSect(uint8 Device)
{
uint8 buffer[512];
uint32 RelaStaSect;
ATA_ReadSector(Device,(uint16 *)buffer,0,1); (1)
if((buffer[510]==0x55)&&(buffer[511]==0xAA)) (2)
{
if(((buffer[0]==0xEB)&&(buffer[2]==0x90))||(buffer[0] == 0xE9)) (3)
{
RelaStaSect = 0;
}
else
{
RelaStaSect = buffer[454]+ (4)
buffer[455]*0x100 +
buffer[456]*0x10000 +
buffer[457]*0x1000000;
}
}
return RelaStaSect;
}
(1) 读设备的 0 号扇区。
(2) 如果该扇区为有效的分区表,该扇区的最后两个字节数据必为 0x55 和 0xAA。
(3) 如果该扇区为 DOS 引导扇区,该扇区的首字节必为 0xEB 或 0xE9,如果首字节为
0xEB 则第 2 个字节必为 0x90;如果设备的 0 号扇区为 DOS 引导扇区,则逻辑盘前没有隐含扇区,即逻辑盘起始扇区号为 0 号扇区。
(4) 如果该扇区为有效的分区表,则该扇区的 454— 457 节字保存的值为第一个卷(逻辑盘)的起始扇区号。
2.创建 ADS 工程
使用工程模板创建基于 uC/OS-II 的工程,将 ZLG/FS 文件管理系统的相关文件和
ZLG/CF 驱动添加到工程;将 uC/OS-II 源码程序及 ARM 的移值代码拷贝到适当的文件夹。
ZLG/FS 文件管理系统的配置使用默认值即可;由于 ZLG/FS 文件管理系统至少使用
uC/OS-II 操作系统 2 个事件,而 ZLG/CF 驱动使用 1 个信号量事件,所示配置 uC/OS-II 操作系统的事件数不能少于 3 个。
添加相关的头文件到,config.h”文件中,如程序清单 1.11 所示。
程序清单 1.11 添加头文件及配置
/* ZLG/FS需包含的头文件 */
#include "fat.h" (1)
#include "OSFile.h" (2)
extern char *strupr(char *Str); (3)
extern uint16 CFCammand(uint8 Cammand,void *Parameter); (4)
/*系统与 ZLG/CF 驱动接口函数的头文件 */
#include "SysATA.H" (5)
/* CF 存储卡需配置及包含的头文件 */
#define UCOSI (6)
#include "IDE.H" (7)
(1) ZLG/FS 文件管理系统所包含的头文件。
(2) ZLG/FS 文件管理系统基于 uC/OS-II 所需的头文件。
(3) strupr()函数的外部声明。 strupr()是标准库的函数,但是由于 ADS 编译器库没有包含该函数,所以需要编写该函数。
(4) CFCammand()为基于 ZLG/FS 文件管理系统读写 CF 卡的底层驱动,该函数详见程序清单 1.9。
(5) 系统与 ZLG/CF 驱动相关的接口函数所在,SysATA.c”文件的头文件。
(6) ZLG/CF 驱动基于 uC/OS-II 操作系统,需要定义,UCOSII”宏。
(7) ZLG/CF 驱动文件的头文件。
建立一个任务 TaskStart(),并在该任务初始化 ATA 接口,ZLG/FS 文件管理系统及对 CF
卡的文件进行操作。演示程序中:创建,ARM&FAT”文件夹;打开或创建“单片机,TXT”
文件,并在该文件末端插入一段字符。演示程序如程序清单 1.12 所示。
程序清单 1.12 编写任代码
#include "config.h"
OS_STK TaskStk[1024];
OS_STK TaskStartStk[1024];
char *strupr(char *Str) (1)
{
char *cp;
cp = Str;
while (*cp != 0)
{
if (*cp >= 'a' && *cp <= 'z' )
{
*cp -= 'a' - 'A';
}
cp++;
}
return Str;
}
void TaskStart (void *pdata);
int main(void)
{
OSInit();
OSTaskCreate(TaskStart,(void *)0,&TaskStartStk[1023],9); (2)
OSTaskCreate(OSFileTask,(void *)0,&TaskStk[1023],8); (3)
OSStart();
return 0;
}
char *FileName = "a:\\单片机,tx"; (4)
char WritFileDat[]= (5)
"\r\n******************Copyright (c) 周立功单片机发展有限公司 ***********************\r\n\
\r\n\
ZLG/FS是广州周立功单片机发展有限公司开发的面向嵌入式系统开发的小型文件系统,\r\n\
是 ZLG 系列中间件的重要成员之一。它是与 FAT12,FAT16,FAT32 高度兼容的文件系统,可以 \r\n\
直接与个人电脑交换文件。它是可移植的、可固化的文件系统,可以用于前后台系统,也可 \r\n\
用于多任务环境。目前 ZLG/FS 的最新版本为 1.0。 \
\r\n\
\r\n*********************************ARM 开发部门 ************************************\r\n";
void TaskStart (void *pdata)
{
HANDLE FHandle;
pdata = pdata;
SYS_BusIni(); /*ATA总线初始化 */ (6)
ATA_INI(); /*ATA 设备初始化 */ (7)
OSAddFileDriver(CFCammand); /*增加 ATA 设备 0*/ (8)
FHandle = OSFileOpen(FileName,"rw"); (9)
OSFileSek(FHandle,0 SEK_END); (10)
OSFileWrite(WritFileData,sizeof(WritFileData),FHandle); (11)
OSFileClose(FHandle); (12)
OSMakeDir("a:\ARM&FATS"); (13)
OSAllCacheWriteBack();
OSRemoveFileDriver(0);
while (1);
}
(1) 由于 ADS 编译器库中没有包含 strupr()函数,但该函数在 ZLG/FS 文件管理系统中使用到,所以必须编写该函数,该函数具有将小写字母转化为大写功能。
(2) 创建 TaskStart()任务。该任务对 ZLG/CF 驱动进行初始化,并对 CF 卡的文件进行操作。
(3) 创建 OSFileTask 任务。该任务为 ZLG/FS 文件管理系统的服务任务,详见文件管理系统章节。
(4) 将在 CF 卡内打开或创建的文件名。
(5) 将插入到打开文件末尾的字符串。
(6) 模拟 ATA 总线接口的初始化。
(7) 初始化 ATA 接口上的设备。
(8) 为 ZLG/FS 文件系统增加 CF 卡的驱动。
(9) 打开或创建“单片机,txt”文件,OSFileOpen()函数详见 ZLG/FS 文件管理系统章节。
(10) 将指针移到打开文件的末尾,OSFileSeek()函数详见 ZLG/FS 文件管理系统章节。
(11) 插入( 5)中的字符串到文件的指定位置,OSFileWrite()函数详见 ZLG/FS 文件管理系统章节。
(12) 关闭文件,OSFileClose()函数详见 ZLG/FS 文件管理系统章节。
(13) 创建,ARM&FATS”文件夹,OSMakeDir()函数详见 ZLG/FS 文件管理系统章节。
3.程序演示步骤
(1) 将 EasyARM2200 开发板配套光盘中的,CF_IDE_EXEMPLE” 文件夹内容拷到硬盘,
并将所有只读属性文件改为可写。
(2) 将 uC/OS-II 操作系统的源代码,拷贝到,CF_IDE_EXEMPLE\uCOSII_FAT_CF\
SOURCE”文件夹,由于 uC/OS-II 的版权原因,配套光盘没有提供源代码。
(3) 使用 ADS1.2 打开,\CF_IDE_EXEMPLE\uCOSII_FAT_CF\file\ uCOSII_FAT_CF.mcp”
工程文件。
(4) 编译工程,并进入硬件调试状态。
(5) 插入 CF 卡或接入 IDE 硬盘;如果接入的是 IDE 硬盘,则需将硬盘设置为主设备,
并连接上 IDE 硬盘电源( IDE 硬盘电源可由电脑电源提供,开发板不提供硬盘电源) 。
注意,CF 存储卡或 IDE 硬盘应已被格式化,未格式化的磁盘 ZLG/FS 不能识别。
(6) 调试程序,直到程序结束。
(7) 验证演示程序结果。
(8) 将 USB1.1 PACK 插入 EasyARM2200 开发板的 PACK 座上。
(9) 使用 AXD 装载,\CF_IDE_EXEMPLE\mass\mass_Data\Debug\mass.axf” USB 大容量类调试文件,然后全速运行程序(使用 EasyJTAG 硬件仿真) 。
(10) 连接上 USB 接口线,USB1.1 PACK 枚举及初始化完成后,“我的电脑”里会出现一个可移动的磁盘。
注意,如果是第一次使用该程序,电脑需要安装驱动程序; Win98/Me 的驱动在,\CF_IDE_EXEMPLE\
mass\D12MassStorage_SW(Win98_Ver1.0)”文件夹中,Win2000/XP 系统不需驱动;驱动安装步骤与一般 U 盘安装步骤一样。
(11) 点击打开新发现的磁盘,就可以发现演示程序创建的,ARM&FATS”文件夹和“单片机,txt”文件;点击打开“单片机,txt”文件,就可以看到文件末尾插入的字符串。
注意,光盘中的 USB 大容量程序只是为了验证本演示程序的结果,本书中并没有介绍 USB 大容量类程序内容;如果对 USB 大容量类程序感兴趣,敬请留意周立功单片机发展有限公司出版的相关图书。