Olimex MOD-RTC interface exampe

Download pre-release library modules and new examples to use with Astrobe for LPC2000. Forum members can also upload their own source code examples.
Post Reply
4GlCoder
Posts: 27
Joined: Fri Jul 22, 2011 2:47 pm

Olimex MOD-RTC interface exampe

Post by 4GlCoder » Sat Oct 27, 2012 12:21 pm

While most NXP processors do have a RTC on-board, I think you can never have enough examples of interfacing.

This examples shows interfacing with Olimex UEXT plugin module, in this case combined with LPC2378-STK board. The clock using I2C interface.

After some study of MOD-RTC schematic I have created the following MODULE:

Code: Select all

MODULE UEXTMODRTC;
(* FOR ARM LPC2378-STK Board interfacing with UEXT connector, the following UEXT pins are used:
     NAME  = GPIO  = DIR  NAME ON MOD-RTC PIN-PU/PD
   1 +3V3                 +3V3
   2 GND                  GND
   3 TXD2  = P0.10 = IN   CLKOUT          PCF8563 clock output. (* 4.7k PU on LPC2378 *)
   4 RXD2  = P0.11 = IN   #INT            PCF8563 interrupt output, active LOW. (* 4.7k PU on LPC2378 *)
   5 SCL0  = P0.28 = I/O  SCL             I2C; 4.7k PU on LPC2378-STK because Open Drain Output
   6 SDA0  = P0.27 = I/O  SDA             I2C; 4.7k PU on LPC2378-STK because Open Drain Output
   7 MISO1 = P0.8  =                      not used
   8 MOSI1 = P0.9  =                      not used
   9 SCK1  = P0.7  =                      not used
  10 SSEL1 = P0.6  =                      not used
*)

IMPORT SYSTEM, LPC := LPC2378, I2C;

CONST
  (* All pins are of Port 0 *)
  CLKPin* = 10;          (* UEXT Pin-3, is P0.10, Configured as Input *)
  INTPin* = 11;          (* UEXT Pin-4, is P0.11, Configured as Input *)

PROCEDURE InitRTC*;
VAR
  bits : SET;
BEGIN
  (* Configure for GPIO /FIO *)
  SYSTEM.GET(LPC.SCS, bits);
  IF ~(0 IN bits) THEN
    SYSTEM.PUT(LPC.SCS, bits + {0})  (* enables GPIO on ports 0 and 1 *)
  END;
  (* setting pins to function as GPIO 
      P0.10 = bits 21:20 = 0:0, P0.11 = bits 23:22 = 0:0
     *)
  SYSTEM.GET(LPC.PINSEL0, bits);
  SYSTEM.PUT(LPC.PINSEL0, bits - {20..23}); (* configure P0.10, p0.11 for GPIO *)
  (* setting pin pu/pd resistors none needed, because wired on board(s) 
      P0.10 = bits 21:20 = 1:0, P0.11 = bits 23:22 = 1:0 *)
  SYSTEM.GET(LPC.PINMODE0, bits);
  SYSTEM.PUT(LPC.PINMODE0, bits - {20,22} + {21,23});
  (* setting direction of the pins P0.10, P0.11 to input *)
  SYSTEM.GET(LPC.FIO0DIR, bits);
  SYSTEM.PUT(LPC.FIO0DIR, bits - {CLKPin, INTPin});
END InitRTC;

PROCEDURE InitI2C*(bus: INTEGER);
(* Ref: LPC2xxx User Manual *)
CONST
  freq = 100000;
VAR
  select: SET;
BEGIN
  (* Configure for GPIO /FIO *)
  SYSTEM.GET(LPC.SCS, select);
  IF ~(0 IN select) THEN
    SYSTEM.PUT(LPC.SCS, select + {0})  (* enables GPIO on ports 0 and 1 *)
  END;
  IF bus = 0 THEN 
    (* LPC2378 I2C0 Deviation: uses different pins *)
	  (* Connect I2C0 signals 
       P0.27 to SDA0 = 23:22 = 0:1
       P0.28 to SCL0 = 25:24 = 0:1 *)
    SYSTEM.GET(LPC.PINSEL1, select);
    SYSTEM.PUT(LPC.PINSEL1, select - {23, 25} + {22, 24})
  ELSE
    (* LPC2103 I2C1 *)
	  (* Connect I2C1 signals (SCL1 & SDA1) to P0.17 and P0.18 *)
    SYSTEM.GET(LPC.PINSEL1, select);
    SYSTEM.PUT(LPC.PINSEL1, select - {3, 5} + {2, 4})
  END;
  I2C.Init(bus, freq)
END InitI2C;

END UEXTMODRTC.
Then I created a sample program to test this:

Code: Select all

