 1Content-type: application/x-Unknown; charset=UTF8package nmc.sidekick;

import com.ajile.jem.rawJEM;
import com.ajile.drivers.spi.*;
import java.util.Calendar;
import java.util.Date;
import com.ajile.drivers.irq.InterruptController;
import com.ajile.drivers.irq.InterruptEventListener;
import nmc.sidekick.RtcTime;
import java.util.TimeZone;
import com.ajile.text.DateFormat;


public class Rtc implements InterruptEventListener {

    private static Rtc TheRtc = new Rtc();
    private SpiMaster spi = SpiMaster.getInstance();
    private int erruptsProcessed = 0;
    private int WErruptsProcessed = 0;
    private int DErruptsProcessed = 0;
    public int CTRL_1_REGStartValue =0;
    public int CTRL_2_REGStartValue =0;
    public int YEAR_CTR_REGStartValue=0;
    public int MCCBStartValue=0;
    public int DAYOFMONTH_CTR_REGStartValue=0;
    public int HOUR_CTR_REGStartValue=0;
    public int MINUTE_CTR_REGStartValue=0;
    public int SECOND_CTR_REGStartValue=0;

    public boolean isInitialized=false;
    
    public static Rtc getInstance() {
        // should assert(TheRtc != null)
        if(TheRtc !=null)
          return TheRtc;
        else{
          DebugMessage.println("Rtc.getInstance() no instance -- returning null");
          return null;
        }
    }

    
   public static void initialize() {
      if(TheRtc.isInitialized==false){
         synchronized(TheRtc){
            TheRtc.initRtcCsN();
	TheRtc.readSetCtlRegisters();
	TheRtc.isInitialized=true;
         }
      }
   }

    public void readSetCtlRegisters() {
        // Control Reg 1:
        // D7: WALE:  0: disarm alarm interrupt ckt 1:enables
        // D6: DALE:   0: disarm alarm interrupt ckt 1:enables
        // D5: 12/24:    0: select 12 hr mode w/ am pm indications 1:24 hr mode
        // D4: CLEN2  0: enable 32 khz clock ckt 1: disables (this reg is SCRATCH3 for RxC348B)
        // D3: TEST     0: Normal Operation 1: Test mode
        // D<2:0>: Test waveform settings

        // Control Reg 2:
        // D7: VDSL:          0:Select Voltage Supply Mon Threshhold 2.1V 1: Select 1.6V
        // D6: VDET:          0: Indicate Supply Voltage Above Threshhold 1:Vs is below threshhold
        // D5: SCRATCH1:  0: 1:
        // D4: XSTP            0: Oscillation normal 1:Oscillation halted 
        // D3: !CLEN1         0: Enable 32kHz clock output 1: disables (this reg is SCRATCH2 for RxC348B)
        // D2: CTFG:           0: Clear Periodic interrupt indicator  1: No Action
        // D1: WAFG:         0: Mismatch between current time and preset alarm time 1: match
        // D0: DAFG:          0: Mismatch between current time and preset alarm time 1: match

        int c1 = get(CTRL_1_REG);
        
        if(isInitialized==false)
            CTRL_1_REGStartValue=c1;    
              
        c1 &= 0xc0; // 12HR Mode set, 32 kHz clk EN, set NORMAL mode, don't touch WALE,DALE 
        c1 |= 0x20; // 24HR Mode Set
        set(CTRL_1_REG, c1);
        int c2 = get(CTRL_2_REG);
        
        if(isInitialized==false)
            CTRL_2_REGStartValue=c2;

        set(CTRL_2_REG, 0x20);
        
        c1 = get(CTRL_1_REG);
        c2 = get(CTRL_2_REG);
        if(isInitialized==false) {
            YEAR_CTR_REGStartValue=get(YEAR_CTR_REG);
            MCCBStartValue=get(MCCB_REG);
            DAYOFMONTH_CTR_REGStartValue=get(DAYOFMONTH_CTR_REG);
            HOUR_CTR_REGStartValue=get(HOUR_CTR_REG);
            MINUTE_CTR_REGStartValue=get(MINUTE_CTR_REG);
            SECOND_CTR_REGStartValue=get(SECOND_CTR_REG);  
        }
        printStartupValues();
    }

