C (言語)でパラレルポートの2線式ソフトウェアを書く

要約

このアプリケーションノートは、アプリケーションノートAN3230の中に記述されているパラレルポートハードウェアを用いるためのパラレルポート2線式ソフトウェアを書くためのソースコードと取り扱い方法を説明します。

参照:アプリケーションノート「Application Note: Adding Windows NT/2000/XP Support to the AN3315 Parallel-Port 2-Wire Software」

はじめに

このアプリケーションノートは、アプリケーションノートAN3230の中に記述されているパラレルポートハードウェアのための2線式ソフトウェア開発技術者のために提供されるC (言語)のソースコードについて説明します。ソースコードは、ダラスセミコンダクタのFTPサイトから無償で入手することができ、Windows® 95またはWindows 98オペレーティングシステムを使用しているPC上でも実行することができます。さらに、パラレルポートハードウェアの簡単な評価およびデバッギングをするために使える基本的な2線式通信ソフトウェアを提供する簡単なWindowsプログラムも、同じFTPサイトから入手することができます。ハードウェアについての詳細に関してはアプリケーションノートAN3230を参照してください。

このアプリケーションノートの中で提供されているソフトウェアは、お客様が評価のためにそのまま使用することができるように無償で提供しています。ダラスセミコンダクタは、このソフトウェアが引き起こすかもしれない、いかなる損害に対して一切責任を負わないものとします。お客様自身の責任でこのソフトウェアをお取り扱いください。

ソフトウェアの実行要件

「はじめに」で述べたとおり、このプログラムは、Windows 95またはWindows 98オペレーティングシステムを装備したPC上で実行する必要があります。このソフトウェアは直接パラレルポートにアクセスするため、Windows NTベースのオペレーティングシステムではこのタスクを遂行するためのドライバが必要です。

さらに、複数のパラレルポート動作モードがありますが、それらのなかには、このソフトウェアと互換性がないものもあります。うまく動作する2つのモードはEPPとECPで、ほとんどのPCのパラレルポートモードは、BIOS設定で変更することができます。

ソースコードの説明と使い方

ソースコード(Appendix A参照)はANSI Cで書かれています。したがって、どんなCコンパイラとも互換性があるはずです。できる限り簡単に使えるように、すべてのコードと宣言は一つのファイル「2wire.c」で作成してあり、リソースを使うためにプロジェクトの中に含まれなければならない(別個の)ヘッダーファイルは存在しません。

2線式ソースコードの使い方:

  1. 「2wire.c」ファイルをプロジェクトディレクトリに配置してください。
  2. パラレルポートにアクセスするプログラムの先頭に「#include "2wire.c"」宣言を追加してください。
  3. パラレルポートを選択するためにParPortSelect(1)をコールしてください。引数は利用するポート番号(例えば表示例のように LPT1)を決定します。有効なポート番号は1、2および3です。ほとんどのPCはLPT1を使います。
  4. 表3で示されているChangeDelayCount(int i)コマンドをつかって2線式インタフェースのタイミングを較正してください。
  5. 表1の「Basic 2-Wire Functions」、又は表2の「Multiple-Byte 2-Wire Functions」のいずれかの関数をコールしてください。

パラレルポートのタイミングは、ソフトウェアを実行しているPCの速度に大きく依存します。そのため、コンピュータごとのタイミング変化によって信頼性が高い通信の確立が困難になります。この問題を解決するためには、高速なコンピュータ上のタイミングが、インタフェース速度の最大値(fast modeのデバイスで400kビット/秒)を超過しないようにするために可変長の遅延をSDAとSCL信号間に挿入します。遅延時間は、ChangeDelayCount(int i)関数をコールすることによって制御されます。この関数は、PCが実行する短い遅延時間{10個のNOPと1個のfor( ; ; )ループの実行時間}の回数を変更します。デフォルト値のiは1000で、この値は、P3 600MHzマシン上で適度に遅い通信を提供します。これは、多くのPCで適度な速度(性能)になり、確実に動作するはずです。より小さいiの値は、インタフェースの速度を上げますが、プログラマは確実に、2線式デバイスの仕様以内の通信速度にしなければなりません。より速いマシン上では、より大きなiの値が、通信を確立するために要求されます。これはデバッキングが必要な場合には、少し考察が必要です。

