// LOGGER_1.C (CCS PCM, PIC16F84) // // This was one of a weekly project assigned in my undergraduate micro // processor class. This was written by Lawrence Taylor and Lisa // Archer-Davies, Baltimore. Both will be graduating in May, '99. // // The idea is a small unit that might be packed with strawberries, // medicine or other temperature sensitive items during transit to // periodically log temperature and store the result in EEPROM. We // used a 24C32 (4K X 8), but this could be modified to be a 65, 128 or // 24LC256. // // On boot, the processor reads the LOG/DUMP input on RA.2. If LOG // the processor reads the NORM/INIT input on RA.3. If INIT, the // next 24C32 memory address is intitialized to 0x0000 and // this is saved to the 16F84's EEPROM and the processors goes into a // sleep mode. // // If LOG and NORM, the unit periodically measures temperature, fetches // the address of the next 24C32 location from the 16F84 EEPROM, writes // the temperature to this address in the 24C32, increments the address // pointer and saves to the 16F84 EEPROM. That is, the memory address // pointer is implemented using nonvolatile memory. Thus, if power is // lost, any data which has been logged is not overwritten. Rather, // logging picks up where it left off. // // If DUMP, the logged data is displayed on a serial LCD. // // Timing between samples is performed using loop timing. // // An LED on RA.0 provides feedback to the user when the processor // boots. DUMP - 1 flash, INIT - 2 flashes, LOG NORM - 3 flashes. // // In this routine, a temperature measurement is not actually performed. // Rather, get_temp() is a stub which returns various values. This // can be replaced with a measurment using the DS1621, DS1820 or similar // or with any other measurement. // // copyright, Peter H. Anderson, Baltimore, MD, Apr, '99 #case #include <16f84.h> #include <defs_f84.h> // delay routines void delay_10us(int t); void delay_ms(long t); // i2c routines byte i2c_in_byte(void); void i2c_out_byte(byte o_byte); void i2c_nack(void); void i2c_ack(void); void i2c_start(void); void i2c_stop(void); void i2c_high_sda(void); void i2c_low_sda(void); void i2c_high_scl(void); void i2c_low_scl(void); // 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); void write_16f84_eeprom(int adr, int d); int read_16f84_eeprom(int adr); void write_32(long adr, int dat); int read_32(long adr); int get_temp(void); long get_mem_ptr(void); void save_mem(long current); void flash(int blinks); #define TxData 0 // RA.0 for serial LCD #define SDA_PIN rb2 // RB.2 #define SCL_PIN rb1 // RB.1 #define SDA_DIR trisb2 #define SCL_DIR trisb1 #define MAX_ADR 0x2000 // for 24C32 - 0x1000, for 2465 - 0x2000, for 24128 - 0x4000 // for 24256 - 0x8000 main() { long mem_adr; int y, n, t_f, location; trisa2 = 1; // LOG/DUMP switch trisa3 = 1; // NORM / INIT switch trisa1 = 0; // LED lcd_init(); if(!ra2) //dump data { flash(1); // flash LED one time location = get_mem_ptr(); // fetch ptr from 16F84 EEPROM for(n = 0; n < location; n++) { y = read_32(n); // read byte from 2432 at adress n lcd_dec_byte(y, 2); // and display lcd_char(' '); if ((((n + 1) % 4) == 0) && (n != 15)) { lcd_new_line(); } } #asm SLEEP #endasm } else if(!ra3) //initialize memory pointer { flash(2); // flash LED two times write_16f84_eeprom(0x00,0x00); write_16f84_eeprom(0x01,0x00); #asm SLEEP #endasm } else // log the data { flash(3); // give some feedback do { mem_adr = get_mem_ptr(); // get next available address t_f = get_temp(); // make a temperature measurment write_32(mem_adr, t_f); // save it to the 2432 EEPROM ++mem_adr; save_mem(mem_adr); // save next avaiable location delay_ms(30000); // 30 seconds between samples }while(mem_adr < MAX_ADR); // EEPROM full #asm SLEEP #endasm } } long get_mem_ptr(void) // fetch memory pointer from 16F84 EEPROM { int hi, lo; hi = read_16f84_eeprom(0x00); lo = read_16f84_eeprom(0x01); return(hi << 8)|(lo); } void save_mem(long current) // save memory pointer to 16F84 { write_16f84_eeprom(0x00,current >> 8); // high byte write_16f84_eeprom(0x01,(current & 0xff)); // low byte } void flash(int blinks) // flash LED blink times { int n; for(n = 0; n < blinks; n++) { ra1 = 1; delay_ms(300); ra1 = 0; delay_ms(300); } } void write_32(long adr, int dat) // write dat to adr in 24C32 { i2c_start(); i2c_out_byte(0xa0); i2c_nack(); i2c_out_byte((adr >>8)& 0xff); i2c_nack(); i2c_out_byte(adr & 0xff); i2c_nack(); i2c_out_byte(dat); i2c_nack(); i2c_stop(); delay_ms(25); // wait for eeprom to program } int read_32(long adr) // fetch data at adr in 24C32 { int y; i2c_start(); i2c_out_byte(0xa0); i2c_nack(); i2c_out_byte((adr >>8)& 0xff); i2c_nack(); i2c_out_byte(adr & 0xff); i2c_nack(); i2c_start(); i2c_out_byte(0xA1); i2c_nack(); y = i2c_in_byte(); //i2c_nack(); i2c_stop(); return (y); } int read_16f84_eeprom(int adr) { EEADR=adr; rd=1; // set the read bit return(EEDATA); } void write_16f84_eeprom(int adr, int d) { EEADR = adr; EEDATA = d; wren = 1; // write enable EECON2 = 0x55; // protection sequence EECON2 = 0xaa; wr = 1; // begin programming sequnce delay_ms(20); wren = 0; // disable write enable } int get_temp(void) // this is simply a stub to return the number { // of degrees F static int val = 68; ++val; if(val>82) { val = 68; } return(val); } // delay routines void delay_10us(int t) { #asm BCF STATUS, RP0 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); } // i2c routines byte i2c_in_byte(void) { byte i_byte, n; i2c_high_sda(); for (n=0; n<8; n++) { i2c_high_scl(); if (SDA_PIN) { i_byte = (i_byte << 1) | 0x01; // msbit first } else { i_byte = i_byte << 1; } i2c_low_scl(); } return(i_byte); } void i2c_out_byte(byte o_byte) { byte n; for(n=0; n<8; n++) { if(o_byte&0x80) { i2c_high_sda(); } else { i2c_low_sda(); } i2c_high_scl(); i2c_low_scl(); o_byte = o_byte << 1; } i2c_high_sda(); } void i2c_nack(void) { i2c_high_sda(); // data at one i2c_high_scl(); // clock pulse i2c_low_scl(); } void i2c_ack(void) { i2c_low_sda(); // bring data low and clock i2c_high_scl(); i2c_low_scl(); i2c_high_sda(); } void i2c_start(void) { i2c_low_scl(); i2c_high_sda(); i2c_high_scl(); // bring SDA low while SCL is high i2c_low_sda(); i2c_low_scl(); } void i2c_stop(void) { i2c_low_scl(); i2c_low_sda(); i2c_high_scl(); i2c_high_sda(); // bring SDA high while SCL is high // idle is SDA high and SCL high } void i2c_high_sda(void) { // bring SDA to high impedance SDA_DIR = 1; delay_10us(5); } void i2c_low_sda(void) { SDA_PIN = 0; SDA_DIR = 0; // output a hard logic zero delay_10us(5); } void i2c_high_scl(void) { SCL_DIR = 1; // high impedance delay_10us(5); } void i2c_low_scl(void) { SCL_PIN = 0; SCL_DIR = 0; delay_10us(5); } // 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 BCF STATUS, RP0 MOVLW 9 MOVWF n BCF STATUS, C LCD_CHAR_1: BTFSS STATUS, C BSF PORTA, TxData BTFSC STATUS, C BCF PORTA, 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 PORTA, 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 STATUS, RP0 BCF PORTA, TxData BSF STATUS, RP0 BCF TRISA, TxData BCF STATUS, RP0 #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); }