www.pudn.com > ÓÃCÓïÑÔ¶ÁдCF¿¨³ÌÐò.zip > hoarder.c


// Hoarder board software http://vadim.www.media.mit.edu/Hoarder/Hoarder.htm 
// Copyright (C) 2001-2002 MIT Media Laboratory 
// Concept, design, and programming by Vadim Gerasimov  
// Code fragments by Paul Covell, Matthew Drake 
// Ideas contributed by  
// Paul Covell, Ted Selker, Sandy Pentland, Richard DeVaul,  
// Brian Silverman, Tim Hirzel, Josh Weaver, Ivan Chardin  
 
#include <16F877.H> 
#list 
#include "Math32.c" 
 
// Configuration bits for initial programming 
#fuses   HS, NOPROTECT, WDT, PUT, BROWNOUT, NOLVP 
 
// 20 MHz clock 
#use delay (clock=20000000) 
 
// up to 57.6kBaud (recommended 38.4) to be compatible with BiM2 transceiver  
// up to 115,2kBaud for cable connection 
#use rs232 (baud=115200, xmit=PIN_C6, rcv=PIN_C7) 
 
// Disable automatic TRIS programming for all ports 
#use fast_io(a)   
#use fast_io(b)   
#use fast_io(c) 
#use fast_io(d) 
#use fast_io(e) 
 
// for MIThril bus and I2C-enabled add-on boards 
#use I2C (master, sda=PIN_C4, scl=PIN_C3, address=0xc0, SLOW) 
 
// define for initial PIC programming to include the serial programming function 
#define InitialProgram 
 
// define only one: Rochester, ESL, GAME, or Tim 
//#define Rochester 
#define ESL // Every Sign of Life collect/broadcast 
//#define GAME // Ball game broadcast 
//#define Tim // Tim Hirzel's project data collection 
 
// define OldSAK for boards earlier than NOV 2001 
//#define OldSAK 
 
// define DelayedWrite to use with IBM MicroDrive (TM) 
//#define DelayedWrite 
 
// CompactFlash pins 
// 3 address lines (all others grounded) 
  #define CF_A0      PIN_B0 
  #define CF_A1      PIN_B1  
  #define CF_A2      PIN_B2 
// data flow strobe signals 
  #define CF_N_OE    PIN_B3 
  #define CF_N_WE    PIN_B4 
// card detect signal (high if card is not present)   
  #define CF_CD1     PIN_B7 
  #define cf_out     input(CF_CD1) 
// CF power switch   
#ifdef OldSAK 
  #define CF_POWER   PIN_C0 
#else 
  #define CF_POWER   PIN_B6 
#endif 
 
// LED pins   
  #define CF_LED0    PIN_C1 
  #define CF_LED1    PIN_C2 
 
// EKG amplifier enable for BIOMETRICS add-on module    
  #define EKG_EN     PIN_C5 
 
// Real time clock signals 
// two lines shared with CF data bus 
  #define RTC_RST    PIN_B5	    
  #define RTC_CLK    PIN_D0	    
  #define RTC_IO     PIN_D1	 
 
// BiM transceiver pins TX-, RX-enable and Carrier Detect 
// TX, RX data pins connected to UART 
#ifdef OldSAK 
  #define BIM_TX     PIN_E0    
  #define BIM_RX     PIN_E1    
  #define BIM_CD     PIN_E2  
#else 
  #define BIM_TX     PIN_E2    
  #define BIM_RX     PIN_E1    
  #define BIM_CD     PIN_C0  
#endif   
 
#define RBIF     0x58 
#define RCIF	   0x65 
#define OERR	   0xC1 
#define CREN	   0xC4 
 
// Event button -- Tim Hirzel's project 
#define BUTTON	 PIN_A4 
 
#byte   SPBRG	   = 0x99 
#byte   TXSTA	   = 0x98 
#byte   PORTA	   = 0x05	 
#byte   PORTB	   = 0x06	 
#byte   PORTC	   = 0x07	 
 
#byte CF_DATA   = 0x08  /* Port D */ 
#byte CF_DATA_TRIS = 0x88 /* Port D TRIS */ 
 
// 1-in 0-out 
#define TRIS_A     0xFF  // 11111111  
#define TRIS_B     0x80  // 10000000  
 
#ifdef OldSAK 
  #define TRIS_C     0xF8  // 11111000 
  #define TRIS_C_SLEEP 0x80  // 10000000 
  #define TRIS_E 0x04  // 0000x100 
#else 
  #define TRIS_C     0xD9  // 11011001  
  #define TRIS_C_SLEEP 0x81  // 10000001 
  #define TRIS_E 0x01  // 0000x001 
#endif 
 
#define TRIS_D_IN  0xFF  // 11111111 
#define TRIS_D_OUT 0x00  // 00000000  
#define TRIS_D_RTC_OUT 0xFC 
#define TRIS_D_RTC_IN  0xFE 
 
// Function Prototypes  
void print_prompt(); 
#separate void save_audio(); 
#separate void save_data(); 
#separate void send_data(); 
#separate void send_data_bim(); 
#separate void send_data_Tim(); 
#separate void send_player_info(); 
void led_off(); 
void led_red(); 
void led_green(); 
#separate boolean process_command(); 
void print_help(); 
int fromHex(char hex); 
void set_default_signals(); 
void cf_task_file_read(int addr); 
void cf_task_file_write(int addr, int data); 
byte rtc_read(int addr); 
void rtc_write(int addr, int data); 
#separate boolean get_disk_configuration(); 
void cf_set_addr(int addr); 
 
 
// Global variables 
 
// Data file name on CF card 
char const fname[12] = "ESL     DAT"; 
// A/D buffer 
#define buf_size 32 
long buffer[buf_size]; 
int buf_head; 
int buf_count; 
int ad_count; 
boolean ad_on; 
boolean audio; 
long voltage; 
 
int curData;  // Holds data to be sent 
int curAddr;  // Holds address to be read/written 
 
