PIC C, Using the 12CE5X Internal EEPROM
// 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);
}