   public void printStartupValues(){
     DebugMessage.println("CTRL_1_REG "+ Integer.toHexString(CTRL_1_REGStartValue));
     DebugMessage.println("CTRL_2_REG "+ Integer.toHexString(CTRL_2_REGStartValue));
     DebugMessage.println("YEAR_CTR_REG "+ Integer.toHexString(YEAR_CTR_REG));
     DebugMessage.println("MCCB_REG "+ Integer.toHexString(MCCB_REG));
     DebugMessage.println("DAYOFMONTH_CTR_REG "+ Integer.toHexString(DAYOFMONTH_CTR_REG));
     DebugMessage.println("HOUR_CTR_REG "+ Integer.toHexString(HOUR_CTR_REG));
     DebugMessage.println("MINUTE_CTR_REG "+ Integer.toHexString(MINUTE_CTR_REG));
     DebugMessage.println("SECOND_CTR_REG "+ Integer.toHexString(SECOND_CTR_REG));
   }
     
    public void runTest() {
       DebugMessage.println("reading control registers");
// Duplicated in readSetCtlRegisters() for external access
        int c1 = get(CTRL_1_REG);
        c1 &= 0xc0;
        set(CTRL_1_REG, c1);
        int c2 = get(CTRL_2_REG);
        DebugMessage.println("ctrl1=0x" + Integer.toHexString(c1)+ "; ctrl2=0x" + Integer.toHexString(c2)+ ";");
        c2 &= ~3;
        set(CTRL_2_REG, c2);

        c1 |= 0x20;
        set(CTRL_1_REG, c1);
        c1 = get(CTRL_1_REG);
        DebugMessage.println("ctrl1=0x" + Integer.toHexString(c1)+ "; ctrl2=0x" + Integer.toHexString(c2)+ ";");


        RtcTime torig = getTime();
        DebugMessage.println("Before setting, the time (by the rtc) is: "
                           + torig);
        setTime(2002, Calendar.MAY, 31, 23, 59, 48);
        int r = 0;
        installInterruptHandler();
        enableInterrupts();
        setAlarmD(18, 58);
        while (true) {

            RtcTime t = getTime();
            DebugMessage.println("The current time (by the rtc) is: "
                               + t);

            DebugMessage.println("interrupts processed = " + erruptsProcessed);
            if (erruptsProcessed != r) {
                DebugMessage.println("got a new interrupt");
                r = erruptsProcessed;
                c2 = get(CTRL_2_REG);
                if ((c2 & 3) != 0) {
                    set(CTRL_2_REG, (c2 & ~3));
                }
                DebugMessage.println("ctrl reg 2 = 0x" 
                                   + Integer.toHexString(c2));
                if (++t.minute >= 60) {
                    t.minute = 0;
                    if (++t.hour == 24) {
                        t.hour = 0;
                    }
                }
                    
                setAlarmD(t.hour, t.minute);
                reenableInterrupts();
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                DebugMessage.println("got InterruptedException: " + e);
            }
        }
    }

    public void installInterruptHandler() {
        InterruptController.
            addInterruptListener(this, 
                                 InterruptController.GPIOA_BIT5_INTERRUPT);
    }

    public void enableInterrupts() {
        InterruptController.
            enableInterrupt(InterruptController.GPIOA_BIT5_INTERRUPT);
    }

    public void reenableInterrupts() {
        bp3(0x21);
        rawJEM.set(AjileAddr.GPIOA_CLEAR_INT, (1<<5)); // GPIOA5
        rawJEM.setField(1, 0x0900201c, 5, 1);
        bp3(0x31);
    }

    public void interruptEvent() {
        // this is called during the 'rupt, from the interrupt controller
        bp2(0x11);
        erruptsProcessed++;
        rawJEM.setField(0, 0x0900201c, 5, 1);
    }

    public int bp2(int i) {
        return i++;
    }

    public int bp3(int i) {
        return i++;
    }