「Basic 2-Wire Functions」は、2線式デバイスにアクセスするほとんどのアプリケーションに使えるでしょう。開始(START)条件の送信、バイトのライト/リード、および、停止(STOP)条件を送信する機構は、これらのルーチンの中で処理されます。したがって、デバイスとの通信で最後に残る障害は、タイミングとコールされるルーチンの順序だけです。これらのルーチンを使うには、これまで説明したようにタイミングを調節し、デバイスのデータシートを読んで、アクセスすべきレジスタをコールする順序を決定してください。

「Multiple-Byte 2-Wire Functions」は、1つのコマンドでデバイスから最大256バイトのリード/ライトに使用することができます。しかし、すべてのデバイスが通信中に同じデータシーケンスを利用するとは限リません。もしこれらの関数を使うことを考える場合には、提供されたソースコードが、アクセスされているデバイスと互換性があるかどうか確認してください。複数バイトをライト/リードする主な利点は、複数バイトを単一コマンドで送信するめ、単一バイトをライト/リードするために複数のコマンドを送信するのに対比して、アプリケーションのために必要とされるコール回数を抑えることです。複数バイトのライト/リードルーチンは、デバイスアドレスを設定するためにSetSlaveAddress()コマンドを使います。したがって、SetSlaveAddress()は、複数バイトのライト/リードコマンドを使う前に、コールされなければなりません。

「Enable LED/Disable LED」関数は、AN3230の中で示されているLEDをステータス表示として使用することを可能にします。「SetStrobe/ClearStrobe」関数は、LED端子をオシロスコープのトリガーとして使用することを可能にします。これらの関数は、ハードウェアとソフトウェアの問題をデバッグするのに非常に役立ちます。

表1. Basic 2-Wire Functions

FUNCTION PROTOTYPE FUNCTION DESCRIPTION RETURN VALUE
int Start() Generates 2-Wire start condition. Can also be called to generate a re-start condition. 1
int Stop() Generates 2-Wire stop condition 1
int WriteData(unsigned char ucData); Writes the argument to the slave 1 if slave acknowledges,
0 if slave does not acknowledge
int ReadDataAck(unsigned char *ucData); Reads the data byte from slave to ucData and acknowledges 1
int ReadDataNack(unsigned char *ucData); Reads the data byte from slave to ucData and does not acknowledge 1
int ResetBus() Clocks SCL 9-times then generates a stop condition 1

表2. Multiple-Byte 2-Wire Functions

FUNCTION PROTOTYPE FUNCTION DESCRIPTION RETURN VALUE
int SetSlaveAddress(unsigned char ucADDR) Sets the slave address for multiple-byte read and write accesses 1
int WriteBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256]) Writes iCount bytes to the slave at the device address set by SetSlaveAddress(), beginning at memory address set by ucMemAddr. 1 if slave acknowledges,
0 if slave does not acknowledge any byte.
Int ReadBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256]) Reads iCount bytes to the slave at the device address set by SetSlaveAddress(), beginning at memory address set by ucMemAddr. 1 if slave acknowledges during command writes,
0 if slave does not acknowledge during command writes.

表3. 追加のポートセットアップおよびデバッグ関数

FUNCTION PROTOTYPE FUNCTION DESCRIPTION RETURN VALUE
int ParPortSelect(int iLPT) Sets parallel port access variables to specified port number. iLPT = 1 for LPT1. 1 for successful change
0 for failure
int ChangeDelayCount(int iCount) This determines the "i" value used with the DelayASMx10() command during SDA and SCL communications. Call this function with higher i values to make communication slower. The default value is 1000, which should provide moderate to slow communication speed. This is a safe value for i. Most PCs will be able to use a lower value of i to speed up communications. 1
void DelayASMx10(int i) Delays 10 clock cycles per i. This is called as the delay that determines SDA and SCL timing. It does not need to be called by the software developer, it is already embedded into the start/stop/read/write commands. NULL
int EnableLED() enables LED in AN3230 circuit 1
int DisableLED() disables LED in AN3230 circuit 1
int SetStrobe() Sets the LED pin in the AN3230 circuit high for oscilloscope triggering 1
int ClearStrobe() Sets the LED pin in the AN3230 circuit low for oscilloscope triggering 1