MODULE MODRTC;
(* =========================================================================  
   Example ARM Oberon I2C Real Time Clock Interface Program  

   Description:
     Set and Reads the MOD-RTC Real-time Clock/calender and demonstrates 
     use of I2C / UEXT interface on LPC2378-STK 
   
   Target: 
     LPCxx systems with a MOD-RTC module connected to a Olimex UEXT connector
     connected to the I2C0 or I2C1 bus

   Tested with:
     Olimex LPC-2378-STK

     (I2CBUS = 0)
     Embedded Artists LPC2103 Education Board 
     Embedded Artists LPC2148 Education Board v3

     (I2CBUS = 1)
     Coridium Corp LPC2103 ARMmite
     Coridium Corp LPC2103 ARMmite PRO
     
   Refs: 
     NXP LPC2103 User Manual UM10161
     NXP PCF8563 Real-time clock/calendar Datasheet 
     Oberon for LPC2000 Microcontrollers
      
   ========================================================================= *)

IMPORT LPC, I2C, SYSTEM, Main, Out, Timer, UEXT := UEXTMODRTC;

CONST
  (* Set I2CBUS to 1 for ARMmite and ARMmite PRO *)
  (* Set I2CBUS to 0 for all other boards *)
  I2CBUS = 0;  (* 0 is OK for LPC2378, but pins used on LPC2378-STK differ, See Init *)

  (* This results in use of address A2 (write) and A3 (read) *)
  RTCAddr = 051H;
  
  (* Registers of RTC *)
  Status1 = 0;
  Status2 = 1;
  Seconds = 2;  (* Msb contains Voltage Low Flag *)
  Minutes = 3;
  Hours = 4;
  Days = 5;
  Weekdays = 6;
  Months = 7;    (* Msb contains Century Flag *)
  Years = 8;
  MinuteAlarm = 9;
  HourAlarm=10;
  DayAlarm=11;
  WeekdayAlarm=12;
  ClkOutControl=13;
  TimerControl=14;
  Timero=15;
 
(* Converts a number to the BCD format tttt|uuuu (tens and units)*)
PROCEDURE CharToBCD(a : SYSTEM.BYTE) : SYSTEM.BYTE;
BEGIN
  RETURN CHR(ORD(a) MOD 10 + (ORD(a) DIV 10 * 16))
END CharToBCD;

(* Converts a BCD coded register back to a binary number *)
PROCEDURE BCDToChar(a : SYSTEM.BYTE; mask : SET) : SYSTEM.BYTE;
VAR
  sub : INTEGER;
BEGIN
  sub := 10 * ORD(SYSTEM.VAL(SET, ORD(a) DIV 16) * mask);
  RETURN CHR(ORD(a) MOD 16 + sub)
END BCDToChar;

PROCEDURE Progress(msg: ARRAY OF CHAR; status: INTEGER);
BEGIN
  Out.String(msg); 
  IF status = I2C.OK THEN 
    Out.String(" OK")
  ELSE 
    Out.String(" Error ");
    Out.Int(status, 0)
  END;
  Out.Ln
END Progress;

PROCEDURE Dow(d : CHAR; VAR daystr : ARRAY OF CHAR);
BEGIN
  IF ORD(d) > 6 THEN 
    daystr := ""
  ELSE
    CASE d OF
     0X : daystr := "Sunday"
    |1X : daystr := "Monday"
    |2X : daystr := "Tuesday"
    |3X : daystr := "Wednesday"
    |4X : daystr := "Thursday"
    |5X : daystr := "Friday"
    |6X : daystr := "Saturday"
    END
  END
END Dow;

PROCEDURE OutTimeDate(sec, min, hr, day, wd, month, year : SYSTEM.BYTE);
VAR
  days : ARRAY 12 OF CHAR;
BEGIN
  Out.String("Time: ");
  Out.Int(ORD(hr), 2); Out.Char(":"); Out.Int(ORD(min), 2); Out.Char(":"); Out.Int(ORD(sec), 2);  Out.Ln;
  Out.String("Date: ");
  Out.Int(ORD(day), 2); Out.Char("-"); Out.Int(ORD(month), 2); Out.Char("-"); Out.Int(ORD(year), 2); Out.Ln;
  Out.String("Day of the week: ");
  Dow(SYSTEM.VAL(CHAR, wd), days);
  Out.String(days); Out.Ln
END OutTimeDate;

(* Sets the date and time to the specified values
   if the value is outside the allowed range
   it will be set to zero
   returns TRUE if successful, 0 if there was an ERROR while writing
   ranges: sec(0-59), min (0-59), hr(0-23), day(0-31), weekday(0-6,Sun=0)
           month(1-12), year(0-99)
 *)           