    int toBcd(int b) {
        int b0 = b % 10;
        int b1 = b / 10;
        return (b1<<4) | b0;
    }
    int fromBcd(int bcd) {
        return (bcd & 0xf + (bcd>>4)*10);
    }
    public void setAlarmD(int hour, int min) {
        int c1 = get(CTRL_1_REG);
        c1 &= ~DALE;
        set(CTRL_1_REG, c1);

        set(ALARM_D_HOUR_REG, toBcd(hour));
        set(ALARM_D_MIN_REG, toBcd(min));

        int c2 = get(CTRL_2_REG);
        c2 &= ~DAFG;
        set(CTRL_2_REG, c2);
        c1 |= DALE;
        set(CTRL_1_REG, c1);
    }

           
    public RtcTime getTime() {
        RtcTime t = new RtcTime();
        int mccb = get(MCCB_REG);
        int y = get(YEAR_CTR_REG);
        DebugMessage.println("Rtc.getTime() YEAR_CTR_REG = 0x"+Integer.toHexString(y));

        t.year = (y & 0xf) + (y >> 4)*10;
        if ((mccb & 0x80) == 0)
            t.year += 1900;
        else
            t.year += 2000;
        //DebugMessage.println("Rtc.getTime()");
        DebugMessage.println("Rtc.getTime(), mccb = 0x" + Integer.toHexString(mccb));
        t.month = (0xf & mccb) + ((0x70 & mccb) >> 4)*10;
        
        int d = get(DAYOFMONTH_CTR_REG);
        t.day = (0xf & d) + (d >> 4)*10;
        

        //          t.hour = get(HOUR_CTR_REG);
        //          if ((t.hour & 0x20) != 0) {
        //              t.hour &= ~0x20;
        //          } else if (t.hour == 12) {
        //              t.hour = 0;
        //          }

        int h = get(HOUR_CTR_REG);
        DebugMessage.println("Rtc.getTime() HOUR_CTR_REG = 0x"+Integer.toHexString(h));
        t.hour = (h & 0xf) + (h>>4)*10;
        

        int min = get(MINUTE_CTR_REG);
        DebugMessage.println("Rtc.getTime() MINUTE_CTR_REG = 0x"+Integer.toHexString(min));
        t.minute = (0xf & min) + (min>>4)*10;
        int sec = get(SECOND_CTR_REG);
        DebugMessage.println("Rtc.getTime() SECOND_CTR_REG = 0x"+Integer.toHexString(sec));
        t.second = (0xf & sec) + (sec>>4)*10;
        
        // Check for invalid time/date
        boolean isValid=true;
        if( t.month<1 || t.month>12) 
            isValid=false;
        if( t.day<1) 
            isValid=false;
        if( t.day>28 &&  t.month==2) 
            isValid=false;
        if( t.day>30 &&  (t.month==4 || t.month==6 || t.month==9 || t.month==11)) 
            isValid=false;
        if( t.day>31 &&  (t.month==1 || t.month==3 || t.month==5 || t.month==7 || t.month==8 || t.month==10 || t.month==12)) 
            isValid=false;
        if( t.hour<0 || t.hour>24) 
            isValid=false;
        if( t.minute<0 || t.month>59) 
            isValid=false;
        if( t.second<0 || t.second>59) 
            isValid=false;
        if(isValid==false){
            DebugMessage.println("Rtc.getTime() - Invalid RTC time -- resetting RTC to Epoch Time");
            t.year=1970;
            t.month=1;
            t.day=1;
            t.hour=t.minute=t.second=0;
            setTime(t);
        }  
        DebugMessage.println("Rtc.getTime() "+t.year+" "+t.month+" "+t.day+" "+t.hour+" "+t.minute+" "+t.second);
  
        return t;
    }

        
    public void setTime(long ms) {
        RtcTime t = toRtcTime(ms);        
        DebugMessage.println("Rtc.setTime(long) ms "+ms+" RtcTime "+toString(t));
        setTime(t);
    }

    public void setTime(RtcTime t) {
        DebugMessage.println("Rtc.setTime(RtcTime) "+toString(t));
        setTime(t.year, t.month, t.day, t.hour, t.minute, t.second);
    }