Basic 2-Wire Functionの例

このセクションでは、「Basic 2-Wire Function」を使用して、DS1086のDACレジスタに2バイトを書き込み、続いてそれらを読み出す方法を説明します。DS1086のスレーブアドレスはB0hで、DACレジスタはメモリアドレス08hから始まる2バイトです。

アドレス08hと09hに0180hを書き込むためには次の手順が使えます。

unsigned char fail = 0;

Start();                   // Generates Start Condition
fail |= !WriteData(0xB0);  // Writes the slave address
fail |= !WriteData(0x08);  // Writes the memory address of the DAC register
fail |= !WriteData(0x01);  // Writes the MSB of the DAC register
fail |= !WriteData(0x80);  // Writes the LSB of the DAC register
Stop();                    // Generates Stop Condition
if(fail == 1)
      Error("Device failed to acknowledge during write attempt");

DACレジスタに書き込まれた2バイトを読み出すためには、次のコードが使えます。

unsigned char ucDataMSB=0;        // define variable for MSB data to be stored after the read
unsigned char ucDataLSB=0;        // define variable for LSB data to be stored after the read
unsigned char fail = 0;

Start();                          // Generate Start Condition
fail |= !WriteData(0xB0);         // Write the slave address, LSbit=0 to signify write byte
fail |= !WriteData(0x08);         // Write the memory address of the DAC register
Start();                          // Generates a re-start condition
WriteData(0xB1);                  // Writes the slave address, LSbit=1 to signify read byte
ReadDataAck(&ucDataMSB);          // Reads the MSB of DAC and sends acknowledgement to the slave
ReadDataNack(&ucDataLSB);         // Reads the LSB of DAC and does not acknowledge the slave
Stop();                           // Generates Stop Condition
if(fail ==1)
      Error("Device failed to acknowledge during read attempt");

Windowsパラレルポート2線式ソフトウェア

図1のソフトウェア画面は、「2wire.c」ソフトウェアの基本機能を実証するために書かれたものですが、同様にAN3230ハードウェアをデバッグにも使うことができます。

図1. パラレルポート2線式ソフトウェアのウインドウ画面
図1. パラレルポート2線式ソフトウェアのウインドウ画面

このソフトウェアは、[Parallel Port Select]の項でリストされた3個のパラレルポートのうちのどれかとの通信が可能です。

[2-Wire Functions]部の中のボタンは、単にダイアログボックスの中のパラメータを受け付けて、対応する「2wire.c」関数をコールします。[Start]はSTART条件を生成します。[Write Data]は、このボタン右側のボックスのパラメータを取得して、スレーブ(デバイス)に書き込みます。2つの[Read/ack] [Read/nack]ボタンは、両方ともスレーブから1バイトを読み出しますが、1つはデータ転送に受信応答をしますが、もう1つはデータ転送に受信応答をしません。[Stop]ボタンは[STOP]条件を生成します。[Write Byte]ボタンは、[2-Wire Device address]ボックスの中の(アドレス)値を、LSbitを[0]にして書き込みます。そして[Read]ボタンは、同じ値を、LSbitを[1]にして書き込みます。これらのボタンは、[Write Data]ボタンが、データおよびメモリアドレスのために、アクセスされる部分ごとにスレーブアドレスの値を煩雑に変更せずに使えるようにします。

[One-Byte write]は、[2-Wire Device Address]ボックスの中でリストされたスレーブアドレスの[Addr]ボックスでリストされたメモリアドレスへ単一データバイト(データ)を送信します。

[One-Byte read]は、[2-Wire Device Address]ボックスの中でリストされたスレーブアドレスの[Addr]ボックスでリストされたメモリアドレスから1バイトを読み出します。このダイアログは、応答(acknowledgement)表示がなく、単に1バイトを読み出しているだけです。

[Two-Byte write]は、[2-Wire Device Address]ボックスの中でリストされたスレーブアドレスの[Addr]ボックスでリストされたメモリアドレスへ2個のデータバイト(MSBデータおよびLSBデータ)を送信します。