PROCEDURE SetTimeDate(sec, min, hr, day, wd, month, year : SYSTEM.BYTE) : BOOLEAN;
VAR
  result : BOOLEAN;
  status : INTEGER;
  ap : ARRAY 1 OF SYSTEM.BYTE; (* array of parameters to pass, don't depend on LEN! *)
  ca : ARRAY 7 OF SYSTEM.BYTE; (* clock values to set *)
BEGIN
  IF ORD(sec) > 59 THEN sec := 0X END;
  IF ORD(min) > 59 THEN min := 0X END;
  IF ORD(hr) > 23 THEN hr := 0X END;
  IF ORD(wd) > 6 THEN wd := 0X END;
  IF (ORD(month) = 0) OR (ORD(month) > 12) THEN month := 1X END;
  IF ORD(year) > 99 THEN year := 0X END;
  
  ca[0] := CharToBCD(sec); 
  ca[1] := CharToBCD(min);
  ca[2] := CharToBCD(hr); 
  ca[3] := CharToBCD(day);
  ca[4] := CharToBCD(wd); 
  ca[5] := CharToBCD(month);
  ca[6] := CharToBCD(year);
  ap[0] := 02X;   (* Set the register to start read wiaddress pointer to 2 *)
  Timer.MSecDelay(20);
  (* DON'T use I2C.Write *)
  status := I2C.WriteBytes(RTCAddr, ap, 1, ca, 0, 7);
  Progress("MODRTC.SetTimeDate=", status);
  RETURN status = I2C.OK
END SetTimeDate;

(* Gets the current time (in decimal format)
   returns TRUE if successful, FALSE if there was an ERROR while reading
 *)

PROCEDURE GetTimeDate(VAR sec, min, hr, day, wd, month, year : SYSTEM.BYTE) : BOOLEAN;
VAR
  status : INTEGER;
  ap : ARRAY 1 OF CHAR; (* Array of parameters to pass *)
  ca : ARRAY 7 OF CHAR; (* clock array that receives data *)
BEGIN
  Timer.MSecDelay(20);
  ap[0] := 02X; (* Address to start reading = RTC register#2 *)
  (* DON'T use I2C.Read !! *)
  status := I2C.ReadBytes(RTCAddr, ap, 1, ca, 0, 7);  (* Reads 7 bytes starting from RTC register# 2 *)
  Progress("MIDRTC.GetTimeDate=", status);
  IF status = I2C.OK THEN
    sec := BCDToChar(ca[0], {0,1,2});
    min := BCDToChar(ca[1], {0,1,2});
    hr  := BCDToChar(ca[2], {0,1});
    day := BCDToChar(ca[3], {0,1});
    wd  := CHR(ORD(ca[4]) MOD 8);
    month := BCDToChar(ca[5], {0});
    year := BCDToChar(ca[6], {0..3})
  END;
  RETURN status = I2C.OK
END GetTimeDate;

PROCEDURE dummy(aoc : ARRAY OF CHAR);
VAR
  i : INTEGER;
BEGIN
  i := LEN(aoc);
  Out.String("LEN-Length of array = ");
  Out.Int(i, 2);
  Out.String(", Is this what you expected?");
  Out.Ln;
END dummy;

PROCEDURE Run;
VAR
  test1 : ARRAY 1 OF CHAR; (* allocates 4 bytes *)
  sec, min, hr, day, wd, month, year : SYSTEM.BYTE;
  status : INTEGER;
BEGIN
  dummy(test1);  (* demonstrates compiler rounding on length of a 1 byte array
                    which makes use of LEN USELESS on CHAR/BYTE array  *)
  status := I2C.OK;
  hr := CHR(11); min := CHR(15); sec := CHR(45);               (* 11:15:45 = hh:mm:ss *)
  day := CHR(29); wd := 6X; month := CHR(9); year := CHR(12);  (* 2012, september 29, saturday = day no.6 *)
  IF SetTimeDate(sec, min, hr, day, wd, month, year) THEN
    REPEAT
      IF GetTimeDate(sec, min, hr, day, wd, month, year) THEN
        OutTimeDate(sec, min, hr, day, wd, month, year)
      END;
      Timer.MSecDelay(30000)  (* Wait 30 seconds *)
    UNTIL FALSE
  ELSE
    Out.String("Could not set clock")
  END
END Run;

BEGIN
  UEXT.InitRTC;
  UEXT.InitI2C(I2CBUS);
  Run
END MODRTC.
Note that this example contains:
- Conversion To/From Values to BCD and back
- Interfacing with UEXT module
- Interfacing with I2C
- A reminder that rounding array lengths can cause problems in Interfacing
- Adding functionality to a board without soldering

Have Fun!0

Post Reply