char sector[4]; // Current CF sector number for Read/Write operations 
boolean disk_valid; // CF seems ok 
boolean file_valid; // Data file seems ok 
 
// FAT (file allocation table) variables 
char data_start[4]; 
char fat_start[4]; // first sector of first copy of FAT 
char dir_start[4]; // first sector of root directory 
char data_sectors[4]; // number of data sectors 
char data_file_size[4]; 
char sector_counter[4]; // to count down number of unused sectors in data file 
byte sectors_per_cluster; 
long root_entries; // number of entries in root directory 
long cluster; 
 
// Real time clock buffer 
char time[8]; // hold all 8 time bytes from RTC chip 
// Time in ms after last reset (if timer int set to 1kHz) 
char ms[4]; // millisecond timer 
 
char cmd; // last command character 
long write_pos; // write position in current sector 
long crc16;  
 
 
// self-programming function 
// reprograms PIC thru serial port 
// host sends pieces of code as: 
// 1 byte header 0xAB 
// 2 bytes start address 
// 2 bytes (N) number of words to program 
// N words (N*2 bytes) program words 
//  
// after each program word function sends back  
// 1 if word programmed successfully 
// 0 if not 
// 
// send non 0XAB byte to exit the cycle and restart Hoarder 
// 
#ifdef InitialProgram	 
#ORG 0x1F00, 0x1FFF 
void self_program() 
{ 
  long addr, count, val, i; 
  disable_interrupts(GLOBAL); 
  while(getc()==0xAB) { 
    *(&addr)=getc(); 
    *(&addr+1)=getc(); 
    *(&count)=getc(); 
    *(&count+1)=getc(); 
    for (i=0; i3) ad_count=0; 
    } 
//#ifdef GAME    
//    if (ad_count==1) set_adc_channel(4); else set_adc_channel(ad_count);	 
//#else 
    set_adc_channel(ad_count);	 
//#endif 
  } 
} 
 
// Resets AD buffer 
void purge_adc_buffer() 
{ 
  buf_head=0; 
  buf_count=0; 
  set_adc_channel(0); 
  ad_count=0; 
} 
 
// Green-Red... Happy? 
void blink_happy() 
{ 
  while (TRUE) { 
    led_green(); 
    delay_ms(250);  
    led_red(); 
    delay_ms(250);  
  } 
} 
 
void blink_sad() 
{ 
  while (TRUE) { 
    led_red(); 
    delay_ms(250);  
    led_off(); 
    delay_ms(250);  
  } 
} 
 
// Turn off CF 
void disable_cf() 
{ 
  set_default_signals(); 
} 
 
// exit if CF is plugged; otherwise sleep waking up on WDT to check for CF;  
void take_a_nap() 
{ 
  if (input(CF_POWER)) { 
    output_low(CF_POWER); 
    delay_ms(10); 
  }	 
  disable_cf(); 
  do { 
    while(cf_out) {	 
      led_red(); 
      delay_ms(1);	 
      CF_DATA=0; 
      SET_TRIS_D(TRIS_D_OUT); 
      set_tris_c(TRIS_C_SLEEP); 
      led_off(); 	 
      sleep(); 
    }    
    delay_ms(500); 
  } while(cf_out); // make sure CF is still in after 0.5 sec  
  output_low(CF_POWER); // power up 
  output_high(CF_N_OE); 
  output_high(CF_N_WE); 
  SET_TRIS_D(TRIS_D_IN); 
  led_green();	 
  delay_ms(250); 
  led_off(); 	 
  // Check CF card configuration 
  disk_valid=get_disk_configuration(); 
} 
 
boolean enable_cf() 
{ 
  // sleep until CF is present 
  take_a_nap(); 
  return TRUE;  
} 
 
 
#define I2CWRITE 0b00000000 
#define I2CREAD  0b00000001 
 
void putI2C(int device, int data)  
{ 
  i2c_start(); 
  i2c_write(device); 
  i2c_write(data); 
  i2c_stop(); 
} 
 
int getI2C(int device)  
{ 
  i2c_start(); 
  i2c_write(device | I2CREAD); 
  _return_ = i2c_read(); 
  i2c_stop(); 
} 
 
void send_hello() 
{ 
  while (!input(RCIF)) printf("Hello! "); 
} 
   
void main() 
{ 
  setup_adc_ports(A_ANALOG); // port A all analog inputs 
  setup_adc(ADC_CLOCK_DIV_32); // set ADC clock 
  setup_psp(PSP_DISABLED); 
 
  set_default_signals(); // preset some of the signals 
 
  SET_TRIS_A(TRIS_A); // set TRIS values 
  SET_TRIS_B(TRIS_B); 
  SET_TRIS_C(TRIS_C); 
  SET_TRIS_D(TRIS_D_IN); 
  SET_TRIS_E(TRIS_E); 
 
  port_b_pullups(TRUE); // Should be enabled; Used for B6 and B7 (CF power and CF detection) 
  setup_timer_2(T2_DIV_BY_4, 250, 5); // default timer: 1mS interval @ 20 MHz; see timer2() 
  zero32(ms); // zero millisecond counter 
  ad_on=FALSE; 
  audio=FALSE; 
  purge_adc_buffer(); 
 
//  enable_interrupts(INT_RB);   
  enable_interrupts(INT_TIMER2);   
  enable_interrupts(GLOBAL); 
   
  disable_cf(); 
//  set_default_signals(); 
//  send_hello(); 
#ifdef GAME   
  puts("GAME"); 
  send_player_info(); 
#else   
//  take_a_nap(); 
#endif 
#ifdef Rochester   
  puts("Roch"); 
  delay_ms(5000); 
  save_audio(); 
#endif   
#ifdef ESL   
  puts("HackFest v odno slovo"); 
//  send_data_bim(); 
#endif   
#ifdef Tim   
  puts("Tim"); 
  send_data_Tim(); 
#endif   
  print_prompt();	 
  while(TRUE) 
  { 
    led_off(); 
    process_command(); 
    led_red();	 
    led_green(); 
    led_green(); 
//    if (cf_out) take_a_nap(); 
  } 
} 
 