[Two-Byte read]は、[2-Wire Device Address]ボックスの中でリストされたスレーブアドレスの[Addr]ボックスでリストされたメモリアドレスから2個のデータバイトを読み出します。このダイアログは、読み出した最初のデータバイトに対しては受信応答をしますが、読み出した2番目のデータバイトに対しては受信応答をしません。

[Find Address(es)]ボタンは、スレーブがどのアドレスを持っているか決定するために、2線式バス上へすべてのスレーブアドレス(00h-FEh)を書き込んで、受信応答をチェックします。受信応答を返したアドレスは、[Status]ボックスにリストされます。このボタンは、AN3230ハードウェアが正しくセットアップされているかどうかおよびスレーブが2線式バスに接続されてデータを受信できるかどうかを決定するのにも使えます。

[Comm. Delay]ボタンは、ボタン右側の整数を引数にしてChangeDelayCount()関数をコールします。これは、2線式インタフェースのタイミング調整に使えます。

[LED]ボタンは、AN3230回路に示されている表示LEDをイネーブル/ディセーブルします。[Strobe Enable]ボタンは、このソフトウェアが、1および2バイトをライト/リード動作する前にLEDピンをHighにセットし、通信終了後にLEDピンをLowにセットするように設定をします。

[Test Circuit]ボタンは、ループバックテストを実行し、SDA出力がLowに設定されている時にSDA入力がLowをリードしていること、および、High状態のときも同様に確認します。また、LED端子をLowおよびHighに設定して、ユーザがLEDの点滅を確認できる十分な休止時間を設けてテストします。

上記のすべてのコマンドは、ユーザに、[Status]ウィンドウの中でフィードバックを提供します。

結論

このアプリケーションノートでは、AN3230の中で示された[parallel port 2-Wire circuit]を使って、「2wire.c」ファイルで提供されるソースコードで、2線式デバイスのプログラミングが簡単であることを検証しました。「2wire.c」の中のルーチンは信号伝達機能を遂行しますが、これによりプログラマがインタフェースタイミングの調整および2線式デバイスのデータシートで概説されている通りに2線式ルーチンを適切な順序でコールすることに専念することを可能にします。

「2wire.c」のソースコードは、ダラスセミコンダクタのFTPサイトからダウンロードして入手することができるウィンドウズプログラムです。

http://files.dalsemi.com/system_extension/AppNotes/

このアプリケーションノートに関する質問は、Dallas Semiconductor Mixed Signal Applications group、宛に英語でお問い合わせください。

付録A - ソースコード

///////////////////////////////////////////////////////////////////////////////
// 2-Wire Parallel Port Functions for Win95/Win98
//
// Dallas Semiconductor (C) 2004
//
// This software is free and available "as is" for use by Dallas
// Semiconductor's customers. Dallas Semiconductor accepts no liability for any
// damages the software may cause. Use at your own risk.
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Include Files

#include <conio.h>


///////////////////////////////////////////////////////////////////////////////
// Definitions

#define LPT1 0x378
#define LPT2 0x3BC
#define LPT3 0x278
#define LPT4 0x378       // not implemented - set to LPT1 for now,
                                     // returns error code if assigned


///////////////////////////////////////////////////////////////////////////////
// Global Variables
unsigned short LPTData;           // parallel port base address
unsigned short LPTStatus;         // LPT Base Address + 1
unsigned short LPTControl;        // LPT Base Address + 2

unsigned char ucDeviceAddress;    // 2-Wire device address for
                                  // multibyte communication

int   DCNT = 1000;                // Delay Count...Sets the delay time used for SDA/SCL
      // during 2-Wire communication. DCNT=25 works well for MY P3 600MHz
      // Laptop. 1000 is the default value providing some guardband for
      // faster machines so they do not attempt to talk faster than the
      // 400kHz rating of the 2-Wire interface. This value can be changed
      // high or lower to find the best value using ChangeDelayCount();


///////////////////////////////////////////////////////////////////////////////
// Function Prototypes

// Parallel Port Utility Functions