    public void setTime(int year, int month, int day, int hour, int minute, int second) {
        int centuryBit;
        int y, yy0, yy1;
        if (year >= 2000) {
            centuryBit = 0x80;
            y = year - 2000;
            yy0 = y % 10;
            yy1 = y / 10;
            // if (yy1 > 9)
            //     throw new IllegalArgumentException("cannot support years beyond 2099");
        } else {
            centuryBit = 0;
            // if (year < 1900)
            //     throw new IllegalArgumentException("cannot support years before 1900");
            y = year - 1900;
            yy0 = y % 10;
            yy1 = y / 10;
        }
        set(YEAR_CTR_REG, (yy1<<4 | yy0));
        DebugMessage.println("Rtc.setTime() YEAR_CTR_REG = 0x"+Integer.toHexString((yy1<<4 | yy0)));
        int mm0, mm1;
        //month += 1; // In java JANUARY==0, in the rtc, january == 1
        // yes, but we'll always make it right at a higher level
        if (month > 9) {
            mm1 = 1;
            mm0 = month - 10;
        } else {
            mm1 = 0;
            mm0 = month;
        }
        set(MCCB_REG, centuryBit | (mm1 << 4) | mm0);
        DebugMessage.println("Rtc.setTime() MCCB_REG = 0x"+Integer.toHexString((centuryBit | (mm1 << 4) | mm0)));
        int dd0 = day % 10;
        int dd1 = day / 10;
        set(DAYOFMONTH_CTR_REG, dd1<<4 | dd0);
        DebugMessage.println("Rtc.setTime() DAYOFMONTH_CTR_REG = 0x"+Integer.toHexString((dd1<<4 | dd0)));
//          int pm;
//          switch (hour) {
//          case 0:
//              hour = 12;
//              pm = 0;
//              break;
//          case  1:
//          case  2:
//          case  3:
//          case  4:
//          case  5:
//          case  6:
//          case  7:
//          case  8:
//          case  9:
//          case 10:
//          case 11:
//              pm = 0;
//              break;
//          case 12:
//              pm = 0x20;
//              break;
//          case 13:
//          case 14:
//          case 15:
//          case 16:
//          case 17:
//          case 18:
//          case 19:
//          case 20:
//          case 21:
//          case 22:
//          case 23:
//              pm = 0x20;
//              hour -= 12;
//              break;
//          default:
//              throw new IllegalArgumentException("hour (" 
//                                                 + hour 
//                                                 + ") is negative or too high");
//              // break;  // compiler complains that break is unreachable code
//          }
//          int hh0, hh1;
//          hh0 = hour % 10;
//          hh1 = (hour > 9) ? 1 : 0;
//          set(HOUR_CTR_REG, pm | (hh1<<4) | hh0);

        int hh0, hh1;
        hh0 = hour % 10;
        hh1 = hour / 10;
        //DebugMessage.println("Rtc.setTime(i,i,i,i,i,i) "+hour+" "+hh0+" "+hh1+" "+((hh1<<4) | hh0));
        set(HOUR_CTR_REG, (hh1<<4) | hh0);
        DebugMessage.println("Rtc.setTime() HOUR_CTR_REG = 0x"+Integer.toHexString(((hh1<<4) | hh0)));
        int min0, min1;
        min0 = minute % 10;
        min1 = minute / 10;
        set(MINUTE_CTR_REG, (min1<<4) | min0);
        DebugMessage.println("Rtc.setTime() MINUTE_CTR_REG = 0x"+Integer.toHexString((((min1<<4) | min0))));
        int ss0, ss1;
        ss0 = second % 10;
        ss1 = second / 10;
        set(SECOND_CTR_REG, (ss1<<4) | ss0);
        DebugMessage.println("Rtc.setTime() SECOND_CTR_REG = 0x"+Integer.toHexString((((ss1<<4) | ss0))));
    }


    // duplicates other code in full sidekick
    public void initSpiForRtcTest() {
        spi.setClkDivider(spi.SPI_CLOCK_DIVIDER_64);
        spi.setSlaveSelect(spi.SPI_SLAVE_SELECT_NONE);
    }

    // The following is NOT THREAD-SAFE
    // If anything else is doing a read-modify-write of regE at the same time,
    // Heisenbugs are likely to appear
    //
    public void initRtcCsN() {
        // set the value of AdcCsN to 1 (chip select not asserted)
        bp();
        rawJEM.setField(0, AjileAddr.GPIOC_OR, 7, 1); // setField() may make this thread-safe??

        bp();

        rawJEM.setField(1, AjileAddr.GPIOC_OER, 7, 1);

        bp();

    }

    private void assertCs() {
        rawJEM.setField(1, AjileAddr.GPIOC_OR, 7, 1);
    }

