National CP3BT26 User Manual

Page of 278
www.national.com
190
CP3BT26
24.4.1
Avoiding Bus Error During Write Transaction
A Bus Error (BER) may occur during a write transaction if
the data register is written at a very specific time. The mod-
ule generates one system-clock cycle setup time of SDA to
SCL vs. the minimum time of the clock divider ratio.
The problem can be masked within the driver by dynamical-
ly dividing-by-half the SCL width immediately after the slave
address is successfully sent and before writing to the ACB-
SDA register. This has the effect of forcing SCL into the
stretch state.
The following code example is the relevant segment of the
ACCESS.bus driver addressing this issue.
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; NAME: ACBRead     Reads "Count" byte(s) from selected I2C Slave.  If read address differs from previous
;                   Read or Write operation (as recorded in NextAddress), a "dummy" write transaction is
;                   initiated to reset the address to the desired location.  This is followed by a repeated
;                   Start sequence and the Read transaction.  All transactions begin with a call to ACBStartX
;                   which sends the Start condition and Slave address.  Checks for errors throughout process.
;
; PARAMETERS:   UBYTE   Slave   -   Slave Device Address. Must be of format 0xXXXX0000
;               UWORD   Addrs   -   Byte/Array address (extended addressing mode uses two byte address)
;               UWORD   Count   -   Number of bytes to read
;               UBYTE   *buf    -   Pointer to receive buffer
;
; CALLS:    ACBStartX
;
; RETURNED: error status
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
UWORD   ACBRead (UBYTE Slave, UWORD Addrs, UWORD Count, UBYTE *buf)
{
    ACB_T   *acb;
    UBYTE   err, *rcv;
    UWORD   Timeout;
    acb =   (ACB_T*)ACB_ADDRESS;                    /* Set pointer to ACB module                            */
                                                    /* If the indicated address differs from the last       */
    if (Addrs != NextAddress) {
                                                    /* recorded access (i.e. Random Read), we must first    */
                                                    /* send a "dummy" write to the desired new address..    */
        NextAddress =   Addrs;                      /* Update last address placeholder                      */
    KeyInit();
KBD_OUT &= ~BIT0;
                                                    /* Send start bit and Slave address...                  */
        if ((err = ACBStartX (Slave | (Addrs >> 7 & 0x0E), ACB_WRITE, 0)))
            return (err);                           /* If unsuccessful, return error code                   */
 //   KBD_OUT &= ~BIT0;
        acb->ACBsda =   (UBYTE)Addrs;               /* Send new address byte                                */
KBD_OUT &= ~BIT0;
        Timeout =   1000;                           /* Set timeout                                          */
                                                    /* Wait for xmitter to be ready...zzzzzzzzz             */
        while (!(acb->ACBst & ACBSDAST) && !(acb->ACBst & ACBBER) && Timeout--);
        if (acb->ACBst & ACBBER) {                 
                                                    /* If a bus error occurs while sending address, clear   */
            acb->ACBst  |=  ACBBER;                 /* the error flag and return error status               */
            return (ACBERR_COLLISION);
        }
KBD_OUT &= ~BIT0;        
        if (!Timeout)                               /* If we timeout, return error                          */
            return (ACBERR_TIMEOUT);
    }
                                                    /* (Re)Send start bit and Slave address...              */
    if ((err = ACBStartX (Slave | (Addrs >> 7 & 0x0E), ACB_READ, Count)))
                                                    /* If error, return                                     */
        return (err);
    rcv =   buf;                                    /* Get address of read buffer                           */
                                                    /* Read Count bytes into user’s buffer                  */
    while (Count) {
        if (Count-- == 1)                           /* If this the final byte, or only one requested, send  */
            acb->ACBctl1    |=  ACBACK;             /* the NACK bit after reception                         */
        Timeout =   1000;                           /* Set timeout                                          */
        while (!(acb->ACBst & ACBSDAST) && Timeout--);
        if (!Timeout)                               /* Timed out??                                          */
                                                    /* YES - return error                                   */
            return (ACBERR_TIMEOUT);
 
        *rcv++  =   acb->ACBsda;                    /* NO - Read byte from Recv register                    */
                                                    /* Adjust current address placeholder                   */
        NextAddress++;
    }