void DelayASMx10(int i);                 // delays 10 clock cycles per i
int ChangeDelayCount(int iCount);        // changes i for DelayASMx10
int ParPortSelect(int iLPT);             // sets access variables to specified
      // port. Must be called before any other parallel port functions

// Basic 2-Wire Functions
int Start();                                    // 2-Wire Start Command
int Stop();                                     // 2-Wire Stop Command
int WriteData(unsigned char ucDATA);            // 2-Wire Write Byte
int ReadDataNack(unsigned char *ucDATA);        // 2-Wire Read Byte with NACK
int ReadDataAck(unsigned char *ucDATA);         // 2-Wire Read Byte with ACK

// MultiByte Write/Read 2-Wire Functions

int SetSlaveAddress(unsigned char ucADDR);      // sets slave address for
      // WriteBytes and ReadBytes Commands
int WriteBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256]);
      // Write upto 256 bytes to device address set by SetSlaveAddress.
int ReadBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256]);
      // Reads upto 256 bytes from device address set by SetSlaveAddress.

// Utility 2-Wire Functions

int ResetBus();                   // clocks SCL 9 times and performs stop to "free"
                                  // SDA and SCL for the software master.
int SetStrobe();                  // sets strobe pin for debugging (AN3230)
int ClearStrobe();                // clears strobe pin for debugging (AN3230)

// Enable / Disable LED on AN3230 circuit

int EnableLED();                  // enables LED in AN3230 circuit
int DisableLED();                 // disables LED in AN3230 circuit


///////////////////////////////////////////////////////////////////////////////
// Function Definitions

void DelayASMx10(int iDelayCount) // delays 10 NOPs iDelayCount times.
{ // See DCNT description in variable declarations for more information
      int iLoopCount = 1;
      for (; iLoopCount<=iDelayCount; iLoopCount++)
      {
            __asm
            {
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
                  NOP
            }
      }
}
int ParPortSelect(int iLPT)
{     // Selects the parallel port used for operation.
      if(iLPT == 1)
      {
            LPTData = LPT1;
            LPTStatus = LPTData + 1;     // LPT Data + 1
            LPTControl = LPTData + 2;    // LPT Data + 2
            return 1;                          // legal port value
      }
      else if(iLPT == 2)
      {
            LPTData = LPT2;
            LPTStatus = LPTData + 1;     // LPT Data + 1
            LPTControl = LPTData + 2;    // LPT Data + 2
            return 1;                          // legal port value
      }
      else if(iLPT == 3)
      {
            LPTData = LPT3;              // legal port value
            LPTStatus = LPTData + 1;     // LPT Data + 1
            LPTControl = LPTData + 2;    // LPT Data + 2
            return 1;                          // legal port value
      }
      else if(iLPT == 4)
      {
      LPTData = LPT4;                    // not implemented,but defined as LPT1
      LPTStatus = LPTData + 1;           // LPT Data + 1
      LPTControl = LPTData + 2;          // LPT Data + 2
      return 0;                          // return 0 for unimplemented value
      }
      else
            return 0;                    // illegal value, return 0 for error
      }

int ChangeDelayCount(int iCount)         // changes i for DelayASMx10
{     // Changes the DCNT to increase/decreas delays between 2-Wire edges
      DCNT = iCount;       // See DCNT declaration in variable declarations.
      return 1;
}
int EnableLED()
{     // Enables the LED shown on the AN3230 circuit
      unsigned char Data;

      // Read original value of control byte
      Data = _inp(LPTControl);

      // Turn LED on
      _outp(LPTControl, (Data & 0xF7));        // Clear P17 to turn LED on
      return 1;
}

int DisableLED()
{     // Disables the LED shown on the AN3230 circuit
      unsigned char Data;

      // Read original value of control byte
      Data = _inp(LPTControl);

      // Turn LED on
      _outp(LPTControl, (Data | 0x08));        // Clear P17 to turn LED on
      return 1;
}

int Start()
{     // Performs 2-Wire start condition
      unsigned char Data;

      // Read original value of data byte
      Data = _inp(LPTData);

      // Ensure SDA and SCL are high

      // Set SDA high (write D1 to a 0)
      Data = Data & 0xFD;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      // Set SCL high (write D0 to a 0)
      Data = Data & 0xFE;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

// Bring SDA low, then bring SCL low

      // Bring SDA low (write D1 to a 1)
      Data = Data | 0x02;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      // Bring SCL low (write D0 to a 1)
      Data = Data | 0x01;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      return 1;
}

