// EEPROM_1.C (CCS PCB - PIC12CE519) // // Illustrates how to write to and read from EEPROM on 12CE519. // // Function _12c5_eeprom(int op, int EEADDR, int *p_dat) is used to // write to or read from EEPROM. op indicates whether the operation is // write or read. Note that the data is passed by reference. The // function returns PC_OFFSET which is useful in debugging. // // Function _12c5_eeprom is adapted from Microchip's "FLASH51X" routine. // It uses nominally 100 program words. Note that it uses table loop_up // to facilitate a "switch" and thus must be located on the lower half // of a page. // // Serial LCD on GP0. // // copyright, Peter H. Anderson, Baltimore, MD, April, '99 #case #include <12ce519.h> #include <defs_12c.h> // standard definitions for 12C series int _12c5_eeprom(int op, int EEADDR, int *p_dat); void set_dirs(int d); void set_options(int d); // timing routines void delay_ms(long t); void delay_10us(int t); // LCD routines void lcd_init(void); void out_RAM_str(int *s); void lcd_hex_byte(int val); void lcd_dec_byte(int val, int digits); int num_to_char(int val); void lcd_char(int ch); void lcd_new_line(void); #define TxData 0 #define WR_EE 1 #define RD_EE 0 void main(void) { int n, adr, y, dat; lcd_init(); for(n=0, adr=0x06; n<4; n++, adr++) // write some data to EEPROM { dat=adr+2; // generate some data y=_12c5_eeprom(WR_EE, adr, &dat); lcd_char('W'); // used for debugging lcd_hex_byte(y); // for debugging delay_ms(25); } lcd_new_line(); delay_ms(500); for (n=0, adr=0x06; n<4; n++, adr++) // now read it back and display it { dat=0; // just to show it is working _12c5_eeprom(RD_EE, adr, &dat); lcd_hex_byte(dat); lcd_char(' '); } lcd_new_line(); delay_ms(500); } int _12c5_eeprom(int op, int EEADDR, int *p_dat) { int COUNTER, PC_OFFSET, EEBYTE, EEDATA; if (op==WR_EE) { EEDATA=*p_dat; goto WRITE_BYTE; } goto READ_RANDOM; #asm READ_CURRENT: MOVLW 0x84 // PC offset for read current addr. EE_OK bit7='1' MOVWF PC_OFFSET // Load PC offset GOTO INIT_READ_CONTROL WRITE_BYTE: MOVLW 0x80 // PC offset for write byte. EE_OK: bit7 = '1' GOTO INIT_WRITE_CONTROL READ_RANDOM: MOVLW 0x83 // PC offset for read random. EE_OK: bit7 = '1' INIT_WRITE_CONTROL: MOVWF PC_OFFSET // Load PC offset register, value preset in W MOVLW 0xA0 // Control byte with write bit, bit 0 = '0' START_BIT: BCF GPIO, SDA // Start bit, SDA and SCL preset to '1' //******* Set up output data (control, address, or data) and counter //***** PREP_TRANSFER_BYTE: MOVWF EEBYTE // Byte to transfer to EEPROM already in W MOVLW 8 // Counter to transfer 8 bits MOVWF COUNTER //************ Clock out data (control, address, or data) byte //****** OUTPUT_BYTE: BCF GPIO, SCL // Set clock low during data set-up RLF EEBYTE, F // Rotate left, high order bit into carry bit BCF GPIO, SDA // Set data low, if rotated carry bit is BTFSC STATUS, C // a '1', then: BSF GPIO, SDA // reset data pin to a one, otherwise leave low NOP BSF GPIO, SCL // clock data into EEPROM DECFSZ COUNTER, F // Repeat until entire byte is sent GOTO OUTPUT_BYTE NOP // Needed to meet Timing (Thigh=4000nS) //********************** Acknowkedge Check //******* BCF GPIO, SCL // Set SCL low, 0.5us < ack valid < 3us NOP // Needed to meet Timing (Tlow= 4700nS) BSF GPIO, SDA NOP NOP NOP // Necessary for SCL Tlow at low voltage, NOP // Tlow=4700nS BSF GPIO, SCL // Raise SCL, EEPROM acknowledge still valid BTFSC GPIO, SDA // Check SDA for acknowledge (low) BCF PC_OFFSET, 7 // If SDA not low (no ack), set error flag BCF GPIO, SCL // Lower SCL, EEPROM release bus BTFSS PC_OFFSET, 7 // If no error continue, else stop bit GOTO STOP_BIT //***** Set up program counter offset, based on EEPROM operating mode //***** MOVF PC_OFFSET, W ANDLW 0x0F ADDWF PCL, F GOTO INIT_ADDRESS // PC offset=0, write control done, send address GOTO INIT_WRITE_DATA // PC offset=1, write address done, send data GOTO STOP_BIT // PC offset=2, write done, send stop bit GOTO INIT_ADDRESS // PC offset=3, write control done, send address GOTO INIT_READ_CONTROL // PC offset=4, send read control GOTO READ_BIT_COUNTER // PC offset=5, set counter and read byte GOTO STOP_BIT // PC offset=6, random read done, send stop //********** Initalize EEPROM data (address, data, or control) bytes //****** INIT_ADDRESS: INCF PC_OFFSET, F // Increment PC offset to 2 (write) or to 4 (read) MOVF EEADDR,W // Put EEPROM address in W, ready to send to EEPROM GOTO PREP_TRANSFER_BYTE INIT_WRITE_DATA: INCF PC_OFFSET, F // Increment PC offset to go to STOP_BIT next MOVF EEDATA,W // Put EEPROM data in W, ready to send to EEPROM GOTO PREP_TRANSFER_BYTE INIT_READ_CONTROL: BSF GPIO, SCL // Raise SCL BSF GPIO, SDA // raise SDA INCF PC_OFFSET, F // Inc PC offset to go to READ_BIT_COUNTER next MOVLW 0xA1 // Set up read control byte, ready to send to EEPROM GOTO START_BIT // bit 0 = '1' for read operation //************************** Read EEPROM data //***** READ_BIT_COUNTER: BSF GPIO, SDA // set data bit to 1 so we're not pulling bus down. NOP BSF GPIO, SCL MOVLW 8 // Set counter so 8 bits will be read into EEDATA MOVWF COUNTER READ_BYTE: BSF GPIO, SCL // Raise SCL, SDA valid. SDA still input from ack BSF STATUS, C // Assume bit to be read = 1 BTFSS GPIO, SDA // Check if SDA = 1 BCF STATUS, C // if SDA not = 1 then clear carry bit RLF EEDATA, F // rotate carry bit (=SDA) into EEDATA; BCF GPIO, SCL // Lower SCL BSF GPIO, SDA // reset SDA DECFSZ COUNTER, F //Decrement counter GOTO READ_BYTE // Read next bit if not finished reading byte BSF GPIO, SCL NOP BCF GPIO, SCL //****************** Generate a STOP bit and RETURN *********************** //***** STOP_BIT: BCF GPIO, SDA // SDA=0, on TRIS, to prepare for transition to '1' BSF GPIO, SCL // SCL = 1 to prepare for STOP bit NOP // equivalent 4 NOPs neccessary for I2C spec Tsu:sto = 4.7us NOP NOP NOP BSF GPIO, SDA // Stop bit, SDA transition to '1' while SCL high #endasm *p_dat = EEDATA; return(PC_OFFSET); // 1 is OK, 0 is NO } void set_options(int d) { #asm MOVF d, W OPTION #endasm } void set_dirs(int d) { #asm MOVF d, W TRIS GPIO #endasm } // timing routines void delay_10us(int t) { #asm DELAY_10US_1: CLRWDT NOP NOP NOP NOP NOP NOP DECFSZ t, F GOTO DELAY_10US_1 #endasm } void delay_ms(long t) // delays t millisecs { do { delay_10us(100); } while(--t); } // LCD routines int num_to_char(int val) // converts val to hex character { int ch; if (val < 10) { ch=val+'0'; } else { val=val-10; ch=val + 'A'; } return(ch); } void lcd_char(int ch) // serial output to PIC-n-LCD, 9600 baud { int n, dly; // start bit + 8 data bits #asm MOVLW 9 MOVWF n BCF STATUS, C LCD_CHAR_1: BTFSS STATUS, C BSF GPIO, TxData BTFSC STATUS, C BCF GPIO, TxData MOVLW 32 MOVWF dly LCD_CHAR_2: DECFSZ dly, F GOTO LCD_CHAR_2 RRF ch, F DECFSZ n, F GOTO LCD_CHAR_1 BCF GPIO, TxData CLRWDT MOVLW 96 MOVWF dly LCD_CHAR_3: DECFSZ dly, F GOTO LCD_CHAR_3 CLRWDT #endasm } void lcd_init(void) // sets TxData in idle state and resets PIC-n-LCD { #asm BCF GPIO, TxData BCF DIRS, TxData MOVF DIRS, W TRIS GPIO #endasm lcd_char(0x0c); delay_ms(250); } void lcd_new_line(void) // outputs 0x0d, 0x0a { lcd_char(0x0d); delay_ms(10); // give the PIC-n-LCD time to perform the lcd_char(0x0a); // new line function delay_ms(10); } void out_RAM_str(int s) { while(*s) { lcd_char(*s); ++s; } } void lcd_hex_byte(int val) // displays val in hex format { int ch; ch = num_to_char((val>>4) & 0x0f); lcd_char(ch); ch = num_to_char(val&0x0f); lcd_char(ch); } void lcd_dec_byte(int val, int digits) // displays byte in decimal as either 1, 2 or 3 digits { int d; int ch; if (digits == 3) { d=val/100; ch=num_to_char(d); lcd_char(ch); } if (digits >1) // take the two lowest digits { val=val%100; d=val/10; ch=num_to_char(d); lcd_char(ch); } if (digits == 1) // take the least significant digit { val = val%100; } d=val % 10; ch=num_to_char(d); lcd_char(ch); }