    public void negateCs() {
        rawJEM.setField(0, AjileAddr.GPIOC_OR, 7, 1);
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            DebugMessage.println("InterruptedException while sleeping in Rtc.negateCS()");
        }
    }

    public void bp() {
        DebugMessage.println("Rtc.bp() set breakpoint here");
    }

    static final int SECOND_CTR_REG        = 0x00;
    static final int MINUTE_CTR_REG        = 0x10;
    static final int HOUR_CTR_REG          = 0x20;
    static final int DAYOFWEEK_CTR_REG     = 0x30;
    static final int DAYOFMONTH_CTR_REG    = 0x40;
    static final int MCCB_REG              = 0x50;
    static final int YEAR_CTR_REG          = 0x60;
    static final int OSC_ADJ_REG           = 0x70;
    static final int ALARM_W_MIN_REG       = 0x80;
    static final int ALARM_W_HOUR_REG      = 0x90;
    static final int ALARM_W_DAYOFWEEK_REG = 0xa0;
    static final int ALARM_D_MIN_REG       = 0xb0;
    static final int ALARM_D_HOUR_REG      = 0xc0;
    static final int CTRL_1_REG            = 0xe0;
    static final int CTRL_2_REG            = 0xf0;

    static final int READ   = 0xc;
    static final int WRITE  = 0x8;

    static final int WALE = 0x80; // C1
    static final int DALE = 0x40; // C1
    static final int WAFG = 0x02; // C2
    static final int DAFG = 0x01; // C2

    public void set (int reg, int arg) {
        int cmd = reg | WRITE;
        DebugMessage.println("SPI WRITE 0x" 
                           + Integer.toHexString(reg)
                           + " "
                           + Integer.toHexString(arg));
        synchronized (spi) {
            assertCs();
            rawJEM.setField(1, 0x09001804, 4, 1);
            spi.writeReadData(cmd); // with AdcCsN high
            spi.writeReadData(arg);
            negateCs();
            rawJEM.setField(0, 0x09001804, 4, 1);
        }
    }

    public int get(int reg) {
        int cmd = reg | READ;
        int retval;
        synchronized (spi) {
            rawJEM.setField(1, 0x09001804, 4, 1);
            assertCs();
            spi.writeReadData(cmd); // with AdcCsN high
            retval = spi.writeReadData(0);
            negateCs();
            rawJEM.setField(0, 0x09001804, 4, 1);
        }
        return retval;
    }

    public long currentTimeMillis(){
      if(isInitialized==false){
        initialize();
      }
       RtcTime t=getTime();
       return toMillis(t);
    }
       
    public static synchronized String toString(long msTime){
         DebugMessage.println("Rtc.toString(long) converting "+msTime);
         Date d=new Date(msTime);
         return(toString(d));    
    }
    
    public static String toString(Date d){
      Calendar c=Calendar.getInstance();
      c.setTime(d);    
      return ((c.get(Calendar.YEAR)+"/"+(c.get(Calendar.MONTH)+1)+"/"+c.get(Calendar.DAY_OF_MONTH)+" "+c.get(Calendar.HOUR_OF_DAY)+":"+c.get(Calendar.MINUTE)+":"+c.get(Calendar.SECOND)));
    } 
    public static String toString(RtcTime t){
      return (t.toString());
    }     
      
     public static synchronized RtcTime toRtcTime(long ms){
         Calendar c = Calendar.getInstance();
         c.setTime(new Date((long)ms));
         //c.setTimeZone(TimeZone.getTimeZone("UTC"));
         RtcTime r = new RtcTime();
         r.year      = c.get(Calendar.YEAR);
         r.month   = c.get(Calendar.MONTH)+1;
         r.day       = c.get(Calendar.DAY_OF_MONTH);
         r.hour      = c.get(Calendar.HOUR_OF_DAY);
         r.minute  = c.get(Calendar.MINUTE);
         r.second = c.get(Calendar.SECOND);
         DebugMessage.println("Rtc.toRtcTime(long) "+ms+"->"+toString(r));
         return r;
     }
    
     public synchronized long toMillis(RtcTime rt){

    	Calendar cal = Calendar.getInstance();
    	Date d;
    	cal.set(Calendar.YEAR,rt.year);
    	cal.set(Calendar.MONTH,rt.month-1);
    	cal.set(Calendar.DAY_OF_MONTH,rt.day);
    	cal.set(Calendar.HOUR_OF_DAY,rt.hour);
    	cal.set(Calendar.MINUTE,rt.minute);
    	cal.set(Calendar.SECOND,rt.second);
    	d = cal.getTime();
    	long millis = d.getTime();
            DebugMessage.println("Rtc.toMillis(RtcTime) "+toString(rt)+"->"+millis);

    	return millis;
    }
}
