SSP 并口
1. 并口SPP
1.1 历史
最初的并口设计是单向传输数据的,也就是说数据在某一时刻只能实现输入或者输出。后来IBM又开发出了一种被称为SPP(Standard Parallel Port)的双向并口技术,它可以实现数据的同时输入和输出,这样就将原来的半互动并口变成了真正的双方互动并口;Intel、Xircom 及Zenith于1991年共同推出了EPP(Enhanced Parallel Port,增强型并口),允许更大容量数据的传输(500~1000byte/s),其主要是针对要求较高数据传输速度的非打印机设备,例如存储设备等;紧接着EPP的推出,1992年微软和惠普联合推出了被称为ECP(Extended Capabilities Port,扩展并行口)的新并口标准,和EPP不同,ECP是专门针对打印机而制订的标准;发布于1994年的IEEE 1284涵盖了EPP和ECP两个标准,但需要操作系统和硬件都支持该标准,这对现在的硬件而言已不是什么问题了。目前我们所使用的并口都支持EPP和ECP这两个标准,而且我们可以在BIOS当中自己设置并口的工作模式。
1.2 管脚和寄存器介绍
标准的PC并口有25针和37针两种,分别称为D-subh和Centronics,具体管脚定义见表4.1-4.3。
标准的PC并口使用3个8位的端口寄存器,这些端口寄存器与并行口管脚存在对应关系,我们可以通过这三个端口寄存器去监控并口管脚。这三个端口寄存器依次是数据寄存器、状态寄存器和控制寄存器。
1.2.1 数据寄存器
数据端口或称数据寄存器保存了写入数据输出端口的一字节信息。数据端口可以写入数据,也可以读出数据(即可擦写);写进去的当然是我们希望从数据端口引脚输出的数据,不过读进来的也只是我们上次写进去的数据,或是原来保留在里面的数据,并不是从端口引脚输入PC的数据。
表4.1 SPP模式下的信号定义1
|
数据寄存器(基地址) |
|||||
|
位 |
引脚:D-sub |
信号名 |
信号源 |
是否在连接器处倒相 |
引脚:Centronics |
|
D0 |
2 |
数据位0 |
PC |
否 |
2 |
|
D1 |
3 |
数据位1 |
PC |
否 |
3 |
|
D2 |
4 |
数据位2 |
PC |
否 |
4 |
|
D3 |
5 |
数据位3 |
PC |
否 |
5 |
|
D4 |
6 |
数据位4 |
PC |
否 |
6 |
|
D5 |
7 |
数据位5 |
PC |
否 |
7 |
|
D6 |
8 |
数据位6 |
PC |
否 |
8 |
|
D7 |
9 |
数据位7 |
PC |
否 |
9 |
1.2.2 状态寄存器
状态端口或称状态寄存器保存的是5个输入(S3-S7)的逻辑状态。S0-S2位不出现在并口连接器中。除了S0以外,状态寄存器是只读的,读出数据信息是状态端口引脚上的逻辑状态。S0是支持EPP传输并口的超时标志信息,可以用软件方法清零。在许多并口中,状态输入接有上拉电阻。其定义如下:
表4.2 SPP模式下的信号定义2
|
状态寄存器(基地址+1) |
|||||
|
位 |
引脚:D-sub |
信号名 |
信号源 |
是否在连接器处倒相 |
引脚:Centronics |
|
S0 |
|
Time-Out |
|
|
|
|
S1 |
|
未使用 |
|
|
|
|
S2 |
|
未使用 |
|
|
|
|
S3 |
15 |
nError |
外设 |
否 |
32 |
|
S4 |
13 |
Select |
外设 |
否 |
13 |
|
S5 |
12 |
Paper Out |
外设 |
否 |
12 |
|
S6 |
10 |
nAck |
外设 |
否 |
10 |
|
S7 |
11 |
Busy |
外设 |
是 |
11 |
|
注:有些PC上,0、1、2位均未定义 |
|||||
1.2.3 控制寄存器
控制端口或称控制寄存器保存了C0-C3的4位控制信息。一般来说,这些位被用来输出,然而大多数SPP中,控制位为集电极开路/漏极开路模式,也就是说,它们同样可以用作输入。要从控制位上读取外部逻辑信号,首先先向相应位输出写入“1″,然后读取控制寄存器的值即可。但是,为了提高交换速度,大多数支持EPP和ECP的接口中,控制位工作在不能用作输入的推拉模式下。在一些多模式接口中,控制位采用的是改进型的推拉模式,可以用作输入。其定义如下:
表4.3 SPP模式下的信号定义3
|
控制寄存器(基地址+2) |
|||||
|
位 |
引脚:D-sub |
信号名 |
信号源 |
是否在连接器处倒相 |
引脚:Centronics |
|
0 |
1 |
nStrobe |
PC |
是 |
1 |
|
1 |
14 |
nAutoLF |
PC |
是 |
14 |
|
2 |
16 |
nInit |
PC |
否 |
31 |
|
3 |
17 |
nSelectIn |
PC |
是 |
36 |
| 注:连接器中没有提供的附加位有:
4:中断启用:为1时,IRQ从nAck送往系统的中断控制器;为0时,IRQ不送往中断控制器。 5:双向控制端口的方向控制位:为0时,输出启动;为1时,不能输出;控制端口可以读取外部逻辑电平。 6、7:未定义。 |
|||||
1.2.4 一些重要说明
1. 对于PC自带的并口(我们称之为原生并口),必须在BIOS中设置为ECP或EPP模式;
2. 上面几个表中的基地址一般如下:并口1为0×378,并口2为0×278,他们都是计算机自带的标准并口。当我们的PC机没有自带并口时,需要采用一些转换器,此时基地址将由转换器的驱动具体指定。如我的PC上装的PCI转并口设备模拟的并口的基地址为0xC400;
3. 数据寄存器对应的引脚属于输入/输出口,状态寄存器对应的引脚属于输入口,控制寄存器对应的引脚属于输出口;
4. 根据模式的不同,并口的信号定义也不同;
2.1 linux下对并口的编程
本节主要讨论在Linux如何通过操作前面提到的3个寄存器来达到操作并口管脚的目的。
2.1.1 Linux下如何访问IO端口
Linux下应用程序运行在保护模式下,不能直接对IO端口进行存取,需先通过ioperm取得端口的控制权。Linux的ioperm系统调用允许本地读/写访问系统的IO端口,但仅限于0-0×3FF这1024个端口,其它端口的访问级别可通过iopl更改。ioperm系统调用的形式如下:
#include <unistd.h> /* for libc5 */
#include <sys/io.h> /* for glibc */
int ioperm(unsigned long port, unsigned long num, int turn_on);
在这个系统调用中,port指端口的基地址,num是连续的端口数目,turn_on表明是获得控制权(turn_on=1),还是释放控制权(turn_on=0)。
Jflash源码中,ioperm(LPT1, 3, 1)即:获取并口1的基地址开始之后连续三个端口(数据寄存器、状态寄存器和控制寄存器)的控制权。
在获取控制权后,即可以通过outb和inb等相关函数对IO端口进行写、读操作。
Jflash中相关代码如下:
#define _inp(a) inb(a)
#define _outp(a, d) outb(d, a)
#define OutputPpt(value) _outp((unsigned short)validPpt, value)// 将value写入validPpt,即写入数据寄存器,最后通过并口输出
#define InputPpt() _inp((unsigned short)(validPpt+0×1))// 读入状态寄存器的数值,实际是读取状态寄存器对应的并口管脚
2.1.2 Jflash源码中并口操作部分分析
上一小节中讨论了Jlash源码中最基础的三条函数,本节继续讨论Jflash中并口操作相关的其它函数。
/********************************************************************
* 功能: 设置port 端口开始之后的3个地址为可访问
* 参数: port: 欲设置访问权限的端口
* 返回值: 成功返回1, 失败返回0
********************************************************************/
static int io_access_on(unsigned long port)
{
if (ioperm (port, 3, 1)) {
perror(”ioperm()”);
return 0;
}
if (ioperm (0×80, 1, 1)) {
perror(”ioperm()”);
return 0;
}
return 1;
}
/********************************************************************
* 功能: 设置port 端口开始之后的3个地址为不可访问
* 参数:port: 欲设置访问权限的端口
* 返回值: 无
********************************************************************/
static void io_access_off( unsigned long port )
{
ioperm (port, 3, 0);
ioperm (0×80, 1, 0);
}
/********************************************************************
* 功能: 获取有效的端口号
* 参数: 无
* 返回值: 成功返回LPT1-LPT3中有效的端口, 失败返回0
********************************************************************/
int GetValidPpt(void)
{
if( io_access_on(LPT1) ){// LPT1是否可访问
_outp(LPT1, 0×55);// 若LPT1可访问,则通过写入0×55再读入对比来确认其是否可操作
if((int)_inp(LPT1) == 0×55)
return LPT1;
io_access_off(LPT1);
}
……// 如上依次检测LPT2、LPT3是否可访问
return 0;
}
/********************************************************************
* 功能: 设置并口为兼容模式
* 说明: 由于大部分PC 在PC 中已设置并口为ECP 或EPP模式, 故本程序中没必要再设置
********************************************************************/
#define ECP_ECR (0×402)
#define ECR_STANDARD (0×0)
#define ECR_DISnERRORINT (0×10)
#define ECR_DISDMA (0×0)
#define ECR_DISSVCINT (0×4)
void SetPptCompMode(void)
{
//configure the parallel port at the compatibility mode.
//_outp(validPpt+ECP_ECR, ECR_STANDARD | ECR_DISnERRORINT | ECR_DISDMA | ECR_DISSVCINT);
}
2.2 JTAG与并口的硬件连接
关于JTAG引脚与并口的连接,用户可以根据自己的想法进行,只要与软件中设定能对应就ok。目前,有两种所谓”标准”接法:Sdt 和Wiggler 接法。
下表列出来这两种接法:
表4.5 JTAG与并口的硬件连接
|
JTAG管脚 |
SDT接法 |
Wiggler接法 |
||
|
并口管脚 |
并口管脚号 |
并口管脚 |
并口管脚号 |
|
|
TCK |
D0 |
2 |
D2 |
4 |
|
TMS |
D1 |
3 |
D1 |
3 |
|
TDI |
D6 |
8 |
D3 |
5 |
|
TDO |
Select/STATUS[4] |
13 |
Busy/STATUS[7] |
11 |
|
nTRST |
D2 |
4 |
D0 |
2 |
这里有个问题要着重注意下:Wiggler采用Busy脚做为TDO,其管脚在并口硬件上有个反向器。所以我们需要将读入的电平反向;SDT接法则没有这个必要。
3 Window下的JTAG开发
上面讨论的是在linux底下做JTAG开发。本章简单讨论下windows下的JTAG开发。
从Jflash源码来看,除了并口操作要用到IO口,是与OS相关的,其它都符合ANSI C,不需要做移植。也就是说,只要解决好windows下的并口编程就ok了。
在windows NT架构的操作系统(如XP)底下,应用程序同样不能直接访问IO口,我们同样需要先获取IO端口的控制权,而后使用API去操作他们。
3.1 如何获取IO端口控制权
为了在windows下实现对并口的访问,我们必须安装一些驱动或程序库。可能的手段主要有:
1. winio程序库
WinIO程序库允许在32位的Windows应用程序中通过物理地址直接对I/O端口和物理内存进行存取操作。通过使用一种内核模式的设备驱动器和其它几种底层编程技巧,它绕过了Windows系统的保护机制。
2. GIVEIO驱动
安装该驱动后,应用程序可以直接通过IO的物理地址进行直接访问。
Windows下的SJF烧写程序都使用了该驱动。这也是为什么在用SJF烧写vivi等Bootloader前必须先安装GIVEIO驱动的原因。
关于GIVEIO的源码以及如何使用它,大家可到网上搜索下载,也可参考SJF源码。
3.2 IO端口的操作API
在使用GIVEIO驱动取得访问权后,可使用底下两个接口直接操作IO端口:
#include <conio.h>
int _inp(unsigned short port);
port参数为指定的输入端口号。调用后,它从port参数指定的端口读入并返回一个字节,输入值可以是在0-255范围内的任意无符号整数值。
int _outp(unsigned short port, int databyte);
port 参数为指定的输出端口号,databyte 参数为输出的值。调用后,它将databyte参数指定的值输出到port 参数指定的端口并返回该值。databyte 可以是0-255范围内的任何整数值。
这两个函数都没有错误值返回。在Win98下可以直接用, 但在NT下必须要挂驱动程序。
3.3 关于并口的端口号
当使用转并口设备时,并口端口号会比较特殊。这时,我们只需再定义一个LPT4,并在ppt.c的GetValidPpt函数中增加对该并口的支持就ok了。
我的PCI转并口设备的端口号是0xC400,以下是我新增的代码:
#define LPT4 0×0C400
int GetValidPpt(void)
{
……
if( io_access_on(LPT3) ){
_outp(LPT3, 0×55);
if((int)_inp(LPT3) == 0×55)
return LPT3;
io_access_off(LPT3);
}
return 0;
}
该日志未加标签。
相关日志
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.