int Stop()
{     // Performs 2-Wire stop condition
      unsigned char Data;

      // Read original value of data byte
      Data = _inp(LPTData);
      // Ensure SDA and SCL are low

      // Make SCL low (write D0 to a 1)
      Data = Data | 0x01;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      // Make SDA low (write D1 to a 1)
      Data = Data | 0x02;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      // Bring SCL high, then bring SDA high

      // Bring SCL high (write D0 to a 0)
      Data = Data & 0xFE;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      // Bring SDA high (write D1 to a 0)
      Data = Data & 0xFD;
      _outp(LPTData, Data);
      DelayASMx10(DCNT);

      return 1;
}

int WriteData(unsigned char ucDATA)
{
      // This routine writes 1 data byte and checks for slave acknoledgement
      // Assumes Start already issued and SDA and SCL are both low

      unsigned char shiftbyte, statusbyte;
      int i;
      unsigned char ucTempData;

      shiftbyte = ucDATA;
      ucTempData = _inp(LPTData);

      // loop for 8 bits
      for (i=0; i<8; i++)
      {
            if (shiftbyte & 0x80)
            {
                  // Set SDA high (D1=0)
                  ucTempData = ucTempData & 0xFD;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);

                  // Clock SCL (D0=0, D0=1)
                  ucTempData = ucTempData & 0xFE;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);
                  ucTempData = ucTempData | 0x01;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);

                  // Bring SDA low (D1=1)
                  ucTempData = ucTempData | 0x02;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);

            }
            else
            {
                  // Bring SDA low (D1=1)
                  ucTempData = ucTempData | 0x02;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);

                  // Clock SCL (D0=0, D0=1)
                  ucTempData = ucTempData & 0xFE;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);
                  ucTempData = ucTempData | 0x01;
                  _outp(LPTData, ucTempData);
                  DelayASMx10(DCNT/2);
            }
            shiftbyte = shiftbyte << 1;
      }

      // Release SDA (D1=0)
      ucTempData = ucTempData & 0xFD;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Check if slave ACKs

      // Bring SCL high (D0=0)
      ucTempData = ucTempData & 0xFE;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Read SDA
      statusbyte = _inp(LPTStatus);

      // Bring SCL low (D0=1)
      ucTempData = ucTempData | 0x01;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Display ACK
      if (statusbyte & 0x20)
      {  // Slave ACK'd
            return 1;
      }
      else
      {  // Slave did not ACK
            return 0;
      }
}

int ReadDataNack(unsigned char *ucDATA)
{     // This routine reads one byte and NACKs. It assumes SDA and SCL
      // are low becuase a start condition/communication has already occured
      unsigned char shiftbyte;
      int i;
      int ucTempData, Status;

      shiftbyte = 0x00;

      // Ensure SDA is released (D1=0) and SCL is low (D0=1)
      ucTempData = _inp(LPTData);
      ucTempData = ((ucTempData & 0xFD) | 0x01);
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // loop for 8 bits
      for (i=0; i<8; i++)
      {     // Clock in one bit
            // Bring SCL high (D0=0)
            ucTempData = ucTempData & 0xFE;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);

            // Read in SDA
            Status = _inp(LPTStatus);

            // Bring SCL low (D0=1)
            ucTempData = ucTempData | 0x01;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);

            if (Status & 0x20)
            {     // Bit is high (although inverted through the 7405)
                  shiftbyte = shiftbyte << 1;
                  shiftbyte = shiftbyte & 0xFE; // Just in case compiler does not shift in 0
            }
            else
            {  // Bit is low (although inverted through the 7405)
                  shiftbyte = shiftbyte << 1;
                  shiftbyte = shiftbyte | 0x01;
            }
      }

      // NACK - Release SDA and clock SCL
      // Release SDA high (D1=0)
      ucTempData = ucTempData & 0xFD;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Bring SCL high (D0=0)
      ucTempData = ucTempData & 0xFE;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Bring SCL low (D0=1)
      ucTempData = ucTempData | 0x01;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      *ucDATA = shiftbyte;

      return 1;
}

