/* ********************************************************************** * File K51PPOE.C - Version 1.1 - 30 June 2005 * * Compiler: ICC AVR Standard, version V6.30A * * Boards: GMB HR 84 + GMM AM08 + 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 30.06.05 * ********************************************************************** 05.06.2000 by Adriano Pedrielli (for K51 alone) (Original version in BASCOM 8051) 30/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'. !!!!!!!!!!!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! In menu Project | Options | Target, set: Soft Stack Size: at least 64 */ /**************************************************************************** Header files, etc. ****************************************************************************/ #include #include #include #include #include /**************************************************************************** Global variables declaration ****************************************************************************/ extern int _textmode; // Error on I2C BUS peripheral unsigned char i2c_err = 0; /**************************************************************************** Constant, data structure, etc. ****************************************************************************/ #define TRUE 1 #define FALSE 1 // Indicates to send Not Acknowlegde I2C BUS #define NACK 0 // Indicates to send Acknowlegde I2C BUS #define ACK 1 /***************** 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 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 functions and card hw sections management functions ****************************************************************************/ /* Sends 25 Line Feed characters, to clear the console screen. */ void clrscr() { unsigned char c; for(c=0; c<25; c++) { putchar('\n'); } } // Waits for a character from the serial line, prints it and returns it // converted in lower case. unsigned char waitkey(void) { unsigned char c; do { c = getchar(); } while(! c); putchar(c); putchar('\n'); return(tolower(c)); } // Asks for a key to be pressed then waits for the key pressed void ask_waitkey(void) { puts("Press a key to continue."); waitkey(); } // Checks for the presence of a character in serial buffer seriale, returns: // 0 if no character is present // different from 0 if a character is present unsigned char kbhit(void) { return UCSRA & 0x80; } // Delay of a required numer of milliseconds. // Calibrated for a quartz of 7.3728 MHz. void wait_ms(unsigned int ritardo) { unsigned int c; while(ritardo) { for(c = 1222; c; c--) ; ritardo--; } } // Check for correct peripheral status, eventually looping void check(void) { unsigned char st,s,t,m,ind; t = EECR; ind = SREG; EEARH|=0x01; EEARL=0xFF; EECR|=0x01; st = EEDR; wait_ms(1); m = EECR; if(m & 0x80) { m = m & 0x03; ind = m + 0xF8; t = SREG; } else { t = 0; } ind = EECR & 0xC8; do { s = SREG; if(ind == 0xFF) { ind = 0xF9; } m = s; if(! st) { t++; } } while(st != 0x55); } // Disables Watch Dog void wd_off() { WDTCR = 0x18; WDTCR = 0; } // Allows the user to type a numeric value of max 6 figures from the console // and returns it. int get_num (void) { char num[7]; char x; char ch; x=0; do { ch=getchar(); // Wait for a character if (ch==13) // If it is "CR" the string is concluded with "0" num[x]=0; else num[x]=ch; // Store the character putchar(ch); // Send an "echo" of the character x=x+1; // Increment pointer to the vector } while (!(ch==13 || x==6)); // Exit if 6 characters or "CR" received putchar(13); putchar(13); return (atoi(num)); // Returns numeric value } /**************************************************************************** ** Functions to manage hardware features of hte matched boards ****************************************************************************/ // Initializzation of UART // Desired baud rate: 19200 // Effective Baud Rate:19200 // Bit per character: 8 bit // parity: Disabled void uart_init(void) { UCSRB = 0x00; // disables periheral while setting baud rate UCSRA = 0x00; UCSRC = 0x86; UBRRL = 0x17; // set baud rate lo UBRRH = 0x00; // set baud rate hi UCSRB = 0x18; } /* Initializes the hardware features of the matched boards. */ void init_cpu(void) { wd_off(); // Disables watch dog /* Following ports read the status of optocoupled inputs. The signals must be configured as inputs. PC.0 <- IN1 PC.1 <- IN2 PD.2 <- IN3 PD.3 <- IN4 PD.4 <- IN5 PD.5 <- IN6 PC.2 <- IN7 PC.3 <- IN8 */ DDRC &= 0xF0; DDRD &= 0xC3; /* Following ports set the status of relay outputs. The signals must be configured as outputs. PB.0 -> OUT A1 PB.2 -> OUT A2 PD.6 -> OUT B1 PD.7 -> OUT B2 */ DDRB |= 0x05; DDRD |= 0xC0; } void set_relays(unsigned char dato) /* Sets status of the 4 buffered output lines with the value saved in low nibble of 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: PB.0 -> OUT A1 PB.2 -> OUT A2 PD.6 -> OUT B1 PD.7 -> OUT B2 */ { if ((dato&0x01)!=0) // Reads and sets status of PB.0=OUT A1 PORTB &= 0xFE; else PORTB |= 0x01; //endif if ((dato&0x02)!=0) // Reads and sets status of PB.2=OUT A2 PORTB &= 0xFB; else PORTB |= 0x04; //endif if ((dato&0x04)!=0) // Reads and sets status of PD.6=OUT A3 PORTD &= 0xBF; else PORTD |= 0x40; //endif if ((dato&0x08)!=0) // Reads and sets status of PD.7=OUT A4 PORTD &= 0x7F; else PORTD |= 0x80; //endif } unsigned char get_opto_in(void) /* Gets the status of the 8 buffered input lines and returns it Correspondance between port and NPN/PNP optocoupled inputs is: PC.0 <- IN1 PC.1 <- IN2 PD.2 <- IN3 PD.3 <- IN4 PD.4 <- IN5 PD.5 <- IN6 PC.2 <- IN7 PC.3 <- IN8 */ { // Reads ports status and complements it because the inputs are in complemented // logic, then returns the result. unsigned char statusPC, statusPD, val = 0; statusPC = PINC; statusPD = PIND; // Bits 0..1 di val val = statusPC & 0x03; // Bit 2..5 di val val |= statusPD & 0x3C; // Bits 6..7 di val val |= (statusPC & 0x0C) << 4; return(~val); } /**************************************************************************** Procedures for I2C BUS management ****************************************************************************/ /* Initializes I2CBUS hardware with bit rate 46080 Hz */ void init_i2c(void) { DDRC&=0xCF; PORTC|=0x30; // Enable pull-up TWSR&=0xFC; // Prescaler set to 1:1 TWBR=72; // Clock rate 46080 Hz } /* 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; TWCR = i2cc; // Fornisce comando passato hlpw = 0; // Resets timeout counter do { hlpw++; // Increments timeout counter wait_ms(1); // Pause 1 msec } while(((TWCR & 0x80) == 0) && (hlpw <= 50)); // Waits I2C interrupt flag or timeout return(TWSR & 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 error. */ void i2c_start(void) { TWCR|= 0x04; // Enables I2C hw controller if (i2c_cmd(0xA4)!=0x08) // Generates start and check status i2c_err=i2c_err | 0x01; // Set start error bit } /* Generates repeated start sequence for I2C BUS line through hw controller and checks for any possible error. */ void i2c_repstart(void) { if (i2c_cmd(0xA4)!=0x10) // Generates start and check status i2c_err=i2c_err | 0x02; // Set repeated start error bit } /* Generates stop sequence for I2C BUS line through hw controller and checks for any possible error. */ void i2c_stop(void) { unsigned int hlpw; TWCR = 0x94; // Generates stop hlpw = 0; // Resets timeout counter do { hlpw++; // Increments timeout counter wait_ms(1); // Pause 1 msec } while(((TWCR & 0x10) != 0) && (hlpw <= 50)); // Waits stop completed flag or timeout if (hlpw>50) // Final status check i2c_err=i2c_err | 0x04; // Set stop error bit TWCR&=0xFB; // Disable I2C 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) { TWDR=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(0x84)!=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(0x84)!=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 error. */ void i2c_wr(unsigned char i2c_byte) { TWDR = i2c_byte; // Write data on controller if (i2c_cmd(0x84)!=0x28) // Sends data and checks status i2c_err=i2c_err | 0x10; // Sets error on transmit data } /* 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(0xC4)!=0x50) // Receives data and checks status i2c_err=i2c_err | 0x20; // Sets receive data error bit // endif } else { // Receives data and sends NACK bit if (i2c_cmd(0x84)!=0x58) // Receives data and checks status i2c_err=i2c_err | 0x20; // Sets receive data error bit // endif } //endif return TWDR; // Restituisce il dato ricevuto } /* Shows current status of I2C BUS errors */ 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 led_mask, val_out; init_cpu(); // Initializes sections of CPU used set_relays(0); // Opens the contacts of all relays uart_init(); // Initializes USART as a console _textmode = 1; // Transforms \n into \r\n wait_ms(10); // Wait for signals settling clrscr(); // Shows demo program menu puts("Demo 1.1 for GMM AM08 ds110204 + GMBHR84 ds220503 + K51-AVR ds200500"); // Internal control check(); // Enable I2C BUS hardware peripheral i2c_err=0x00; // Reset error variable for I2C init_i2c(); // Initialize I2C BUS with clock 46080 Hz demo_init(); // Initialize peripherals used by K51-AVR puts(""); puts("Makes a shift on the TTL output signals on CN1."); while(TRUE) // Infinite Loop { led_mask=0x01; val_out=~led_mask; // To drive LEDs in complemented logic // For example with DEB 01 or TIO 16 while(led_mask) { i2c_n_send(Wpcf8574, &val_out, 1); // Send data to PCF8574 led_mask<<=1; // Left shift val_out=~led_mask; wait_ms(500); // To make the shift visible } led_mask=0x80; val_out=~led_mask; // To drive LEDs in complemented logic // For example with DEB 01 or TIO 16 while(led_mask) { i2c_n_send(Wpcf8574, &val_out, 1); // Send data to PCF8574 led_mask>>=1; // Right shift val_out=~led_mask; wait_ms(500); // To make the shift visible } } // end while(TRUE) }