/* ********************************************************************** * File K51PPOE.C - Rel. 1.1 with uC/51 V. 1.20.04 * * Boards: GMB HR 84 + GMM 932 + K51-AVR * * 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 01.06.05 * ********************************************************************** 05.06.2000 by Adriano Pedrielli (for K51 alone) (Original version in BASCOM 8051) 01/06/05: K51PPOE.C - Rel. 1.1 - By Graziano Gaiba This demo allows to drive an I2C BUS peripheral on board of K51-AVR, PCF8574, through mini-BLOCK module GMB HR84 and grifo(r) mini module. PCF 8574 is an I/O expander that allows to drive 8 TTL digital lines, both as input and as output, all the operations of read and write are performed through synchronous serial interface I2C BUS. This demo activates in sequence one line at a time, creating the classic bit shift to the right and to the left, and writes 'out' on the 7 segments display of K51-AVR, near the characters 'out'. */ #include "lpc932.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 #define PINSCL P1_2 // Pins used as I2C BUS lines #define PINSDA P1_3 #define NACK 0 // Indicates to generate I2C BUS Acknowlegde bit #define ACK 1 // Indicates to generate I2C BUS Not Acknowlegde bit /***************** Addresses list for Saa1064 ***********************/ #define Saa1064 0x38 // Slave address SAA1064 #define Wsaa1064 0x70 // Slave address SAA1064 in Write #define Rsaa1064 0x71 // Slave address SAA1064 in Read #define Ctb 0x00 // Ind. Control byte #define Dig1 0x01 // Ind. Digit 1 #define Dig2 0x02 // Ind. Digit 2 #define Dig3 0x03 // Ind. Digit 3 #define Dig4 0x04 // Ind. Digit 4 /************** Addresses list for PCF8574A or P *********************/ /* There are two versions with incompatible slave address of PCF8574, A and P. Select the one desired with the following code. */ // Addresses list for PCF8574A /* #define Pcf8574 0x3C // Slave address PCF8574A #define Wpcf8574 0x78 // Slave address PCF8574A in Write #define Rpcf8574 0x79 // Slave address PCF8574A in Read */ // Indirizzi per PCF8574P #define Pcf8574 0x24 // Slave address PCF8574P #define Wpcf8574 0x48 // Slave address PCF8574P in Write #define Rpcf8574 0x49 // Slave address PCF8574P in Read // Inizialization od SAA1064 #define saa1064_init_length 0x06 // Inizialization sequence length const unsigned char saa1064_init[]={ Ctb, // Points control register 0x27, // = 0b00100111 // bit0 =1 dynamic mode // bit1 =1 digit 1+3 not blanked // bit2 =1 digit 2+4 not blanked // bit3 =0 no test segment // bit4 =0 no 3mA segment current // bit5 =1 6mA segment current // bit6 =0 no 12mA segment current // bit7 =0 indifferent 92, // writes 'o' 28, // writes 'u' 120, // writes 't' 0 // writes DY4 off }; // Codes to convert a figure from 0 to F in 7 segments coding const unsigned char cifra_7seg[]={0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F, 0x77,0x7C,0x39,0x5E,0x79,0x71}; /**************************************************************************** General purpose global variables, hw sections management ****************************************************************************/ near unsigned char i,dr; near unsigned long clk; // Clock della CPU near unsigned char i2c_err = 0; // Error on I2C BUS hardware peripheral /**************************************************************************** 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 $0FF00 ; 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 the hw lines for I2C BUS communication plus the internal I2C BUS controller, and sets a 12 KHz communication bit rate. For the I2C BUS bit rate it is selected the clock source, taking care of the frequency saved in global variable clk. Resets global variables err_i2chw. */ void i2c_init(void) { P1M1=P1M1 | 0x0C; // Configures P1.2 (SCL) and P1.3 (SDA) P1M2=P1M2 | 0x0C; // in mode 3=Open Drain PINSCL=1; // Allows management of I2C BUS line from PINSDA=1; // proper internal controller if (clk==RCINT) { I2SCLL=153; // Sets bit rate= 2*(153+153) clock cycles I2SCLH=153; // equals to about 12 KHz at 7.3728 MHz } else { I2SCLL=230; // Sets bit rate= 2*(230+230) clock cycles I2SCLH=230; // equals to about 12 KHz at 11.0592 MHz } //Endif i2c_err=0x00; // Resets variable for I2C error } /* 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 w; I2CON=i2cc; // Writes passed parameter in I2C control register w=0; // Resets timeout counter while (((I2CON & 0x08)==0) && (w<60000)) // Waits I2C interrupt flag or timeout w++; // Increments timeout counter //endwhile w=I2STAT; // Reads current I2C status register w=w & 0xF8; // Mantains only significants bits return (unsigned char) w; // Returns masked status register } /* Generates start sequence for I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. This procedure selects the Master xxxxxxx mode of controller. */ void i2c_start(void) { I2CON=0x40; // Enables I2C hw controller if (i2c_cmd(0x60)!=0x08) // Set start bit and checks status i2c_err=i2c_err | 0x01; // Sets error on start //endif } /* Generates a repeated start sequence for I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. This procedure selects the Master xxxxxxx mode of controller.*/ void i2c_repstart(void) { I2CON=0x40; // Enables I2C hw controller if (i2c_cmd(0x60)!=0x10) // Set start bit and checks status i2c_err=i2c_err | 0x02; // Sets error on repeated start //endif } /* Generates stop sequence for I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. */ void i2c_stop(void) { unsigned int w; I2CON=0x50; // Set stop bit in I2C control register w=0; // Resets timeout counter do { w++; // Increments timeout counter delay(1); // Generates 1 msec pause } while ((I2CON & 0x10) && (w<200)); // Waits end of stop sequence or timeout if (I2CON & 0x10) // Checks final status i2c_err=i2c_err | 0x04; // Sets error on stop //endif I2CON=0x00; // Disables I2C hw controller } /* Sends the slave address passed in sla parameter on the I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. The sla.0 bit defines the data direction and so selects if the controller works in Master transmit mode or Master receive mode. */ void i2c_sla(unsigned char sla) { I2DAT=sla; // Writes slave address+R/W bit on controller if (sla & 0x01) // Checks directions for different results verifications { // Data direction=R -> Master receive mode if (i2c_cmd(0x40)!=0x40) // Send slave add. and checks status for R i2c_err=i2c_err | 0x08; // Sets error on slave address // endif } else { // Data direction=W -> Master transmit mode if (i2c_cmd(0x40)!=0x18) // Send slave add. and checks status for W i2c_err=i2c_err | 0x08; // Sets error on slave address // endif } //endif } /* Sends the data passed in dat parameter on the I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. The controller must be set in Master transmit mode. */ void i2c_wr(unsigned char dat) { I2DAT=dat; // Write data on controller if (i2c_cmd(0x40)!=0x28) // Sends data and checks status i2c_err=i2c_err | 0x10; // Sets error on transmit data //endif } /* Receive a data from the I2C BUS line through hw controller and checks for any possible errors, that are saved in global variables err_i2chw. The ack parameter defines if an Acknowledge bit must follow the data reception and the received data is returned in the function name. The controller must be set in Master receive mode. */ unsigned char i2c_rd(unsigned char ack) { if (ack) // Checks Acknowledge bit { // Receives data and sends ACK bit if (i2c_cmd(0x44)!=0x50) // Receives data and checks status i2c_err=i2c_err | 0x20; // Sets error on receive data // endif } else { // Receives data without ACK bit if (i2c_cmd(0x40)!=0x58) // Receives data and checks status i2c_err=i2c_err | 0x20; // Sets error on receive data // endif } //endif return I2DAT; // Returns received data on controller } /* Shows current status of I2C BUS errors, saved in global variables err_i2chw, and resets it. */ void i2c_showres(void) { printf("-Current I2C BUS errors:"); if (i2c_err) { // Shows current errors if (i2c_err & 0x01) // Checks and shows error on start printf(" Start"); // endif if (i2c_err & 0x02) // Checks and shows error on repeated start printf(" Repeated-Start"); // endif if (i2c_err & 0x04) // Checks and shows error on stop printf(" Stop"); // endif if (i2c_err & 0x08) // Checks and shows error on slave address printf(" Slave-address"); // endif if (i2c_err & 0x10) // Checks and shows error on transmit data printf(" Transmit-data"); // endif if (i2c_err & 0x20) // Checks and shows error on receive data printf(" Receive-data"); // endif puts(""); // Completes shown error message } else puts(" none"); // Shows no errors //endif i2c_err=0x00; // Resets variable for I2C error } /* Send n_out bytes then read n_in bytes from slave address I2C BUS indicated in parameter using hardware peripheral. Data read are stored in arra getData, its type is unsigned char *. */ void i2c_n_receive(unsigned char i2c_slave, unsigned char *getData, unsigned char n_out, unsigned char n_in) { unsigned char dummy; i2c_start(); // Generate start i2c_sla(i2c_slave); // Scrive slave address for(dummy = 0; dummy < n_out; dummy++) { i2c_wr(getData[dummy]); // Send data } i2c_repstart(); // Generate repeated start i2c_sla(i2c_slave | 0x01); // Send slave address + R for(dummy = 0; dummy < n_in; dummy++) { if(dummy == n_in - 1) // If last byte to read... { getData[dummy] = i2c_rd(NACK); // ...read byte and generate NACK } else // Otherwise... { getData[dummy] = i2c_rd(ACK); // ...read byte and generate ACK } } i2c_stop(); // Generate stop } /* Send n_out bytes to slave address I2C BUS indicated in parameter using hardware peripheral. Data to send must be stored in the array passed as sendData, type unsigned char *. */ void i2c_n_send(unsigned char i2c_slave, unsigned char *sendData, unsigned char n_out) { unsigned char dummy; i2c_start(); // Generate start i2c_sla(i2c_slave); // Send slave address for(dummy = 0; dummy < n_out; dummy++) { i2c_wr(sendData[dummy]); // Send data } i2c_stop(); // Generate stop } /**************************************************************************** Specific procedures for K51-AVR on board peripherals management ****************************************************************************/ /* Initialization of K51-AVR on board peripherals */ void demo_init(void) { unsigned char i; // Waits for SAA1064 to turn ON do { i2c_start(); i2c_sla(Rsaa1064); i = i2c_rd(NACK); i2c_stop(); // If there are errors, they are printed to a console if(i2c_err) { i2c_showres(); } } while(i); // Send SAA1064 initialization sequence i2c_n_send(Wsaa1064, saa1064_init, saa1064_init_length); // If there are errors, they are printed to a console if(i2c_err) { i2c_showres(); } } // Converts a value from 0 to 255 in two 4 bit hexadecimal figures void cifre(unsigned char v, unsigned char *c1, unsigned char *c2) { *c1=(v & 0xf0) >> 4; *c2=v & 0x0f; } /******** Converts a figure in range from 0 to F in 7 segments ******** This procedure converts a figure in range from 0 to F in 7 segments format, if value is greater than F the display is off. Parameters: Input : value in the range from 0 to F Outout : value in 7 segments format. ************************************************************************/ unsigned char digit(unsigned char n) { if(n < 16) { return cifra_7seg[n]; // reads value from table } else { return 0; // If value greater than 15 turn off display } } /*********************** Visualizes a byte in HEX ********************* The first display visualizes the number of the channel being converted, the second display is off. This procedure allows to show a byte in hexadecimal format in the two displays on the right. E.g.: 255= FFH, 32= 20H etc. Parameters: Input : number to visualize in HEX Output : nothing **************************************************************************/ void vis_num(unsigned char t) { unsigned char h, l; cifre(t, &h, &l); // Splits in 2 nibble of t // high nibble and low nibble i2c_start(); // Start sequence i2c_sla(Wsaa1064); // Slave address i2c_wr(Dig3); // First digit i2c_wr(digit(h)); // Transforms into "7 segments" and send i2c_wr(digit(l)); // Transforms into "7 segments" and send i2c_stop(); // Stop sequence } /**************************************************************************** Main program ****************************************************************************/ void main(void) { unsigned char i, d; 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; } for(;;) { iniser(19200, 1); // Harware serial port as console initialization i2c_init(); // Hardware I2C BUS peripheral initialization demo_init(); // Peripherals initialization clrscr(); // Clears the screen puts("Demo 1.1 for GMM932 ds300803 + GMBHR84 ds220503 + K51-AVR ds200500"); puts(""); // Internal control check(); puts("Makes a shift on the TTL output signals on CN1."); while(TRUE) // Infinite Loop { i=0x01; d=~i; // To drive LEDs in complemented logic // For example with DEB 01 or TIO 16 while(i) { i2c_n_send(Wpcf8574, &d, 1); // Send data to PCF8574 i<<=1; // Left shift d=~i; delay(500); // To make the shift visible } i=0x80; d=~i; // To drive LEDs in complemented logic // For example with DEB 01 or TIO 16 while(i) { i2c_n_send(Wpcf8574, &d, 1); // Send data to PCF8574 i>>=1; // Rifht shift d=~i; delay(500); // To make the shift visible } } // end while(TRUE) } // end for(;;) }