int ReadDataAck(unsigned char *ucDATA)
{
      // Read 8 bits of data and ACK
      unsigned char shiftbyte;
      int i;
      int ucTempData, Status;

      shiftbyte = 0x00;

      // Ensure SDA is released (D1=0) and SCL is low (D0=1)
      ucTempData = _inp(LPTData);
      ucTempData = ((ucTempData & 0xFD) | 0x01);
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // loop for 8 bits
      for (i=0; i<8; i++)
      {     // Clock in one bit
            // Bring SCL high (D0=0)
            ucTempData = ucTempData & 0xFE;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);

            // Read in SDA
            Status = _inp(LPTStatus);

            // Bring SCL low (D0=1)
            ucTempData = ucTempData | 0x01;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);

            if (Status & 0x20)
            {     // Bit is high (although inverted through the 7405)
                  shiftbyte = shiftbyte << 1;
                  shiftbyte = shiftbyte & 0xFE;   // Just in case compiler does not shift in 0
            }

            else
            {     // Bit is low (although inverted through the 7405)
                  shiftbyte = shiftbyte << 1;
                  shiftbyte = shiftbyte | 0x01;
            }
      }

      // ACK - Pull SDA low and clock SCL, then release SDA
      // Pull SDA low (D1=1)
      ucTempData = ucTempData | 0x02;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Bring SCL high (D0=0)
      ucTempData = ucTempData & 0xFE;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Bring SCL low (D0=1)
      ucTempData = ucTempData | 0x01;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      // Release SDA (D1=0)
      ucTempData = ucTempData & 0xFD;
      _outp(LPTData, ucTempData);
      DelayASMx10(DCNT/2);

      *ucDATA = shiftbyte;

      return 1;
}

int SetSlaveAddress(unsigned char ucADDR)    // sets slave address for
{     // WriteBytes and ReadBytes Commands
      ucDeviceAddress = ucADDR & 0xFE;
      return 1;
}
int WriteBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256])
{     // Write upto 256 bytes to device address set by SetSlaveAddress.
      int fail = 0;
      int i;

      fail |= !Start();
      fail |= !WriteData((unsigned char)((long)ucDeviceAddress & 0xFE));
      fail |= !WriteData(ucMemAddr);
      for(i = 0; i <iCount; i++)
      {
            fail |= !WriteData(ucData[i]);
      }
      fail |= !Stop();

      if(fail)
            return 0;
      else
            return 1;
}

int ReadBytes(int iCount, unsigned char ucMemAddr, unsigned char ucData[256])
{     // Reads upto 256 bytes from device address set by SetSlaveAddress.
      int fail = 0;
      int i;

      fail |= !Start();
      fail |= !WriteData((unsigned char)((long)ucDeviceAddress & 0xFE));
      fail |= !WriteData(ucMemAddr);
      fail |= !Start();
      fail |= !WriteData((unsigned char)((long)ucDeviceAddress | 0x01));
      for(i = 0; i <(iCount-1); i++)
      {
            fail |= !ReadDataAck(&ucData[i]);
      }
      fail |= !ReadDataNack(&ucData[i]);
      fail |= !Stop();

      if(fail)
            return 0;
      else
            return 1;
}

int ResetBus()
{
      unsigned char ucTempData;
      int i;

      ucTempData = _inp(LPTData);

      for (i = 0; i < 9; i++)
      {
            // Bring SCL low (D0=1)
            ucTempData = ucTempData | 0x01;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);

            // Bring SCL high (D0=0)
            ucTempData = ucTempData & 0xFE;
            _outp(LPTData, ucTempData);
            DelayASMx10(DCNT/2);
      }
      Stop();
      return 1;
}

int SetStrobe()            // sets strobe pin (LED) for debuging (AN3230)
{
      EnableLED();
      //DisableLED();
      return 1;
}

int ClearStrobe()          // clears strobe pin (LED) for debugging (AN3230)
{
      DisableLED();
      //EnableLED();
      return 1;
}