/*  Filename     ZMCP23016.C
    Description  MCP23016 Example Program 
    Hardware     SLAB-51 + MCP23016
    Clock        11.0592 Mhz
    Compiler     Keil PK51 v7.10
    Engineer     Kriangsak B.
    Company      Sila Research Co.,Ltd. 
*/

#include <reg52.h>
#include <absacc.h>
#include <assert.h>
#include <ctype.h>
#include <intrins.h>
#include <math.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/********** I/O PORT **********/

sbit     IPSCL          = P1^5;             // mcp23008
sbit     IPSDA          = P1^6;

/********** SUB FUNCTION **********/

void dmsec (unsigned int count) {           // mSec Delay
    unsigned int i;                         // for Keil CA51 (Speed x 1)
    while (count) {
        i = 115; while (i>0) i--;
        count--;
    }
}

void ipdel (void) {                    // I2C delay 
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
    _nop_ ();
}

void ipchigh (void) {                  // I2C clock high 
    IPSCL = 1;
    ipdel ();
}

void ipclow (void) {                   // I2C clock low 
    IPSCL = 0;
    ipdel ();
}

void ipstart (void) {                  // start condition 
    IPSDA = 1;
    IPSCL = 1;
    IPSDA = 0;
    ipdel ();
    IPSCL = 0;
    IPSDA = 1;
}
                                        
void ipstop (void) {                   // stop condition 
    IPSDA = 0;
    IPSCL = 1;
    ipdel ();
    IPSDA = 1;
}

bit ipwrbyte (unsigned dat) {          // write one byte 
    unsigned char i;                   // return 0 = ok 
    bit outbit;                        // return 1 = error 
    for (i=1;i<=8;i++) {
        outbit = dat & 0x80;
        IPSDA = outbit;
        dat = dat << 1;
        ipchigh ();
        ipclow ();
    }
    IPSDA = 1;
    ipchigh ();
    outbit = IPSDA;
    ipclow ();
    return (outbit);
}

unsigned char iprdbyte () {            // read one byte 
    unsigned char i,dat;               // return 0xff = error 
    bit inbit;
    dat = 0;
    for (i=1;i<=8;i++) {
        ipchigh ();
        inbit = IPSDA;
        dat = dat << 1;
        dat = dat | inbit;
        ipclow ();
    }
    IPSDA = 1;
    ipchigh ();
    inbit = IPSDA;
    ipclow ();
    if (~inbit) dat = 0xff;
    return (dat);
}

unsigned char iprdbytez () {           // read one byte (ack=0)
    unsigned char i,dat;               
    bit inbit;
    dat = 0;
    for (i=1;i<=8;i++) {
        ipchigh ();
        inbit = IPSDA;
        dat = dat << 1;
        dat = dat | inbit;
        ipclow ();
    }
    IPSDA = 0;
    ipchigh ();
    ipclow ();
    IPSDA = 1;
    return (dat);
}

void ip23016wrw (unsigned char addr,com,unsigned int dat) {     // mcp23016 write (word)
    addr = ((addr << 1) & 0x0e) | 0x40;             
    ipstart ();                                  
    ipwrbyte (addr);
    ipwrbyte (com);
    ipwrbyte (dat);
    ipwrbyte (dat >> 8);
    ipstop ();
}

unsigned int ip23016rdw (unsigned char addr,com) {    // mcp23016 read (word)
    unsigned char g0,g1;
    unsigned int dat;                               
    ipstart ();
    addr = ((addr << 1) & 0x0e) | 0x40;
    ipwrbyte (addr);
	ipwrbyte (com);
	ipstop ();

    ipstart ();
    ipwrbyte (addr | 0x01);
    g0 = iprdbytez (); 
    g1 = iprdbyte ();
    ipstop ();
    dat = g1;
    dat = (dat << 8) | g0;
    return (dat);
}

/********** MAIN FUNCTION **********/

void main (void) {
    unsigned char i;
	unsigned int x;
    SCON = 0x52;             	  // set RS232 parameter
    TMOD = 0x20;
    TH1 = 0xfd; PCON |= 0x80;     // 19200
    TR1  = 1;

	dmsec (500);
    ip23016wrw (0,0x02,0xffff);   // set all out = 1
	ip23016wrw (0,0x06,0x0000);   // set all port = out
	dmsec (500);
    for (i=0;i<=9;i++) {               // test port = out (10 times)
        ip23016wrw (0,0x02,0x5555);
        dmsec (250);     
        ip23016wrw (0,0x02,0xaaaa); 
        dmsec (250);     
    }

	ip23016wrw (0,0x06,0xffff);   // set all port = in
	while (1) {
        x = ip23016rdw (0,0x0);  
		printf ("In-Port=%04X\r",x);
		dmsec (250);
	}
}


