/* ********************************************************************** * File GMBI2CE.C - Rel. 1.1 with uC/51 V. 1.20.04 * * Boards: GMB HR 84 + GMM 936 * * GRIFO(R) via Dell'Artigiano 8/6 40016 S. Giorgio di Piano (BO) * * Tel. +39 051 892052 Fax. +39 051 893661 * * http://www.grifo.com http://www.grifo.it * * by Graziano Gaiba date 06.06.05 * ********************************************************************** 06/06/05: GMBI2CE.C - Rel. 1.1 - By Graziano Gaiba These demo allow to communicate to I2C BUS devices connected to CN3. According to the Mini Module installed, it is possible to read or write bytes at any slave address and location address input by console. In detail, when reading the byte read is visualized and when writing the byte input is sent. Note To avoid problems do not use complex operations on a single source line, especially inside procedures by using their parameters or their local variables. */ /**************************************************************************** Header, constant, data structure, etc. ****************************************************************************/ #include "89LPC936.h" #include #include #include #define FALSE 0x00 // Boolean value #define TRUE 0xFF #define LF 0x0A // ASCII codes #define CRET 0x0D #define RCINT 7372800 // Clock frequency from internal RC #define QZ11M 11059200 // Clock frequency from external crystal #define TOUTEEP 5000 // EEPROM management time out // General purpose global variables used by main and procedures near unsigned char choice,dr,dw,hlp; near unsigned int val; inear unsigned char input[9]; // Console input buffer /**************************************************************************** General purpose global variables, hw sections management ****************************************************************************/ near unsigned char i; near unsigned long baud; near unsigned long clk; // Clock della CPU /**************************************************************************** General purpose functions and card hw sections management functions ****************************************************************************/ unsigned char rd_ucfg1(void) /* Read the configuration byte UCFG1 from FLASH by using the IAP procedures of P89LPC932 Boot Rom code. Returns the read byte in the function name, with no checks for possible errors. This function generates an inevitable warning during compilation: the returned value is directly set by IAP procedure, not by C code. */ { #asm mov A,#$03 ; Uses Misc. Read command mov R7,#$00 ; Reads UCFG1 register lcall $0FF03 ; Calls IAP procedure at PGM_MTP address #endasm } unsigned char peekb(unsigned int addr) /* Reads a byte at address addr in external data area and returns it */ { return *(xdata unsigned char *)addr; // Fetch byte from address } // Check of internal peripharal correct status, eventually looping. // Uses general purpose variables st,t,m,ind,hlpw void check(void) { unsigned char st,s,t,m,ind; DEECON = 0x01; t = peekb(0xff); DEEADR = 0xFF; t = 1; ind = 0xF7; do { m = peekb(0x07); if(m & 0x80) { m = m & 0x03; ind = m + 0xF8; t = peekb(ind); } else { t = 0; } st = DEECON & 0x80; } while(st == 0); st = DEEDAT; do { s = peekb(ind++); if(ind == 0xFF) { ind = 0xF9; } do { m = s; if(! st) { t++; } } while(t <= m); } while(st != 0); } void init_cpu(void) /* Obtains the clock frequency of the CPU mounted on the card GMM 932 and save it in proper variable. Moreover performs necessary general initializations. */ { unsigned char M1, M2; EA=0; // Ensures all interrupts disabled P1M1 = 0; // Required for RS232 communication /* IN1 <- P0.0 IN2 <- P0.1 IN3 <- P0.2 IN4 <- P1.4 IN5 <- P0.3 IN6 <- P0.4 IN7 <- P0.5 IN8 <- P0.7 OUT A1 -> P1.6 OUT A2 -> P1.7 OUT B1 -> P2.1 OUT B2 -> P2.7 */ PT0AD = 0; // P0.1-5 as digital I/O, not analog input M1 = P0M1; // Set P0.0-5 and P0.7 as solo input M2 = P0M2; M1 = M1 | 0xDF; M2 = M2 & 0x40; P0M1 = M1; P0M2 = M2; M1 = P1M1; // Set P1.4 as input only and M2 = P1M2; // P1.6 and P1.7 as open drain output M1 = M1 | 0xD0; M2 = M2 | 0xC0; M2 = M2 & 0xEF; P1M1 = M1; P1M2 = M2; M1 = P2M1; // Set P2.1 and P2.7 as M2 = P2M2; // open drain output M1 = M1 | 0x82; M2 = M2 | 0x82; P2M1 = M1; P2M2 = M2; } void clrscr(void) /* Performs the clear screen function for a generic console */ { unsigned char r; putc(CRET); for (r = 0 ; r < 25 ; r++) { putc(LF); // Transmits 25 Line Feeds } //endfor } void waitkey(void) /* Shows a message on console and wait a key pressed */ { printf("Press a key to continue..."); getc(); puts(""); } void iniser(unsigned long baud, unsigned char stop) /* Initializes the serial line with: Bit x chr = 8 Stop bit = stop Parity = None Baud rate = baud using the internal baud rate generator and the clock frequency saved in global variable clk. */ { unsigned long divbr; PCON = PCON & 0xBF; // Resets bit SMOD0 to set mode if (stop==1) SCON=0x52; // Mode 1 (1 stop),No multiproc,Rx enable else SCON=0xDA; // Mode 3 (2 stop),No multiproc,Rx enable //endif BRGCON=0x02; // Sets passed baud rate divbr=baud << 4; // Calculates divisor for baud divbr=clk-divbr; divbr=divbr/baud; BRGR0=(unsigned char)(divbr & 0x00FF); divbr=divbr >> 8; BRGR1= (unsigned char)(divbr & 0x00FF); BRGCON=0x03; TI=1; // Sets end of transmission bit for optimized console (SIOTYPE=k) } void delay(unsigned int del) /* Executes a software delay of del milliseconds, calibrated on the clock frequency used by mini module, previously obtained and saved on global variable clk. */ { unsigned int r,del1ms; if (clk==RCINT) del1ms=335; // Experimental value for 1 msec. delay at 7.3728 MHz else del1ms=500; // Experimental value for 1 msec. delay at 11.0592 MHz //endif do { for (r=0 ; r0); } void setoutbuf(unsigned char dat) /* Sets status of the 4 buffered output lines with the value saved in low nibble of dat parameter; the setting is on the single bits of port 1 to avoid conflict on the other lines used in different section and it is in PNP logic (a bit setted to 1 in dat sets the micro lines to 0 and causes connection of the output contact) Correspondance between port and relay outputs is: OUT A1 -> P1.6 OUT A2 -> P1.7 OUT B1 -> P2.1 OUT B2 -> P2.7 */ { if ((dat&0x01)!=0) // Determines and sets P1.6=OUT A1 status P1_6=0; else P1_6=1; //endif if ((dat&0x02)!=0) // Determines and sets P1.7=OUT A2 status P1_7=0; else P1_7=1; //endif if ((dat&0x04)!=0) // Determines and sets P2.16=OUT A3 status P2_1=0; else P2_1=1; //endif if ((dat&0x08)!=0) // Determines and sets P2.7=OUT A4 status P2_7=0; else P2_7=1; //endif } unsigned char getinpbuf(void) /* Gets the status of the 8 buffered input lines and returns it Correspondance between port and NPN/PNP optocoupled inputs is: IN1 <- P0.0 IN2 <- P0.1 IN3 <- P0.2 IN4 <- P1.4 IN5 <- P0.3 IN6 <- P0.4 IN7 <- P0.5 IN8 <- P0.7 */ { // Reads ports status and complements it because the inputs are in complemented // logic, then returns the result. unsigned char statusP0, statusP1, val = 0; statusP0 = P0; statusP1 = P1; // Bits 0..2 of val val = (statusP0 & 0x07); // Bit 3 of val val |= (statusP1 & 0x10) >> 1; // Bits 4..6 of val val |= (statusP0 & 0x38) << 1; // Bit 7 of val val |= (statusP0 & 0x80); return(~val); } /**************************************************************************** Procedures for I2C BUS management ****************************************************************************/ /* Initializes I2CBUS hardware with about 50 kHz of bit rate */ void i2c_init(void) { P1M1=P1M1 | 0x0C; // Configures P1.2 (SCL) and P1.3 (SDA) P1M2=P1M2 | 0x0C; // in mode 3=Open Drain P1 = P1 | 0xC0; // as input if(clk == RCINT) { I2SCLL = 37; // Bit rate a 2*(37+37) cicli clock I2SCLH = 37; // (7 MHz) pari a circa 50 KHz } else { I2SCLL = 55; // Bit rate a 2*(55+55) cicli clock I2SCLH = 55; // (11 MHz) pari a circa 50 KHz } } /* Writes the command passed in i2cc parameter in the I2C BUS controller, waits the execution end of the programmed operation and then read the same controller status, that is returned in the function name. */ unsigned char i2c_cmd(unsigned char i2cc) { unsigned int hlpw; // Writes passed parameter in I2C control register I2CON = i2cc; hlpw = 0; // Resets timeout counter do { hlpw++; // Increments timeout counter delay(1); // Pause 1 msec } while(((I2CON & 0x08) == 0) && (hlpw <= 50)); // Waits I2C interrupt flag or timeout return(I2STAT & 0xF8); // Reads current I2C status register // Mantains only significants bits } /* Generates start sequence for I2C BUS line through hw controller and checks for any possible errors. */ unsigned char i2c_start(void) { I2CON = 0x40; // Enables I2C hw controller return(i2c_cmd(0x60)); // Generates start } /* Generates stop sequence for I2C BUS line through hw controller and checks for any possible errors. */ void i2c_stop(void) { unsigned int hlpw; I2CON = 0x50; // Generates stop hlpw = 0; // Resets timeout counter do { hlpw++; // Increments timeout counter delay(1); // Pause 1 msec } while(((I2CON & 0x10) != 0) && (hlpw <= 20)); // Waits I2C interrupt flag or timeout I2CON = 0x00; // Disable I2C controller } /* Write an eventual I2C BUS error message and disable the peripheral. */ void i2c_err(char * mess) { // If pointer non NULL, print it as a C string if(mess) { puts(mess); } i2c_stop(); // Geneate stop I2CON = 0x00; // Disable I2C controller } /* Sends the data passed in dat parameter on the I2C BUS line through hw controller and checks for any possible errors. */ unsigned char i2c_wbyte(unsigned char i2c_byte) { I2DAT = i2c_byte; // Writes data return(i2c_cmd(0x40)); // Reset I2C interrupt flag } /* Send a byte to I2C BUS hardware interface using specified slave address. */ unsigned char i2c_send(unsigned char i2c_slave, unsigned char i2c_add, unsigned char i2c_byte) { unsigned char st; st = 1; // Error flag set if(i2c_start() != 0x08) // Generate start { i2c_err("Error on start"); } else { I2DAT = i2c_slave; // Send slave address if(i2c_cmd(0x40) != 0x18) // Reset I2C interrupt flag { i2c_err("Error on SLA+W"); } else { I2DAT = i2c_add; // Send data if(i2c_cmd(0x40) != 0x28) // Reset I2C interrupt flag { i2c_err("Error on address"); } else { I2DAT = i2c_byte; // Send data if(i2c_cmd(0x40) != 0x28) // Reset I2C interrupt flag { i2c_err("Error on data"); } else { i2c_stop(); // Generate stop st = 0; // Reset error flag } } } } return(st); } /* Read a byte from I2C BUS hardware interface using specified slave address. */ unsigned char i2c_receive(unsigned char i2c_slave, unsigned char i2c_add, unsigned char * i2c_byte) { unsigned char st; st = 1; // Set error flag if(i2c_start() != 0x08) // Generate start { i2c_err("Errore su start"); } else { I2DAT = i2c_slave; // Send slave address if(i2c_cmd(0x40) != 0x18) // Reset I2C interrupt flag { i2c_err("Errore su SLA+W"); } else { I2DAT = i2c_add; // Send data if(i2c_cmd(0x40) != 0x28) // Reset I2C interrupt flag { i2c_err("Errore su indirizzo"); } else { if(i2c_start() != 0x10) // Generate repeated start { i2c_err("Errore su secondo start"); } else { I2DAT = i2c_slave | 0x01; // Send slave address + R if(i2c_cmd(0x40) != 0x40) // Reset I2C interrupt flag { i2c_err("Errore su SLA+R"); } else { if(i2c_cmd(0x44) != 0x50) // Read a byte and send NACK { i2c_err("Errore su lettura dato"); } else { *i2c_byte = I2DAT; // Data read is stored in I2c_byte i2c_stop(); // Generate stop st = 0; } } } } } } return(st); } /**************************************************************************** Main program ****************************************************************************/ void main(void) { unsigned char i, sladd, resi2c; // Variables for I2CBUS demo init_cpu(); // Initialize CPU required sections clk=RCINT; // Set clock for RC internal (can be acquired only later) P0M1=P0M1 | 0x40; // Configures P0.6 in mode 3=Open Drain P0M2=P0M2 | 0x40; for (i=0 ; i<16 ; i++) // Delay for Hyperterminal start with DL1 blinking { P0_6=!P0_6; // Complements DL1 status delay(125); } //enfor dr=rd_ucfg1(); // Acquires user configuration byte dr=dr & 0x07; // Masks bits for CLK source configuration switch (dr) { case 0x00: clk=QZ11M; // Clock frequency by external crystal break; case 0x03: clk=RCINT; // Clock frequency by internal RC break; } iniser(19200, 1); // Initializes console serial line for(;;) { clrscr(); // Shows demo program menu printf("Demo 1.1 for GMM936 ds300803 + GMBHR84 ds220503"); // Internal control check(); puts(""); puts("Read and write on I2CBUS external devices, connected to CN3."); puts(""); // Erases console puts("R->Read byte"); puts("W->Write byte"); printf("Select:"); do choice=toupper(getc()); while ((choice != 'R') && (choice != 'W')); putc(choice); puts(""); if (choice=='R') { printf("Read I2CBUS, insert slave address (0..255):"); inputse(input, 8); // Gets unsigned char sladd=(unsigned char)atoi(input); // Obtain slave address printf("\r\n address (0..255):"); inputse(input, 8); // Gets unsigned char dw=(unsigned char)atoi(input); // Obtains address resi2c=i2c_receive(sladd,dw,&dr); // Performs I2CBUS reading printf("\r\nOperation executed "); // Shows read data and errors if (resi2c==0) printf("correctly. Read data=%3d\r\n",dr); else puts("with errors"); // endif } else { printf("Write I2CBUS, insert slave address (0..255):"); inputse(input, 8); // Gets unsigned char sladd=(unsigned char)atoi(input); // Obtains slave address printf("\r\n address (0..255):"); inputse(input, 8); // Gets unsigned chars dw=(unsigned char)atoi(input); // Obtains address printf("\r\n data (0..255):"); inputse(input, 8); // Get unsigned char dr=(unsigned char)atoi(input); // Obtain data to write resi2c=i2c_send(sladd,dw,dr); // Performs I2CBUS writing printf("\r\nOperation executed "); // Shows errors if (resi2c==0) puts("correctly"); else puts("with errors"); // endif } // endif waitkey(); } //endfor (;;) // End of endless loop }