void set_default_signals() 
{ 
  // default RTC signals 
  output_low(RTC_RST); 
  output_low(RTC_CLK); 
  output_low(RTC_IO);  
  // default CF power down signals 
  output_low(CF_N_OE); 
  output_low(CF_N_WE); 
  cf_set_addr(0); 
  output_high(CF_POWER); 
  // default radio modem signals 
  output_high(BIM_TX); // do not transmit 
  output_high(BIM_RX); // do not recive 
  // disable EKG amplifiers if present 
  output_high(EKG_EN); 
  // presumption of invalidity 
  disk_valid=false; 
} 
 
// reset CF 
#separate 
void cf_reset() 
{ 
  output_high(CF_POWER); 
  delay_ms(500); 
  output_low(CF_POWER); 
} 
 
// set CF port address (see task file in CF specs) 
// Horder uses ports 0-7 to control CF 
void cf_set_addr(int addr) 
{ 
  output_bit(CF_A0, bit_test(addr, 0)); 
  output_bit(CF_A1, bit_test(addr, 1)); 
  output_bit(CF_A2, bit_test(addr, 2)); 
} 
 
// write 1 byte into current CF port 
void cf_write(int data) 
{ 
  // set data	 
  CF_DATA_TRIS = TRIS_D_OUT; 
  CF_DATA = data; 
  // Strobe write signal 
  OUTPUT_LOW(CF_N_WE); 
  OUTPUT_HIGH(CF_N_WE); 
  CF_DATA_TRIS = TRIS_D_IN; 
} 
 
// read 1 byte from current CF port 
int cf_read() 
{ 
  CF_DATA_TRIS = TRIS_D_IN; 
  // strobe read signal and get data 
  output_low(CF_N_OE); 
  _return_ = CF_DATA; 
  output_high(CF_N_OE); 
} 
 
// skip bytes from current sector buffer 
// assumes: CF port set to 0 
void cf_read_skip(unsigned int count) 
{ 
  CF_DATA_TRIS = TRIS_D_IN; 
  do { 
    output_low(CF_N_OE); 
    output_high(CF_N_OE); 
    count--; 
  } while (count>0); 
} 
 
// write data to CF port addr 
void cf_task_file_write(int addr, int data) 
{ 
  cf_set_addr(addr); 
  cf_write(data); 
} 
 
// read byte from CF port addr 
int cf_task_file_read(int addr) 
{ 
  cf_set_addr(addr); 
  return(cf_read()); 
} 
 
// enable nice power-saving features of Microdrive (see Microdrive specs) 
// does nothing if used with regular CF memory cards 
void enable_microdrive_write_cache() 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(1, 0x02);   // 0x02 = enable write cache   
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0xEF);   // set features command 
  while ((cf_read()&0xC0)!=0x40); 
  cf_task_file_write(1, 0xAA);   // 0xAA = enable read look ahead   
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0xEF);   // set features command 
  while ((cf_read()&0xC0)!=0x40); 
#ifdef DelayedWrite   
  cf_task_file_write(1, 0x07);   // 7 = enable delayed write   
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0xFA);   // set delayed write command 
  while ((cf_read()&0x80)==0x80); 
#endif   
} 
 
// start reading CF sector 
// global var sector indicates sector number 
// after calling this function use cf_read() and cf_skip(int)  
// to read/skip 512 bytes from sector 
void cf_start_sector_read() 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(2, 1);      
  cf_task_file_write(3, sector[0]);      
  cf_task_file_write(4, sector[1]);      
  cf_task_file_write(5, sector[2]);      
  cf_task_file_write(6, 0xE0 | (sector[3]&0x0F));      
  cf_task_file_write(7, 0x20);   // read sector command 
  while ((cf_read()&0x88)!=0x08); 
  cf_set_addr(0); 
} 
 
// start writing CF sector 
// global var sector contains sector number 
// after calling this function use cf_write() 
// to write 512 bytes to sector 
void cf_start_sector_write() 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(2, 1);      
  cf_task_file_write(3, sector[0]);      
  cf_task_file_write(4, sector[1]);      
  cf_task_file_write(5, sector[2]);      
  cf_task_file_write(6, 0xE0 | (sector[3]&0x0F));      
  cf_task_file_write(7, 0x30);   // write sector command 
  while ((cf_read()&0x88)!=0x08); 
  cf_set_addr(0); 
} 
 
// put CF to sleep mode (see CF specs) 
#separate 
void cf_sleep() 
// put the card into sleep mode 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0x99);   // sleep command 
} 
 
// put CF to standby mode (see CF specs) 
#separate 
void cf_standby() 
// put the card into stadby mode 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0x94);   // standby immediate command 
} 
 
// put CF to idle mode (see CF specs) 
#separate 
void cf_idle(int delay) 
// put the card into idle mode, sets a delay for auto power-down 
{ 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(2, delay);      
  cf_task_file_write(6, 0xE0);      
  cf_task_file_write(7, 0x97);   // idle command 
} 
 
void print_prompt() 
{ 
  putc('>'); 
} 
 
void led_off() 
{ 
  OUTPUT_LOW(CF_LED0); 
  OUTPUT_LOW(CF_LED1); 
} 
 
void led_green() 
{ 
  OUTPUT_LOW(CF_LED1); 
  OUTPUT_HIGH(CF_LED0); 
} 
 
void led_red() 
{ 
  OUTPUT_LOW(CF_LED0); 
  OUTPUT_HIGH(CF_LED1); 
} 
 
// getc() after taking care of possible overflow error 
char loadc() 
{ 
  while (!input(RCIF)) 
  { 
    if (input(OERR)) 
    { 
      output_low(CREN); 
      output_high(CREN); 
    } 
  } 
  return getc(); 
} 
 
// Yep! It's nope. Used for short delays. 
#inline 
void nop() 
{ 
  #asm 
  nop 
  #endasm 
} 
 
// RTC functions 
// see RTC chip specs http://pdfserv.maxim-ic.com/arpdf/DS1302.pdf (if still there) 
// for data format etc 
 
// IMPORTANT 
// RTC READ/WRITE DOES NOT WORK IF CF CARD IS PLUGGED IN AND POWERED DOWN 
// This is because of sharing port D pins 
// So make sure CF is either out or powered up before using RTC 
 
// Read time from RTC chip into time[]  
// 8-byte burst operation 
// Note: nop() are important to get proper timing at 20MHz 
void rtc_read_time() 
{ 
  static byte i, j, result; 
 
  set_tris_d(TRIS_D_RTC_OUT); 
  output_high(RTC_RST); 
  output_high(RTC_IO); 
   
  for (i=0; i<6; i++) { 
    output_high(RTC_CLK); 
    nop(); 
    output_low(RTC_CLK); 
  } 
  output_low(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  output_high(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  set_tris_d(TRIS_D_RTC_IN); 
   
  for (j=0; j<8; j++) { 
    for (i=0; i<8; i++) { 
      output_low(RTC_CLK); 
      nop(); 
    	shift_right(&result, 1, input(RTC_IO)); 
      output_high(RTC_CLK); 
    } 
    time[j]=result; 
  } 
   
  output_low(RTC_CLK); 
  output_low(RTC_IO);  
  output_low(RTC_RST); 
  SET_TRIS_D(TRIS_D_IN); 
} 
 
// Read and return one byte from RTC chip at addr 
int rtc_read(int addr) 
{ 
  int i; 
 
  set_tris_d(TRIS_D_RTC_OUT); 
  output_high(RTC_RST); 
  output_high(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  for (i=0; i<6; i++) { 
    output_bit(RTC_IO, bit_test(addr, 0)); 
    output_high(RTC_CLK); 
    addr>>=1; 
    output_low(RTC_CLK); 
  } 
  output_high(RTC_IO); 
  output_high(RTC_CLK); 
  set_tris_d(TRIS_D_RTC_IN); 
  for (i=0; i<8; i++) { 
    output_low(RTC_CLK); 
    nop(); 
  	shift_right(&_return_, 1, input(RTC_IO)); 
    output_high(RTC_CLK); 
  } 
  output_low(RTC_CLK); 
  output_low(RTC_IO);  
  output_low(RTC_RST); 
  SET_TRIS_D(TRIS_D_IN); 
} 
 
// Write data to RTC chip at addr 
void rtc_write(int addr, int data) 
{ 
  int i; 
 
  set_tris_d(TRIS_D_RTC_OUT); 
  output_high(RTC_RST); 
  output_low(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  for (i=0; i<6; i++) { 
    output_bit(RTC_IO, addr&1); 
    output_high(RTC_CLK); 
    addr>>=1; 
    output_low(RTC_CLK); 
  } 
  output_high(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  for (i=0; i<8; i++) { 
    output_bit(RTC_IO, data&1); 
    output_high(RTC_CLK); 
    data>>=1; 
    output_low(RTC_CLK); 
  } 
  output_low(RTC_IO);  
  output_low(RTC_RST); 
  set_tris_d(TRIS_D_IN); 
} 
 
// Write time to RTC chip 
// 8-byte burst operation 
void rtc_set_time() 
{ 
  int i, j, data; 
 
  set_tris_d(TRIS_D_RTC_OUT); 
  output_high(RTC_RST); 
  output_low(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  output_high(RTC_IO); 
  for (i=0; i<5; i++) { 
    output_high(RTC_CLK); 
    output_low(RTC_CLK); 
  } 
  output_low(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  output_high(RTC_IO); 
  output_high(RTC_CLK); 
  nop(); 
  output_low(RTC_CLK); 
  for (j=0; j<8; j++) 
  { 
    data=time[j]; 
    for (i=0; i<8; i++) { 
      output_bit(RTC_IO, data&1); 
      output_high(RTC_CLK); 
      data>>=1; 
      output_low(RTC_CLK); 
    } 
  } 
  output_low(RTC_IO);  
  output_low(RTC_RST); 
  set_tris_d(TRIS_D_IN); 
} 
 
// Use this function to skip previous records in data file and prevent 
// accidental loss of previously recorded data when Hoarder restarts. 
// Skips to first "unused" sector of data file. 
// - "unused" sector has either invalid signature or timestamp lower than previous sector 
// - sectors written by Hoarder have "ESL" signature and 6-byte time stamp in the beginning 
// 
// Function returns TRUE if data file is valid and not full 
//                  FALSE if data file is not valid or no space left 
// If function returns TRUE sector points to first "unused" sector of data file and  
// sector_counter indicates number of free sectors  
#separate 
boolean init_file_write() { 
  int i, t[6]; 
  if (cf_out || !(disk_valid && file_valid)) return FALSE; 
  move32(sector, data_start); 
  move32(sector_counter, data_file_size); 
  for (i=0; i<6; i++) time[i]=0; 
  while (!is_zero32(sector_counter)) { 
    for (i=0; i<6; i++) t[i]=time[i]; 
    cf_start_sector_read(); 
    if ((cf_read()!='E')||(cf_read()!='S')||(cf_read()!='L')) return TRUE; 
    for (i=0; i<6; i++) time[i]=cf_read(); 
    i=6; 
    while (--i!=0xFF) if (time[i]t[i]) i=0; 
    inc32(sector); 
    dec32(sector_counter); 
  } 
  cf_idle(100); 
  return FALSE; 
} 
 
// Call cf_start_sector_write() for next sector in data file 
// return FALSE if no free sectors left 
// uses global vars sector and sector_counter 
#separate 
boolean next_file_sector_write() { 
  if (cf_out) return FALSE; 
  if (is_zero32(sector_counter)) { 
    cf_idle(100); 
    return FALSE; 
  } 
  cf_start_sector_write(); 
  inc32(sector); 
  dec32(sector_counter); 
  return TRUE; 
} 
 
#separate 
void save_audio() 
{ 
  char tag1[4], tag2[4]; 
  long tagtime, tag1time, tag2time; 
  int tagphase; 
 
  if (cf_out || !(disk_valid && file_valid)) return; 
  // Start at beginning of data file 
  move32(sector, data_start); 
  move32(sector_counter, data_file_size); 
   
  tag1time=0xFFFF; 
  tag2time=0xFFFF; 
  tagphase=0;	 
  // Set timer interrupt to 8kHz to sample audio 
  setup_timer_2(T2_DIV_BY_1, 125, 5); // 8 kHz 
  write_pos=0; 
  // Set flags for timer interrupt data sampling 
  audio=TRUE; 
  ad_on=TRUE; 
  // loop until something received thru serial port  
  // and full sector is written 
  while ((!input(RCIF))||(write_pos!=512)) { 
    // if sector ended start a new one	 
    if (write_pos>=512) { 
      write_pos=0; 
    } 
    else if (write_pos==0) { 
      // exit if no space left in data file	 
      if (!next_file_sector_write()) return; 
      // blink 
      SET_TRIS_C(TRIS_C); 
      led_red();	 
      // write sector signature ESL 
      cf_write('E'); 
      cf_write('S'); 
      cf_write('L'); 
      // write time stamp 
      rtc_read_time(); 
      cf_write(time[0]); 
      cf_write(time[1]); 
      cf_write(time[2]); 
      cf_write(time[3]); 
      cf_write(time[4]); 
      cf_write(time[6]); 
      write_pos+=9; 
      led_off(); 
    }  
    // save accelerometer data 3 times in each sector  
    else if ((write_pos==170)||(write_pos==335)||(write_pos==500)) { 
      putI2C(0xB0, 2); 
      I2C_start(); 
      I2C_write(0xB0 | I2CREAD); 
      cf_write(i2c_read()); 
      cf_write(i2c_read()); 
      cf_write(i2c_read()); 
      cf_write(i2c_read()); 
      i2c_read(0); 
      I2C_stop(); 
      write_pos+=4; 
      // write tag reader data once per sector 
      if (write_pos==504) { 
        if ((tagphase&0x1F)==0) {	 
          putI2C(0xA0, 2); 
          I2C_start(); 
          I2C_write(0xA0 | I2CREAD); 
          tag1[0]=i2c_read(); 
          tag1[1]=i2c_read(); 
          tag1[2]=i2c_read(); 
          tag1[3]=i2c_read(); 
	        tagtime=i2c_read(); 
	        tagtime=tagtime<<8|i2c_read(); 
          i2c_read(0); 
          I2C_stop(); 
          if (tagtime>=tag1time) zero32(tag1);  
          tag1time=tagtime; 
          putI2C(0xA0, 3); 
          I2C_start(); 
          I2C_write(0xA0 | I2CREAD); 
          tag2[0]=i2c_read(); 
          tag2[1]=i2c_read(); 
          tag2[2]=i2c_read(); 
          tag2[3]=i2c_read(); 
          tagtime=i2c_read(); 
  	      tagtime=tagtime<<8|i2c_read(); 
          i2c_read(0); 
          I2C_stop(); 
          if (tagtime>=tag2time) zero32(tag2);  
          tag2time=tagtime; 
        }   
        tagphase++; 
        cf_write(tag1[0]); 
        cf_write(tag1[1]); 
        cf_write(tag1[2]); 
        cf_write(tag1[3]); 
        cf_write(tag2[0]); 
        cf_write(tag2[1]); 
        cf_write(tag2[2]); 
        cf_write(tag2[3]); 
        write_pos+=8;    	 
      }   
    } else if (write_pos<512) { 
    // save buffered audio samples 
    // sampling happens in timer interrupt 
      while (buf_count==0); 
      cf_write(buffer[buf_head]>>2); 
      write_pos++; 
      disable_interrupts(GLOBAL); 
      buf_head=(buf_head+1)%buf_size; 
      buf_count--; 
      enable_interrupts(GLOBAL); 
    } 
  } 
  // stop sampling, restore 1000Hz timer frequency 
  ad_on=FALSE; 
  audio=FALSE; 
  purge_adc_buffer(); 
  setup_timer_2(T2_DIV_BY_4, 250, 5); // 1mS interval @ 20 MHz 
} 
 
#separate 
void save_data() 
{ 
  if (!disk_valid) return; 
  write_pos=0; 
  move32(sector, data_start); 
  ad_on=TRUE; 
  while ((!input(RCIF))||(write_pos!=512)) { 
    if (write_pos>=512) { 
      write_pos=0; 
      inc32(sector); 
    } 
    if (write_pos==0) { 
      led_red(); 
      cf_start_sector_write(); 
      cf_write('E'); 
      cf_write('S'); 
      rtc_read_time(); 
      cf_write(time[0]); 
      cf_write(time[1]); 
      cf_write(time[2]); 
      cf_write(time[3]); 
      cf_write(time[4]); 
      cf_write(time[6]); 
      write_pos+=8; 
      led_off(); 
    } 
    while (buf_count==0); 
    cf_write(buffer[buf_head]); 
    cf_write(buffer[buf_head]>>8); 
    write_pos+=2; 
    disable_interrupts(GLOBAL); 
    buf_head=(buf_head+1)%buf_size; 
    buf_count--; 
    enable_interrupts(GLOBAL); 
  } 
  ad_on=FALSE; 
  purge_adc_buffer(); 
  inc32(sector); 
  cf_start_sector_write(); 
  for (write_pos=0; write_pos<512; write_pos++) cf_write(0); 
} 
 
#separate 
void send_data() 
{ 
  unsigned int temp; 
  long int id; 
  ad_on=TRUE; 
  while (!input(RCIF)) { 
    //for (temp=0; temp<19; temp++) putc(0xF0); 
    putc(0x5A);	 
    for (temp=0; temp<4; temp++) { 	 
      while (buf_count==0); 
      if (temp==3) 
      { 
        putc(buffer[buf_head]); 
        if (id>0) putc((buffer[buf_head]>>8)|((id-1) << 2)|0xA8); 
        else putc(buffer[buf_head]>>8); 
      } 
      if (temp==2) 
      { 
        if (buffer[buf_head]>768) id=0; else if (buffer[buf_head]<256) id=1; else id=2; 
      } 
      disable_interrupts(GLOBAL); 
      buf_head=(buf_head+1)%buf_size; 
      buf_count--; 
      enable_interrupts(GLOBAL); 
    } 
  } 
  ad_on=FALSE; 
  purge_adc_buffer(); 
} 
 
// Calculates standard 16-bit CRC 
// uses global var crc16 
// Note: crc16 should be set to 0 before each series of CRC calculations 
void crc (unsigned int data) 
{ 
  int i, j; 
  for (i=0; i<8; i++) { 
    j=(data^crc16)&1; 
    crc16>>=1; 
    if (j) crc16^=0xA001; 
    data>>=1; 
  } 
} 
 
// send data and update CRC 
void put_crc(unsigned int data) 
{ 
  putc(data);  
  crc(data); 
} 
 
#separate 
void send_data_bim() 
{ 
  unsigned int temp; 
  unsigned int bits; 
  unsigned long order; 
  unsigned int i; 
  if (!init_file_write()) return; 
  ad_on=TRUE; 
  order=0; 
  output_low(BIM_TX); 
  while (!input(RCIF) && next_file_sector_write()) { 
    led_red(); 
    cf_write('E'); 
    cf_write('S'); 
    cf_write('L'); 
    rtc_read_time(); 
    cf_write(time[0]); 
    cf_write(time[1]); 
    cf_write(time[2]); 
    cf_write(time[3]); 
    cf_write(time[4]); 
    cf_write(time[6]); 
    led_off(); 
    for (i=0; i<100; i++) {	  	 
      for (temp=0; temp<5; temp++) putc(0x0F<<(temp%5)); //total packet length 22 
      crc16=0; 
      put_crc(0xA5); 
      put_crc(order);	 
      put_crc(order>>8);	 
      for (temp=0; temp<4; temp++) { 	 
        while (buf_count==0); 
        put_crc(buffer[buf_head]); 
        cf_write(buffer[buf_head]); 
        bits=(bits<<2)|((buffer[buf_head]>>8)&0x03); 
        disable_interrupts(GLOBAL); 
        buf_head=(buf_head+1)%buf_size; 
        buf_count--; 
        enable_interrupts(GLOBAL); 
      } 
      put_crc(bits); 
      cf_write(bits); 
      putc(crc16);      
      putc(crc16>>8); 
      order++; 
    } 
    cf_write('E'); 
    cf_write('N'); 
    cf_write('D'); 
  } 
  output_high(BIM_TX); 
  ad_on=FALSE; 
  purge_adc_buffer(); 
} 
 
#separate 
void send_data_Tim() 
{ 
  unsigned int temp; 
  unsigned int bits; 
  unsigned int i; 
  boolean OldButton, NewButton; 
  unsigned int ButtonCount;  
  if (!init_file_write()) return; 
  ad_on=TRUE; 
  OldButton=Button; 
  while (!input(RCIF) && next_file_sector_write()) { 
  	ButtonCount=0; 
    cf_write('E'); 
    cf_write('S'); 
    cf_write('L'); 
    rtc_read_time(); 
    cf_write(time[0]); 
    cf_write(time[1]); 
    cf_write(time[2]); 
    cf_write(time[3]); 
    cf_write(time[4]); 
    cf_write(time[6]); 
    for (i=0; i<100; i++) {	  	 
      for (temp=0; temp<4; temp++) { 
      	do { 
      		NewButton=input(BUTTON); 
      	  if (!NewButton && OldButton) ButtonCount++; 
      	  OldButton=NewButton; 	 
        } while (buf_count==0); 
        if (buffer[buf_head]>voltage+100) {  
         if (voltage<559) led_green(); else led_red(); 
        }; 
        cf_write(buffer[buf_head]); 
        bits=(bits<<2)|((buffer[buf_head]>>8)&0x03); 
        disable_interrupts(GLOBAL); 
        buf_head=(buf_head+1)%buf_size; 
        buf_count--; 
        enable_interrupts(GLOBAL); 
        led_off(); 
      } 
      cf_write(bits); 
    } 
    cf_write(ButtonCount); 
    cf_write(0); 
    cf_write(0); 
  } 
  ad_on=FALSE; 
  purge_adc_buffer(); 
} 
 
#separate 
void send_player_info() 
{ 
  unsigned int temp; 
  unsigned int bits; 
  unsigned long order; 
  unsigned int i; 
  ad_on=TRUE; 
  order=0; 
  output_low(BIM_TX); 
  while (!input(RCIF)) { 
    for (temp=0; temp<5; temp++) putc(0x5A); //total packet length 15 @ 38400; 22 @ 57600 
    crc16=0; 
    put_crc(0xA5); 
    put_crc(order);	 
    put_crc(order>>8);	 
    for (temp=0; temp<4; temp++) { 	 
      while (buf_count==0); 
      put_crc(buffer[buf_head]); 
      bits=(bits<<2)|((buffer[buf_head]>>8)&0x03); 
			//if (temp==1) if (buffer[buf_head]<15) output_low(BIM_TX); else output_high(BIM_TX); 
      disable_interrupts(GLOBAL); 
      buf_head=(buf_head+1)%buf_size; 
      buf_count--; 
      enable_interrupts(GLOBAL); 
    } 
    put_crc(bits); 
    putc(crc16);      
    putc(crc16>>8); 
    order++; 
  } 
  output_high(BIM_TX); 
  ad_on=FALSE; 
  purge_adc_buffer(); 
} 
 
// Print RTC time 
#separate 
void print_time() 
{ 
  rtc_read_time(); 
  printf("%2X/%2X/%2X %2X:%2X:%2X", time[4], time[3], time[6], time[2], time[1], time[0]); 
} 
 
// Enable RTC oscillator and counter 
#separate 
void reset_time() 
{ 
  printf("Time reset"); 
  rtc_write(7,0); 
  rtc_write(0,0); 
} 
 
// Print CF information 
#separate 
void check_cf() 
{ 
  if (!enable_cf()) { 
    printf("Invalid disk\n"); 
  } else { 
    printf("Dir 0x%2X%2X%2X%2X\n", dir_start[3], dir_start[2], dir_start[1], dir_start[0]); 
    printf("Data sectors 0x%2X%2X%2X%2X\n", data_sectors[3], data_sectors[2], data_sectors[1], data_sectors[0]); 
    if (file_valid) { 
      printf("File start 0x%2X%2X%2X%2X\n", data_start[3], data_start[2], data_start[1], data_start[0]); 
      printf("File size 0x%2X%2X%2X%2X\n", data_file_size[3], data_file_size[2], data_file_size[1], data_file_size[0]); 
    } else printf("File not found"); 
  }   
} 
 
// Set RTC time to 8 bytes received thru serial port 
#separate 
void set_time() 
{ 
  int i; 
  for (i=0; i<8; i++) time[i]=loadc(); 
  rtc_write(7,0); 
  rtc_set_time(); 
} 
 
// Well... 
void print_help() 
{ 
  return; 
} 
 
/* 
| int fromHex(char hex) 
| Preconditions: hex is 1-f 
| Postconditions: returned int is 1-16 
*/ 
int fromHex(char hex) 
{ 
	if(hex >= '0' && hex <= '9') 
	  return(hex - 48); // 0 --> 0 
	 
	if(hex >= 'a' && hex <= 'f') 
	  return(hex - 87);  // a --> 10 
	   
	if(hex >= 'A' && hex <= 'F') 
	  return(hex - 55); // A --> 10 
 
  // Still here?  Invalid 
	return(20); 
	 
} 
 
// Issues identify drive command (0xEC) and sends result to serial port 
#separate 
void identify_drive() 
{ 
  unsigned int cnt; 
 
  while ((cf_task_file_read(7)&0xC0)!=0x40); 
  cf_task_file_write(6, 0xA0);   // drive 0 
  cf_task_file_write(7, 0xEC);   // identify drive command 
  while ((cf_read()&0x80)==0x80); 
  cf_set_addr(0); 
 
  for(cnt = 0; cnt < 0xFF; cnt++) 
  { 
    putc(cf_read()); 
  } 
 
  for(cnt = 0; cnt < 0xFF; cnt++) 
  { 
    putc(cf_read()); 
  } 
  putc(cf_read()); 
  putc(cf_read()); 
} 
 
// reads sector and sends result to serial port 
#separate 
void read_sector() 
{ 
  unsigned int cnt; 
 
  sector[0]=loadc(); 
  sector[1]=loadc(); 
  sector[2]=loadc(); 
  sector[3]=loadc(); 
  cf_start_sector_read(); 
 
  for(cnt = 0; cnt < 0xFF; cnt++) 
  { 
    putc(cf_read()); 
  } 
 
  for(cnt = 0; cnt < 0xFF; cnt++) 
  { 
    putc(cf_read()); 
  } 
 
  putc(cf_read()); 
  putc(cf_read()); 
} 
 
// CF debugging: 
// read and print CF register 
#separate 
void read_register() { 
  int temp; 
  temp=cf_task_file_read(curAddr); 
  printf("%d: 0x%2X(%u)\n", curAddr, temp, temp); 
} 
 
// CF debugging: 
// write CF register 
#separate 
void write_register() { 
  printf("Writing 0x%2X(%u) to addr %u\n", curData, curData, curAddr); 
  cf_task_file_write(curAddr, curData); 
} 
 
// CF debugging: 
// set data to write to CF register 
#separate 
void set_data() { 
  int temp; 
  printf("Data (hex): "); 
  temp = loadc(); 
  putc(temp); 
  if((temp = fromHex(temp)) > 16) return; 
  curData = (temp << 4); 
  temp = loadc(); 
  putc(temp); 
  if ((temp = fromHex(temp)) > 16) return; 
  curData |= temp; 
  printf("\nData 0x%2X(%u)\n", curData, curData); 
} 
 
// CF debugging: 
// set CF register address 
#separate 
void set_address() { 
  int temp; 
  printf("Addr (0-7): "); 
  temp=loadc(); 
  putc(temp); 
  temp = fromHex(temp); 
  if (temp > 7) return; 
  curAddr = temp; 
  printf("\nAddr %u\n", curAddr); 
} 
 
// Find data file in root directory 
// If found returns TRUE and first cluster in global var cluster	 
#separate 
boolean find_data_file_name() { 
  int i, j, k, l; 
  boolean match; 
  move32(sector, dir_start); 
  for (i=0; i>4; i++) { 
    cf_start_sector_read(); 
    for (j=0; j<16; j++) { 
      match=true; 
      for (k=0; k<11; k++) if (cf_read()!=fname[k]) match=false; 
      cf_read_skip(26-11); 
      cluster=cf_read()|((long)cf_read()<<8); 
      if (match) return true; 
      cf_read_skip(32-28); 
    } 
    inc32(sector); 	 
  } 
  return false;  
} 
 
// Scan FAT to determine the length of first consecutive cluster chain in the data file 
// Returns file chain length (number of clusters) in data_file_size 
// Note: Current implementationonly works with consecutive cluster chains;  
//       if file is fragmented only first piece is used  
#separate 
void scan_data_file_clusters() {  
  char tmp[4]; 
  long ptr; 
  long c; 
  zero32(tmp); 
  move32(sector, fat_start); 
  tmp[0]=cluster>>8; 
  add32(sector, tmp); 
  cf_start_sector_read(); 
  ptr=cluster&0xFF00; 
  while (ptr++>4; 
  add32(data_start, dir_start); 
  if (is_zero32(data_sectors)) {  
  // If data_sectors is 0 we got it in a wrong place 
    cf_read_skip(0x20-0x18); 
    data_sectors[0]=cf_read(); 
    data_sectors[1]=cf_read(); 
    data_sectors[2]=cf_read(); 
    data_sectors[3]=cf_read(); 
    cf_read_skip(0x3A-0x24); 
  } else cf_read_skip(0x3A-0x18); 
  if (cf_read()!=0x36) return FALSE; // file system id must be FAT16 
  sub32(data_sectors, data_start); 
  add32(data_sectors, sector); 
// Enable Microdrive write cache... Send me e-mail for more information   
  enable_microdrive_write_cache(); 
// File is valid if we can find valid file.   
  file_valid=find_data_file();   
  cf_idle(100); 
  if (file_valid) {  
    // convert data file size from clusters to sectors 
    // Note: sectors per cluster must be power of 2	 
    tmp=sectors_per_cluster; 
    while (!(tmp&1)) { 
      shift_left(data_file_size,4,0); 
      tmp>>=1; 
    } 
    // calculate first sector of data file 
    zero32(sector); 
    sector[0]=(cluster-2); 
    sector[1]=(cluster-2)>>8; 
    tmp=sectors_per_cluster; 
    while (!(tmp&1)) { 
      shift_left(sector,4,0); 
      tmp>>=1; 
    } 
    // and now data_start points to first sector of data file 
    add32(data_start, sector); 
    return true; 
  } else { 
    // File not found: let's get idle and depressed	 
    return false; 
  }  
} 
 
// Tests IR tag reader from Borg lab  
#separate 
void tag_test() 
{ 
  char tag[4]; 
  long tagtime; 
  putI2C(0xA0, 2); 
  I2C_start(); 
  I2C_write(0xA0 | I2CREAD); 
  tag[0]=i2c_read(); 
  tag[1]=i2c_read(); 
  tag[2]=i2c_read(); 
  tag[3]=i2c_read(); 
  tagtime=i2c_read(); 
  tagtime=tagtime<<8|i2c_read(); 
  i2c_read(0); 
  I2C_stop(); 
  printf("%2X%2X%2X%2X ", tag[0], tag[1], tag[2], tag[3]);  
  putI2C(0xA0, 3); 
  I2C_start(); 
  I2C_write(0xA0 | I2CREAD); 
  tag[0]=i2c_read(); 
  tag[1]=i2c_read(); 
  tag[2]=i2c_read(); 
  tag[3]=i2c_read(); 
  tagtime=i2c_read(); 
  tagtime=tagtime<<8|i2c_read(); 
  i2c_read(0); 
  I2C_stop(); 
  printf("%2X%2X%2X%2X\n", tag[0], tag[1], tag[2], tag[3]);  
} 
 
// reads ADC channel and sends result 
#separate 
void send_adc_sample(int channel) 
{ 
  long sample; 
  set_adc_channel(channel); 
  sample=read_adc(); 
  putc(sample); 
  putc(sample>>8); 
} 
 
// I2C command processor 
// ib - I2C start 
// ie - I2C stop 
// ir - I2C read with ACK 
// i0 - I2C read without ACK 
// iw - I2C write 
// ip - I2C poll 
#separate 
void access_i2c() 
{ 
  switch (loadc()) { 
    case 'b': i2c_start(); break; 
    case 'e': i2c_stop(); break; 
    case 'r': putc(i2c_read()); break; 
    case '0': putc(i2c_read(0)); break; 
    case 'w': i2c_write(loadc()); break; 
    case 'p': putc(i2c_poll()); break; 
  } 
} 
 
#separate 
void read_mithril_sensor() 
{ 
  byte addr; 
  addr=loadc(); 
  putI2C(addr, loadc()); 
  I2C_start(); 
  I2C_write(addr | I2CREAD); 
  putc(i2c_read()); 
  putc(i2c_read()); 
  putc(i2c_read()); 
  putc(i2c_read(0)); 
  I2C_stop(); 
} 
 
// Command processor 
#separate 
boolean process_command() 
{ 
  if (!input(RCIF)) return FALSE; 
  cmd = loadc(); 
 
  /* Echo the character */ 
  if ((cmd>='0')&&(cmd<='7')) { 
    send_adc_sample(cmd-'0'); 
  } else 
  if (cmd=='i') access_i2c(); else 
  if (cmd=='m') read_mithril_sensor(); 
  else { 
    putc(cmd); 
    puts(""); 
    switch (cmd) { 
      case 0xA5: reprogram(); break; 
      case '+': print_time(); break; 
      case '-': reset_time(); break; 
      case '=': check_cf(); break; 
      case '*': identify_drive(); break; 
      case '$': read_sector(); break; 
      case '@': cf_idle(100); break; 
      case '.': cf_sleep(); break; 
      case ',': cf_standby(); break; 
      case 't': set_time(); break; 
      case '(': putc(rtc_read(loadc())); break; 
      case '!': cf_reset(); break; 
      case 'd': set_data(); break; 
      case 'a': set_address(); break; 
      case 'r': read_register(); break; 
      case 'w': write_register(); break; 
      #ifdef Rochester 
      case '?': tag_test(); break; 
      #endif 
      case '#': 
        #ifdef GAME 
        send_player_info(); 
        #endif 
        #ifdef Rochester 
        save_audio(); 
        #endif 
        #ifdef ESL 
        send_data_bim(); 
        #endif 
        #ifdef Tim 
        send_data_Tim(); 
        #endif 
        break; 
    } 
    print_prompt(); 
  }   
  return TRUE; 
}