Had a go at creating a simple widget that displays pages related to the current page in the sidebar. WordPress has some pretty good documentation and there are plenty of examples around, so it was pretty simple:-

<?php
/*
Plugin Name: Show Related Pages
Description: Menu widget to show child pages of the current page.
Version: 0.1
Author: Marc Symonds
Date: July 2014
*/

class mss_showchildpageswidget extends WP_Widget
{
  // Constructor.
  
  function __construct()
  {
    parent::__construct(
        'mss_showchildpageswidget',
        'Show Child Pages',
        array('description' => 'Show child pages related to the current page')
      );
  }
  
  // Output front-end markup.
  
  public function widget($args, $instance)
  {
    global $post;

    // Only output something if viewing a page.
    
    if (is_singular() && $post->post_type == 'page')
    {
      // Get the ancestor pages for the current page.
      
      $elders = get_post_ancestors($post->ID);
      $ecount = count($elders);

      // The list we are going to build will contain the two most recent ancestors (parent and grandparent),
      // the current page with two levels of descendants (child and grandchild) and the siblings of the current
      // page with one level of descendant (child):-
      //
      // Grandparent
      //   Parent
      //     This page
      //       Child1
      //       Child2
      //         Grandchild1
      //     Sibling1
      //     Sibling2
      //       Child1
      // 

      $t = get_the_title($post->ID);
      $l = get_permalink($post->ID);

      $before_child = '<ul class="children"><li><a href="' . $l . '"><b>' . $t . '</b></a>';
      $after_child = '</li></ul>';

      $i = 2;
      $j = 0;
      while ($i > 0 && $j < $ecount)
      {
        $p = $elders[$j];
        $t = get_the_title($p);
        $l = get_permalink($p);
        
        $before_child = '<ul class="children"><li><a href="' . $l . '">' . $t . '</a>' . $before_child;
        $after_child = '</li></ul>' . $after_child;
        $i--;
        $j++;
      }

      // Get list of published pages that are children of the current page.

      $opts = array(
         'child_of'    => $post->ID,
         'depth'       => 2,
         'echo'        => 0,
         'post_type'   => 'page',
         'post_status' => 'publish',
         'show_date'   => '',
         'sort_column' => 'menu_order, post_title',
         'sort_order'  => '',
         'title_li'    => '', 
         'walker'      => '');

      $children = wp_list_pages($opts);
    
      // Get list of sibling pages of the current page.
      
      if ($ecount > 0)
      {
        $opts['child_of'] = $elders[0];
        $opts['depth'] = 2;
        $opts['exclude_tree'] = $post->ID;
        
        $siblings = wp_list_pages($opts);
      }
      else
        $siblings = '';

      // Output.
            
      echo $args['before_widget'];

      $t = $instance['title'];
      $t = apply_filters('widget_title', empty($t) ? 'Related Pages' : $t);

      if (! empty($t))
        echo $args['before_title'] . $t . $args['after_title'];

      echo $before_child;
      echo '<ul class="children">' . $children . '</ul>';
     
      echo $siblings;
         
      echo $after_child;      
        
      echo $args['after_widget'];     
    }
  }
} 


// Register my widget.

function mss_showchildpageswidget_load()
{
  register_widget('mss_showchildpageswidget');
}
add_action( 'widgets_init', 'mss_showchildpageswidget_load' );
?>

This is the code for the main 18M2+.

#PICAXE 18M2
#terminal off

; Project: Word Clock v2.
; File: Word Clock V2.bas
; Description:
;     Read time from DS1307 RTC and display on LED matrix, where
;     the LEDs are behind a template with an array of cut out
;     letters and the lit letters will spell out the time in
;     words. The time will only br accurate to within five
;     minutes. Time can also be displayed as numbers using 3x5
;     blocks of LEDs to display numbers.
;
; Copyright (C) 2014 Marc Symonds
; All rights reserved.
;
; This software may be used and redistributed, with or without
; modification, as long as it is understood that this software
; is provided as-is without any explicit or implied warranties
; of merchantablity or fitness of purpose.


#rem
DS1307 Pins

        +---+
    X1 X|1  |X Vcc
    X2 X|   |X SQW/OUT
+VBatt X|   |X I2C/SCL
    0V X|   |X I2C/SDA
        +---+
#endrem

#rem        
PICAXE18-M2 Pins

                               +---+
 DISPLAYF/TEMP - (Out/In) C.2 -|1  |- C.1 (In/Out) - Button - Mode
   LOWBRIGHT/TEMP - (Out) C.3 -|   |- C.0 (In/Out) - Button - Set
             SerIn - (In) C.4 -|   |- C.7 (In/Out) - Piezo
                     (In) C.5 X|   |X C.6 (In/Out) 
                           0V -|5  |- Vcc
   MAX7219/CLK - (Out/In) B.0 -|   |- B.7 (In/Out) - Button - Adjust
DS1307 - (Out/In/I2C SDA) B.1 -|   |- B.6 (In/Out) - Button - Brightness
  MAX7219/DATA - (Out/In) B.2 -|   |- B.5 (In/Out) - LDR
  MAX7219/LOAD - (Out/In) B.3 -|9  |X B.4 (In/Out/I2C SCL) - DS1307
                               +---+
#endrem

#rem
PICAXE-08M2 Pins

                                      +---+
                                  +V -|1  |- 0V
  DISPLAYF/TEMP - (In) Serial in C.5 -|   |- C.0 Serial out (Out/hserout/DAC) - MAX7219/CLK
Temp Sensor - (Touch/ADS/Out/In) C.4 -|   |- C.1 (In/Out/ADC/Touch/hserin/SRI/I2C SCL) - MAX7219/LOAD
           LOWBRIGHT/TEMP - (In) C.3 -|   |- C.2 (In/Out/ADC/Touch/PWM/Tune/SRQ/I2C SDA) - MAX7219/DATA
                                      +---+
#endrem

#rem
Memory:-

00 - 27 - Registers
30 - 36 - Top half buffer.
37 - 43 - Bottom half buffer.
44 - 50 - Top half buffer compare.
51 - 57 - Bottom half buffer compare.

Sending to the MAX7219 is slow, so we only send data where something has changed.


Digit/Segment layout on display (11x10):-

0A 0B 0C 0D 0E 0F 0G 0P 5A 5B 5C
1A 1B 1C 1D 1E 1F 1G 1P 5D 5E 5F
2A 2B 2C 2D 2E 2F 2G 2P 5G 5P 6A      First MAX7219
3A 3B 3C 3D 3E 3F 3G 3P 6B 6C 6D
4A 4B 4C 4D 4E 4F 4G 4P 6E 6F 6G
--------------------------------
0A 0B 0C 0D 0E 0F 0G 0P 5A 5B 5C
1A 1B 1C 1D 1E 1F 1G 1P 5D 5E 5F
2A 2B 2C 2D 2E 2F 2G 2P 5G 5P 6A      Second MAX7219
3A 3B 3C 3D 3E 3F 3G 3P 6B 6C 6D
4A 4B 4C 4D 4E 4F 4G 4P 6E 6F 6G

P = DP.

#endrem

symbol MAX7219_REGISTER = b0
symbol MAX7219_DATA_1 = b1
symbol DS1307_DATA = b0        ; Must use b0 as bitX is used to manipulate. MAX and DS not accessed at the same time, so can reuse variable.
;symbol vWVAR = w0

symbol MAX7219_DATA_2 = b2
symbol MAX7219_SEND_COUNT = b3
;symbol vWVAR = w1

symbol MAX7219_SEND_DATA = b4
;symbol vBVAR = b5
;symbol vWVAR = w2

;symbol vBVAR = b6
;symbol vBVAR = b7
;symbol vWVAR = w3

;symbol vBVAR = b8
;symbol vBVAR = b9
;symbol vWVAR = w4

;symbol MODE_BUTTON_COUNTER = b10
;symbol SET_BUTTON_COUNTER = b11
symbol DISP_TABLE_IDX = w5

;symbol BRIGHTNESS_BUTTON_COUNTER = b12
;symbol ADJUST_BUTTON_COUNTER = b13
;symbol vWVAR = w6

;symbol vBVAR = b14
;symbol vBVAR = b15
;symbol vWVAR = w7

symbol DISP_MEM = b16
symbol DISP_DATA = b17
;symbol vWVAR = w8

symbol DISP_MERGE = b18
symbol DISP_NUM_TO_SHOW = b19
;symbol vWVAR = w9

symbol v_SECONDS = b20
symbol v_MINUTES = b21
;symbol vWVAR = w10

symbol v_HOURS = b22
symbol v_SECONDS_PREV = b23
;symbol vWVAR = w11

symbol v_MINUTES_PREV = b24
symbol v_MODE = b25
;symbol vWVAR = w12

symbol v_BRIGHTNESS = b26
;symbol vBVAR = b27
;symbol vWVAR = w13


' MAX7219 Pins
symbol MAX7219_LOAD = B.2
symbol MAX7219_DIN = B.3
symbol MAX7219_CLK = B.0

; MAX7219 Registers
symbol MAX7219_NOOP = $00
symbol MAX7219_DIGIT0 = $01
symbol MAX7219_DIGIT1 = $02
symbol MAX7219_DIGIT2 = $03
symbol MAX7219_DIGIT3 = $04
symbol MAX7219_DIGIT4 = $05
symbol MAX7219_DIGIT5 = $06
symbol MAX7219_DIGIT6 = $07
symbol MAX7219_DIGIT7 = $08
symbol MAX7219_DECODE_MODE = $09
symbol MAX7219_INTENSITY = $0A
symbol MAX7219_SCAN_LIMIT = $0B
symbol MAX7219_SHUTDOWN = $0C
symbol MAX7219_TEST_MODE = $0F


; DS1307 I2C Address.
symbol DS1307_ADDRESS = %11010000

; DS1307 Registers.
symbol DS1307_SECONDS = $00
symbol DS1307_MINUTES = $01
symbol DS1307_HOURS = $02
symbol DS1307_WEEKDAY = $03
symbol DS1307_DAY = $04
symbol DS1307_MONTH = $05
symbol DS1307_YEAR = $06
symbol DS1307_CONTROL = $07
symbol DS1307_RAM = $08 ; to $3F

; DS1307 Bits (assumes b0 used to manipulate values)
symbol DS1307_SECONDS_CH = bit7 ; Clock Halt - high = halt, low = go

symbol DS1307_HOURS_1224 = bit6 ; high = 12 hour, low = 24 hour
symbol DS1307_HOURS_AMPM = bit5 ; high = PM, low = AM (in 12 hour mode)

symbol DS1307_CONTROL_OUT = bit7
symbol DS1307_CONTROL_SQWE = bit4
symbol DS1307_CONTROL_RS1 = bit1
symbol DS1307_CONTROL_RS0 = bit0


; We'll use the DS1307 RAM to store some values in case main power is lost.
symbol DS1307_USR_MODE = DS1307_RAM + $00 ; Display mode
symbol DS1307_USR_BRIGHTNESS = DS1307_RAM + $01 ; Brightness level


; Display data will be buffered in these memory loctations, before being sent to the display (MAX7219).
symbol MEM_DISPLAY_TOP = 30 ; Start of buffer for top half of display.
symbol MEM_DISPLAY_TOP_END = 36 ; End of buffer for top half of display.
symbol MEM_DISPLAY_BOTTOM = 37
symbol MEM_DISPLAY_BOTTOM_END = 43

symbol MEM_DISPLAY_TOP_CMP = 44 ; -> 50
symbol MEM_DISPLAY_BOTTOM_CMP = 51 ; -> 57

symbol MEM_BUT_MODE = 58
symbol MEM_BUT_SET = 59
symbol MEM_BUT_ADJUST = 60
symbol MEM_BUT_BRIGHTNESS = 61

symbol MEM_BRIGHT_CURR = 62
symbol MEM_BRIGHT_HIGHER = 63
symbol MEM_BRIGHT_COUNT = 64

; Pins used for buttons.
symbol BUT_MODE = C.1
symbol BUT_MODEP = pinC.1
symbol BUT_SET = C.0
symbol BUT_SETP = pinC.0
symbol BUT_ADJUST = B.7
symbol BUT_ADJUSTP = pinB.7
symbol BUT_BRIGHTNESS = B.6
symbol BUT_BRIGHTNESSP = pinB.6

; Pin used for piezo.
symbol PIEZO = C.7

;
symbol BRIGHTNESS_SENSOR = B.5
symbol TEMP_BRIGHTNESS = C.3
symbol TEMP_BRIGHTNESSP = pinC.3
symbol TEMP_FAREN = C.2
symbol TEMP_FARENP = pinC.2

; Data for displaying digits.

; 0 - 9 On left of display.
symbol TAB_NUM_LEFT = 0

table TAB_NUM_LEFT, (%01110000, %01010000, %01010000, %01010000, %01110000, 0, 0) ; 0
table               (%00100000, %01100000, %00100000, %00100000, %01110000, 0, 0) ; 1
table               (%01110000, %00010000, %01110000, %01000000, %01110000, 0, 0) ; 2
table               (%01110000, %00010000, %00110000, %00010000, %01110000, 0, 0) ; 3 
table               (%01010000, %01010000, %01110000, %00010000, %00010000, 0, 0) ; 4
table               (%01110000, %01000000, %01110000, %00010000, %01110000, 0, 0) ; 5
table               (%01110000, %01000000, %01110000, %01010000, %01110000, 0, 0) ; 6
table               (%01110000, %00010000, %00010000, %00010000, %00010000, 0, 0) ; 7
table               (%01110000, %01010000, %01110000, %01010000, %01110000, 0, 0) ; 8
table               (%01110000, %01010000, %01110000, %00010000, %01110000, 0, 0) ; 9

; 0 - 9 In middle of display.
symbol TAB_NUM_MIDDLE = 70

table TAB_NUM_MIDDLE, (%00000111, %00000101, %00000101, %00000101, %00000111, 0, 0)
table                 (%00000010, %00000110, %00000010, %00000010, %00000111, 0, 0)
table                 (%00000111, %00000001, %00000111, %00000100, %00000111, 0, 0)
table                 (%00000111, %00000001, %00000011, %00000001, %00000111, 0, 0)
table                 (%00000101, %00000101, %00000111, %00000001, %00000001, 0, 0)
table                 (%00000111, %00000100, %00000111, %00000001, %00000111, 0, 0)
table                 (%00000111, %00000100, %00000111, %00000101, %00000111, 0, 0)
table                 (%00000111, %00000001, %00000001, %00000001, %00000001, 0, 0)
table                 (%00000111, %00000101, %00000111, %00000101, %00000111, 0, 0)
table                 (%00000111, %00000101, %00000111, %00000001, %00000111, 0, 0)

; 0 - 9 On right of display.
symbol TAB_NUM_RIGHT = 140

table TAB_NUM_RIGHT, (0, 0, 0, 0, 0, %01111011, %01101111)
table                (0, 0, 0, 0, 0, %10101100, %00010111)
table                (0, 0, 0, 0, 0, %11110011, %01100111)
table                (0, 0, 0, 0, 0, %11110010, %01001111)
table                (0, 0, 0, 0, 0, %11011011, %01001001)
table                (0, 0, 0, 0, 0, %11111001, %01001111)
table                (0, 0, 0, 0, 0, %11111001, %01101111)
table                (0, 0, 0, 0, 0, %01110010, %01001001)
table                (0, 0, 0, 0, 0, %11111011, %01101111)
table                (0, 0, 0, 0, 0, %11111011, %01001111)

; H.
symbol TAB_WRD_Hr = 210

table TAB_WRD_Hr, (%01001000, %01001000, %01111000, %01001000, %01001000, 0, 0)

; M.
symbol TAB_WRD_Mi = 217

table TAB_WRD_Mi, (%01000100, %01101100, %01010100, %01000100, %01000100, 0, 0)


; Pixels to light to tell the time in words. These are the indexes for the table.
symbol TAB_TIM_ITIS = 224
symbol TAB_TIM_OCLOCK = 228
symbol TAB_TIM_FIVE = 232
symbol TAB_TIM_TEN = 236
symbol TAB_TIM_A_QUARTER = 240
symbol TAB_TIM_TWENTY = 244
symbol TAB_TIM_TWENTYFIVE = 248
symbol TAB_TIM_HALF = 252
symbol TAB_TIM_PAST = 256
symbol TAB_TIM_TO = 260
symbol TAB_TIM_ONE = 264
symbol TAB_TIM_TWO = 268
symbol TAB_TIM_THREE = 272
symbol TAB_TIM_FOUR = 276
symbol TAB_TIM_FIVEw = 280
symbol TAB_TIM_SIX = 284
symbol TAB_TIM_SEVEN = 288
symbol TAB_TIM_EIGHT = 292
symbol TAB_TIM_NINE = 296
symbol TAB_TIM_TENw = 300
symbol TAB_TIM_ELEVEN = 304
symbol TAB_TIM_TWELVE = 308

;                          Row  1st 8      Top End    Bottom End
table TAB_TIM_ITIS,       ($00, %01101100, %00000000, %00000000)

table TAB_TIM_OCLOCK,     ($09, %10000011, %00000000, %00000111)
table TAB_TIM_FIVE,       ($02, %10000001, %10000001, %00000000)
table TAB_TIM_TEN,        ($03, %10000011, %00000000, %00000000)
table TAB_TIM_A_QUARTER,  ($01, %11011111, %00001000, %00000000)
table TAB_TIM_TWENTY,     ($02, %01111110, %00000000, %00000000)
table TAB_TIM_TWENTYFIVE, ($02, %11111111, %10000001, %00000000)
table TAB_TIM_HALF,       ($03, %01111000, %00000000, %00000000)

table TAB_TIM_PAST,       ($04, %01111000, %00000000, %00000000)
table TAB_TIM_TO,         ($03, %00000000, %00000000, %00011000)

table TAB_TIM_ONE,        ($05, %01110000, %00000000, %00000000)
table TAB_TIM_TWO,        ($06, %00000000, %00001110, %00000000)
table TAB_TIM_THREE,      ($05, %10000001, %01110000, %00000000)
table TAB_TIM_FOUR,       ($06, %01111000, %00000000, %00000000)
table TAB_TIM_FIVEw,      ($06, %10000111, %00000000, %00000000)
table TAB_TIM_SIX,        ($05, %00001110, %00000000, %00000000)
table TAB_TIM_SEVEN,      ($08, %01111100, %00000000, %00000000)
table TAB_TIM_EIGHT,      ($07, %01111100, %00000000, %00000000)
table TAB_TIM_NINE,       ($04, %10000000, %00000000, %00000111)
table TAB_TIM_TENw,       ($09, %01110000, %00000000, %00000000)
table TAB_TIM_ELEVEN,     ($07, %10000011, %10000001, %01000000)
table TAB_TIM_TWELVE,     ($08, %10000011, %00000000, %00111000)


; ********************************************************************************
; *** Start

  setfreq m4

  ;let dirsB = %00001101
  ;let dirsC = %10001100

  output MAX7219_LOAD
  output MAX7219_DIN
  output MAX7219_CLK

  input BUT_MODE
  input BUT_SET
  input BUT_ADJUST
  input BUT_BRIGHTNESS

  input BRIGHTNESS_SENSOR

  output TEMP_BRIGHTNESS
  output TEMP_FAREN

  output PIEZO
  
  ; The 08M2 which is used to read the temperature is using its serial in pin
  ; as an input from this chip to indicate display mode. Therefore the 08M2
  ; uses the  disconnect  function so that the pin can be used as a normal
  ; input pin. The program in the 08M2 has a long pause when it starts up so
  ; that it can still be programmed.
  ; The delay below allows the 08M2 to get through it's own delay before we
  ; start up.

  pause 3200

  gosub MAX7219_Initialise
  gosub DS1307_Initialise

  ; Load saved settings from DS1307.
  setfreq m8
  hi2cin DS1307_USR_MODE, (v_MODE, v_BRIGHTNESS)
  setfreq m32

  ; Bit 7 is flag for temp display in F.

  let v_MODE = v_MODE & $03
  if v_MODE > 2 then
    let v_MODE = 0
  end if  

  ; Bit 4 is flag for auto brightness.
  let v_BRIGHTNESS = v_BRIGHTNESS & $1f
  if v_BRIGHTNESS > $10 then
    let v_BRIGHTNESS = $10
  end if

  gosub ResetDisplay

; *** Main loop

main:
  gosub SampleBrightness

  ; Read the current time from the DS1307.
  setfreq m8
  hi2cin DS1307_SECONDS, (v_SECONDS, v_MINUTES, v_HOURS)
  setfreq m32

;peek MEM_BRIGHT_CURR, b0
;gosub ShowNum

  if v_BRIGHTNESS > $0f then ; Auto brightness?
    peek MEM_BRIGHT_CURR, b0
    let b1 = v_BRIGHTNESS & $0f
    if b1 <> b0 then
      let v_BRIGHTNESS = $10 + b0
      gosub SetModeBrightness
    end if
  end if

  ; Display the time.
  let b0 = v_MODE & $7f
  on b0 gosub ShowTimeInWords, ShowHoursMinutes, ShowMinutesSeconds

  ; Check for button presses.
  ;BUTTON pin,downstate,delay,rate,bytevariable,targetstate,address

  peek MEM_BUT_MODE, b0
  button BUT_MODE, 1, 255, 0, b0, 1, SwitchModeMain
  poke MEM_BUT_MODE, b0

  peek MEM_BUT_BRIGHTNESS, b0
  button BUT_BRIGHTNESS, 1, 255, 0, b0, 1, SwitchBrightnessMain
  poke MEM_BUT_BRIGHTNESS, b0

  peek MEM_BUT_SET, b0
  button BUT_SET, 1, 255, 0, b0, 1, SetTime
  poke MEM_BUT_SET, b0
  
  pause 10
  goto main


; Make a beep.
KeySound:
  sound PIEZO, (20, 40)
  return
  

; Mode button pressed.
SwitchModeMain:
  poke MEM_BUT_MODE, b0

  gosub KeySound

  ; Hold for 1 second to set temp display.

  setfreq m8
  let b0 = 0
  do
    inc b0
    pause 10
  loop while b0 < 100 and BUT_MODEP = 1
  setfreq m32

  if b0 >= 100 then
    gosub KeySound
    gosub SwitchTempDisplayMode
  else
    gosub SwitchMode
  end if

  goto main


; Brightness button pressed.
SwitchBrightnessMain:
  poke MEM_BUT_BRIGHTNESS, b0

  gosub KeySound
  gosub SwitchBrightness
  goto main  

; Set button pressed.
SetTime:
  poke MEM_BUT_SET, b0

  gosub KeySound
  gosub ClearDisplay
  
; Set top half of display dim and bottom half bright.  
  let MAX7219_DATA_1 = $02
  let MAX7219_DATA_2 = $0f
  gosub MAX7219_SetIntensity

; Change the hours.
  let DISP_TABLE_IDX = TAB_WRD_HR
  gosub DisplayTop
  gosub SendToDisplay

  do
    pause 10
  loop while BUT_SETP = 1

  let b18 = 0
  
  let b15 = v_HOURS & $3f
  let b14 = $23 ; BCD
  gosub AdjustNumber
  if b17 = 0 then SetTimeEnd ; b17 = 0 means cancelled.
  let v_HOURS = v_HOURS & $C0 | b15

; Change the minutes.
  gosub ClearDisplayTop
  let DISP_TABLE_IDX = TAB_WRD_MI
  gosub DisplayTop
  
  let b15 = v_MINUTES
  let b14 = $59 ; BCD
  gosub AdjustNumber
  if b17 = 0 then SetTimeEnd ; b17 = 0 means cancelled.
  let v_MINUTES = b15
  
  if b18 = 0 then SetTimeEnd ; Nothing was changed, so don't update the DS1307.
  
; Save the new time to the DS1307. Set seconds to 0.
  setfreq m8
  hi2cout DS1307_SECONDS, (0, v_MINUTES, v_HOURS)
  setfreq m32
  
SetTimeEnd:
  gosub ResetDisplay
  goto main
  
AdjustNumber:
  gosub ClearDisplayBottom
  let DISP_NUM_TO_SHOW = b15

  gosub DisplayNumBottom
  gosub SendToDisplay

AdjustNumberButtons:
  ;peek MEM_BUT_ADJUST, b0
  ;button BUT_ADJUST, 1, 100, 60, b0, 1, AdjustNumberInc ; Adjust number
  ;poke MEM_BUT_ADJUST, b0
 if BUT_ADJUSTP = 1 then goto AdjustNumberInc

  peek MEM_BUT_SET, b0
  button BUT_SET, 1, 255, 0, b0, 1, AdjustNumberSave ; Save number and move to next
  poke MEM_BUT_SET, b0

  peek MEM_BUT_MODE, b0
  button BUT_MODE, 1, 255, 0, b0, 1, AdjustNumberCancel ; Cancel adjust
  poke MEM_BUT_MODE, b0

  pause 10
  goto AdjustNumberButtons

AdjustNumberInc:
 ; poke MEM_BUT_ADJUST, b0

  gosub KeySound
; Working with BCD numbers, so need to faff about a bit.
  let b0 = b15 & $0f
  if b0 = $09 then
    let b15 = b15 & $f0 + $10
  else
    inc b15
  end if
  
  if b15 > b14 then
    let b15 = $00
  end if
  
  let b18 = 1 ; Flag to indicate number has changed.
  
  goto AdjustNumber
  
AdjustNumberSave:
  poke MEM_BUT_SET, b0

  gosub KeySound

  do
    pause 10
  loop while BUT_SETP = 1
  
  let b17 = 1
  return
  
AdjustNumberCancel:
  poke MEM_BUT_MODE, b0

  gosub KeySound
  
  do
    pause 10
  loop while BUT_MODEP = 1

  let b17 = 0
  return
  

SwitchBrightness:
  let b0 = v_BRIGHTNESS & $10

  if b0 = 0 then
    if v_BRIGHTNESS = 0 then
      peek MEM_BRIGHT_CURR, b0
      let v_BRIGHTNESS = $10 | b0 ; Turn on auto brightness.
      pause 100
      gosub KeySound
    else
      dec V_BRIGHTNESS
    end if
  else
    let v_BRIGHTNESS = $0f ; Turn off auto brightness and set to max.
  end if

; Save the brightness to the DS1307.
  setfreq m8
  hi2cout DS1307_USR_BRIGHTNESS, (v_BRIGHTNESS)
  setfreq m32

  goto SetModeBrightness


SampleBrightness:
  peek MEM_BRIGHT_CURR, b0
  peek MEM_BRIGHT_HIGHER, b1
  readadc BRIGHTNESS_SENSOR, b2
  peek MEM_BRIGHT_COUNT, b3

  let b2 = b2 / 25 + 4

  if b2 < b0 then
    if b1 = 1 then
      poke MEM_BRIGHT_HIGHER, 0
      let b3 = 0
    else
      inc b3
      if b3 > 100 then
        dec b0
        poke MEM_BRIGHT_CURR, b0
        let b3 = 0
      end if
    end if
  elseif b2 > b0 then
    if b1 = 0 then
      poke MEM_BRIGHT_HIGHER, 1
      let b3 = 0
    else
      inc b3
      if b3 > 100 then
        inc b0
        poke MEM_BRIGHT_CURR, b0
        let b3 = 0
      end if
    end if
  else
    let b3 = 0
  end if

  poke MEM_BRIGHT_COUNT, b3

  return


; Save the mode to the DS1307.
SaveMode:
  setfreq m8
  hi2cout DS1307_USR_MODE, (v_MODE)
  setfreq m32

  return

SwitchMode:
; 0=Words, 1=Hours/Minutes, 2=Minutes/Seconds

  let b0 = v_MODE & $7f + 1
  if b0 > 2 then
    let b0 = 0
  end if
  let v_MODE = v_MODE & $80 + b0
  gosub SaveMode
 
; Force update next time round.
ResetDisplay:
  let v_MINUTES_PREV = $ff
  let v_SECONDS_PREV = $ff
  
; *** Fall-through.
SetupMode:
  gosub ClearDisplay
  gosub SendToDisplay
  gosub SetTempDisplayMode
; *** Fall-through.  
SetModeBrightness:
  let MAX7219_DATA_2 = v_BRIGHTNESS & $0f
  
  let b1 = v_MODE & $7f
  if b1 = 0 then
    let MAX7219_DATA_1 = MAX7219_DATA_2 ' Normal "Word" mode, same brightness on top and bottom.
  else
    let MAX7219_DATA_1 = MAX7219_DATA_2 / 3 ; "Minute" or "Second" mode, set top half to 1/3 brightness.
  end if

  if MAX7219_DATA_2 > 7 then
    high TEMP_BRIGHTNESS
  else
    low TEMP_BRIGHTNESS
  end if

  goto MAX7219_SetIntensity


SwitchTempDisplayMode:
  let b0 = v_MODE & $80
  if b0 = 0 then
    let v_MODE = v_MODE | $80
  else
    let v_MODE = v_MODE & $7f
  end if

  gosub SaveMode
; *** Fall-through.  
SetTempDisplayMode:
  let b0 = v_MODE & $80
  let b1 = TEMP_FARENP
  if b0 = 0 then
    if b1 = 1 then
      low TEMP_FAREN
    end if
  else
    if b1 = 0 then
      high TEMP_FAREN
    end if
  end if

  return


FromBCD:
  let b1 = b0 / 16 * 10
  let b0 = b0 & $0f + b1
  return


ShowTimeInWords:
  if v_MINUTES <> v_MINUTES_PREV then
    gosub ClearDisplay

    ; Convert hours from BCD time in to normal values to deal with easier.
    
    let b0 = v_HOURS & $3f ; Remove 24 hour flag.
    gosub FromBCD
    let v_HOURS = b0

    ; Clock is running in 24 hour mode, so convert hours to 12 hour.
      
    if v_HOURS = 0 then
      let v_HOURS = 12
    elseif v_HOURS > 12 then
      let v_HOURS = v_HOURS - 12
    end if

    ; IT IS    
    let DISP_TABLE_IDX = TAB_TIM_ITIS
    gosub DisplayWord
    
    ; Fuzzy time 58-02=0, 03-07=5, 08-12=10, 13-17=15, 18-22=20, 23-27=25, 28-32=30, 33-37=35, 38-42=40, 43-37=45, 48-52=50, 53-57=55
    ; Minutes
    if v_MINUTES <= $02 then
      let DISP_TABLE_IDX = TAB_TIM_OCLOCK
    elseif v_MINUTES <= $07 then
      let DISP_TABLE_IDX = TAB_TIM_FIVE
    elseif v_MINUTES <= $12 then
      let DISP_TABLE_IDX = TAB_TIM_TEN
    elseif v_MINUTES <= $17 then
      let DISP_TABLE_IDX = TAB_TIM_A_QUARTER
    elseif v_MINUTES <= $22 then
      let DISP_TABLE_IDX = TAB_TIM_TWENTY
    elseif v_MINUTES <= $27 then
      let DISP_TABLE_IDX = TAB_TIM_TWENTYFIVE
    elseif v_MINUTES <= $32 then
      let DISP_TABLE_IDX = TAB_TIM_HALF
    else
      if v_MINUTES <= $37 then
        let DISP_TABLE_IDX = TAB_TIM_TWENTYFIVE
      elseif v_MINUTES <= $42 then
        let DISP_TABLE_IDX = TAB_TIM_TWENTY
      elseif v_MINUTES <= $47 then
        let DISP_TABLE_IDX = TAB_TIM_A_QUARTER
      elseif v_MINUTES <= $52 then
        let DISP_TABLE_IDX = TAB_TIM_TEN
      elseif v_MINUTES <= $57 then
        let DISP_TABLE_IDX = TAB_TIM_FIVE
      else
        let DISP_TABLE_IDX = TAB_TIM_OCLOCK
      end if
      
      ; Gone past 30 minutes, so adjust hour to the hour we are coming up to.
      inc v_HOURS
      if v_HOURS > 12 then
        let v_HOURS = 1
      end if
    end if
    
    gosub DisplayWord
    
    ; Past or To. If not O'Clock.
    if v_MINUTES >= $03 and v_MINUTES < $58 then
      if v_MINUTES < $33 then
        let DISP_TABLE_IDX = TAB_TIM_PAST
      else
        let DISP_TABLE_IDX = TAB_TIM_TO
      end if
      
      gosub DisplayWord
    end if
      
    ; Hour.
    let DISP_TABLE_IDX = v_HOURS - 1 * 4 + TAB_TIM_ONE
    gosub DisplayWord
    
    gosub SendToDisplay
    
    let v_MINUTES_PREV = v_MINUTES
  end if

  return


; --------------------------------------------------------------------------------
; Display a word. Which LEDs to light are read from the table.

DisplayWord:
; DISP_TABLE_IDX = Index in table of word to display.

  symbol DW_LINE = b2
  symbol DW_MAIN_DATA = b3
  symbol DW_MAIN_MEM_IDX = b4
  symbol DW_OTHER_MEM_IDX = b6
  symbol DW_DATA = b5

  readtable DISP_TABLE_IDX, DW_LINE ; Line
  inc DISP_TABLE_IDX
  readtable DISP_TABLE_IDX, DW_MAIN_DATA ; Main data
  inc DISP_TABLE_IDX
  
  if DW_LINE > 4 then
    let DW_MAIN_MEM_IDX = DW_LINE - 5 + MEM_DISPLAY_BOTTOM
    let DW_OTHER_MEM_IDX = MEM_DISPLAY_BOTTOM_END - 1
  else
    let DW_MAIN_MEM_IDX = DW_LINE + MEM_DISPLAY_TOP
    let DW_OTHER_MEM_IDX = MEM_DISPLAY_TOP_END - 1
  end if
  
  peek DW_MAIN_MEM_IDX, DW_DATA
  let DW_DATA = DW_DATA or DW_MAIN_DATA
  poke DW_MAIN_MEM_IDX, DW_DATA
  
  readtable DISP_TABLE_IDX, DW_MAIN_DATA
  inc DISP_TABLE_IDX

  peek DW_OTHER_MEM_IDX, DW_DATA
  let DW_DATA = DW_DATA or DW_MAIN_DATA
  poke DW_OTHER_MEM_IDX, DW_DATA
  
  inc DW_OTHER_MEM_IDX
  
  readtable DISP_TABLE_IDX, DW_MAIN_DATA
  peek DW_OTHER_MEM_IDX, DW_DATA
  let DW_DATA = DW_DATA or DW_MAIN_DATA
  poke DW_OTHER_MEM_IDX, DW_DATA

  return


; --------------------------------------------------------------------------------
; Show the time in hours and minutes. Hours in top half, minutes in bottom half.

ShowHoursMinutes:
  if v_MINUTES <> v_MINUTES_PREV then
    gosub ClearDisplay

    ; NB. Time values are in BCD.

    let DISP_NUM_TO_SHOW = v_HOURS & $3f ; Take off the 24 Hour flag.
    gosub DisplayNumTop    

    let DISP_NUM_TO_SHOW = v_MINUTES
    gosub DisplayNumBottom

    gosub SendToDisplay
    
    let v_MINUTES_PREV = v_MINUTES
  end if
  
  return


; --------------------------------------------------------------------------------
; Show the time in minutes and seconds. Minutes in top half, seconds in bottom
; half.

ShowMinutesSeconds:
  if v_SECONDS <> v_SECONDS_PREV then
    gosub ClearDisplay
    
    ; NB. Time values are in BCD.
    
    let DISP_NUM_TO_SHOW = v_MINUTES
    gosub DisplayNumTop

    let DISP_NUM_TO_SHOW = v_SECONDS
    gosub DisplayNumBottom

    gosub SendToDisplay
    
    let v_SECONDS_PREV = v_SECONDS
  end if
  
  return
  
; ********************************************************************************
; *** Display Functions **********************************************************
; ********************************************************************************

; --------------------------------------------------------------------------------
; Send the data from the display buffer to the MAX7219's. It only sends data that
; has changed since the last time data was sent to the MAX7219's.

SendToDisplay:
  symbol STD_MEM_TOP = b5
  symbol STD_MEM_BOT = b6
  symbol STD_MEM_TOP_CMP = b7
  symbol STD_MEM_BOT_CMP = b8
  symbol STD_CMP_TOP_DATA = b3
  symbol STD_CMP_BOT_DATA = b4

  let MAX7219_REGISTER = MAX7219_DIGIT0
  let STD_MEM_TOP = MEM_DISPLAY_TOP
  let STD_MEM_TOP_CMP = MEM_DISPLAY_TOP_CMP
  let STD_MEM_BOT_CMP = MEM_DISPLAY_BOTTOM_CMP

  for STD_MEM_BOT = MEM_DISPLAY_BOTTOM to MEM_DISPLAY_BOTTOM_END
    peek STD_MEM_TOP, MAX7219_DATA_1 ; Read new display data.
    peek STD_MEM_BOT, MAX7219_DATA_2
  
    peek STD_MEM_TOP_CMP, STD_CMP_TOP_DATA ; Read last displayed data.
    peek STD_MEM_BOT_CMP, STD_CMP_BOT_DATA

;    if b1 = b3 and b2 <> b4 then
;      poke b8, b2
;      
;      gosub MAX7219_SendCmdAndDataTo2
;    elseif b1 <> b3 and b2 = b4 then
;      poke b7, b1
;      
;      gosub MAX7219_SendCmdAndDataTo1
;    elseif b1 <> b3 and b2 <> b4 then ; If changed, then send to MAX7219.
    if MAX7219_DATA_1 <> STD_CMP_TOP_DATA or MAX7219_DATA_2 <> STD_CMP_BOT_DATA then ; If changed, then send to MAX7219.
      ; Save new displayed data.
      
      poke STD_MEM_TOP_CMP, MAX7219_DATA_1
      poke STD_MEM_BOT_CMP, MAX7219_DATA_2
      
      gosub MAX7219_SendCmdAndSepData2Both
    endif
    
    inc MAX7219_REGISTER ; Next digit.
    inc STD_MEM_TOP
    inc STD_MEM_TOP_CMP
    inc STD_MEM_BOT_CMP
  next
  goto MAX7219_ShutdownOff


; --------------------------------------------------------------------------------
; Display 2 digit number in the top half of the display

DisplayNumTop:
  ; NB. Values should in BCD.

  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW & 15
  gosub DisplayNum01Top
  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW / 16
; *** Fall-through
; Display the "Tens" part of the number.
DisplayNum10Top:
; DISP_TABLE_IDX = Number to display.

  let DISP_TABLE_IDX = DISP_TABLE_IDX & $00FF * 7 + TAB_NUM_LEFT
  goto DisplayTopAdj:

; Display the "Unit" part of the number.
DisplayNum01Top:
; DISP_TABLE_IDX = Number to display.
  let DISP_TABLE_IDX = DISP_TABLE_IDX & $00FF * 7 + TAB_NUM_MIDDLE
  goto DisplayTopAdj:


; --------------------------------------------------------------------------------
; Display 2 digit number in the bottom half of the display

DisplayNumBottom:
  ; NB. Values should in BCD.

  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW & 15
  gosub DisplayNum01Bottom
;return
  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW / 16
; *** Fall-through.
; Display the "Tens" part of the number.
DisplayNum10Bottom:
; DISP_TABLE_IDX = Number to display.
  let DISP_TABLE_IDX = DISP_TABLE_IDX & $00FF * 7 + TAB_NUM_MIDDLE
  goto DisplayBottom

; Display the "Unit" part of the number.
DisplayNum01Bottom:
; DISP_TABLE_IDX = Number to display.
  let DISP_TABLE_IDX = DISP_TABLE_IDX & $00FF * 7 + TAB_NUM_RIGHT
  goto DisplayBottom

#rem
ShowNum:
  let DISP_NUM_TO_SHOW = b0
  gosub ClearDisplay
  gosub DisplayNum
  gosub SendToDisplay
    
  return

DisplayNum:
  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW / 100 * 7 + TAB_NUM_LEFT
  gosub DisplayTop

  let b0=DISP_NUM_TO_SHOW / 100
  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW % 100 / 10 * 7 + TAB_NUM_MIDDLE
  gosub DisplayTop

  let DISP_TABLE_IDX = DISP_NUM_TO_SHOW % 10 * 7 + TAB_NUM_RIGHT
  goto DisplayTop
#endrem

; --------------------------------------------------------------------------------
; Draw the "character" pointed to by DISP_TABLE_IDX in the top half of the
; display.

DisplayTop:
; DISP_TABLE_IDX = Start table address to read from. DISP_TABLE_IDX will be updated.
  
  for DISP_MEM = MEM_DISPLAY_TOP to MEM_DISPLAY_TOP_END
    readtable DISP_TABLE_IDX, DISP_DATA
    peek DISP_MEM, DISP_MERGE
    let DISP_DATA = DISP_DATA | DISP_MERGE
    poke DISP_MEM, DISP_DATA
    inc DISP_TABLE_IDX
  next

  return


; --------------------------------------------------------------------------------
; Draw the "character" pointed to by DISP_TABLE_IDX in the top half of the
; display adjusted one bit to the right. This is to offset the characters that
; will appear in the middle of the display.
DisplayTopAdj:
; DISP_TABLE_IDX = Start table address to read from. DISP_TABLE_IDX will be updated.

;((x & 1) * 128) + ((x & 254) / 2)
  for DISP_MEM = MEM_DISPLAY_TOP to MEM_DISPLAY_TOP_END
    readtable DISP_TABLE_IDX, DISP_DATA
  
    ; Adjust right one pixel.
    let DISP_MERGE = DISP_DATA & 1 * 128
    let DISP_DATA = DISP_DATA / 2 + DISP_MERGE
  
    peek DISP_MEM, DISP_MERGE
    let DISP_DATA = DISP_DATA | DISP_MERGE
    poke DISP_MEM, DISP_DATA
    inc DISP_TABLE_IDX
  next

  return


; --------------------------------------------------------------------------------
; Draw the "character" pointed to by DISP_TABLE_IDX in the bottom half of the
; display.

DisplayBottom:
; DISP_TABLE_IDX = Start table address to read from. DISP_TABLE_IDX will be updated.

  for DISP_MEM = MEM_DISPLAY_BOTTOM to MEM_DISPLAY_BOTTOM_END
    readtable DISP_TABLE_IDX, DISP_DATA
    peek DISP_MEM, DISP_MERGE
    let DISP_DATA = DISP_DATA | DISP_MERGE
    poke DISP_MEM, DISP_DATA
    inc DISP_TABLE_IDX
  next
  
  return


; --------------------------------------------------------------------------------
; Clear the display.

ClearDisplay:
  gosub ClearDisplayBottom
; *** Fall-through ***
ClearDisplayTop:
  let b13 = MEM_DISPLAY_TOP_CMP
  for DISP_MEM = MEM_DISPLAY_TOP to MEM_DISPLAY_TOP_END
     poke DISP_MEM, 0
     poke b13, 255
     inc b13
  next

  return

ClearDisplayBottom:
  let b13 = MEM_DISPLAY_BOTTOM_CMP
  for DISP_MEM = MEM_DISPLAY_BOTTOM to MEM_DISPLAY_BOTTOM_END
    poke DISP_MEM, 0
    poke b13, 255
    inc b13
  next
  
  return

; ********************************************************************************
; *** DS1307 Functions ***********************************************************
; ********************************************************************************

DS1307_Initialise:
  setfreq m8
  
  hi2csetup i2cmaster, DS1307_ADDRESS, i2cslow_8, i2cbyte

  gosub DS1307_24HourMode

  let b1 = 0
  gosub DS1307_SetClockHalt
  
  goto DS1307_SetSQWE_1Hz


; Set the CH (Clock Halt) bit in RTC.
DS1307_SetClockHalt:
; b1.0 = Clock Halth (CH) state

  hi2cin DS1307_SECONDS, (DS1307_DATA)
  if DS1307_SECONDS_CH <> bit8 then ;(bit8 = bit 0 of b1)
    let DS1307_SECONDS_CH = bit8
    hi2cout DS1307_SECONDS, (DS1307_DATA)
  end if

  return


DS1307_SetSQWE_1Hz:
  let DS1307_DATA = 0 ; Out = 0 and RS1/RS0 = 0.
  let DS1307_CONTROL_SQWE = 1 ; Set SQWE bit in DS1307_DATA (must be b0).
  hi2cout DS1307_CONTROL, (DS1307_DATA) 
  
  return


#rem
; Switch to 12 hour mode
DS1307_12HourMode:
  hi2cin DS1307_HOURS, (DS1307_DATA)
  if DS1307_HOURS_1224 = 0 then
    ; BCD to BIN
    let b1 = DS1307_DATA & $30 / 16 * 10
    let b1 = DS1307_DATA & $0f + b1
  
    let b2 = 1
    if b1 >= 12 then
      if b1 > 12 then
        let b1 = b1 - 12
      endif
      let b2 = 1
    else
      if b1 = 0 then
        let b1 = 12
      end if
      let b2 = 0
    endif
  
    ; BIN to BCD
    let DS1307_DATA = b1 / 10 * 16
    let DS1307_DATA = b1 % 10 + DS1307_DATA
  
    if b2 = 1 then
      let DS1307_HOURS_AMPM = 1 ' Set AM/PM but in DS1307_DATA (must be b0).
    endif
  
    let DS1307_HOURS_1224 = 1
    hi2cout DS1307_HOURS, (DS1307_DATA)
  end if

  return
#endrem

; Switch to 24 hour mode
DS1307_24HourMode:
  hi2cin DS1307_HOURS, (DS1307_DATA)
  if DS1307_HOURS_1224 = 1 then
    ; BCD to BIN
    let b1 = DS1307_DATA & $10 / 16 * 10
    let b1 = DS1307_DATA & $0f + b1

    if DS1307_HOURS_AMPM = 1 then
      if b1 < 12 then
        let b1 = b1 + 12
      end if
    elseif b1 = 12 then
      let b1 = 0
    end if

    ; BIN to BCD
    let DS1307_DATA = b1 / 10 * 16
    let DS1307_DATA = b1 % 10 + DS1307_DATA

    ;let DS1307_HOURS_1224 = 0 ; Bit will already be 0, so don't need to explicitly set it.
    hi2cout DS1307_HOURS, (DS1307_DATA)
  end if
  
  return

; ********************************************************************************
; *** MAX7219 Functions **********************************************************
; ********************************************************************************

MAX7219_Initialise:
  setfreq m32
  
  high MAX7219_LOAD
  low MAX7219_CLK
  low MAX7219_DIN

  pause 10

  let MAX7219_REGISTER = MAX7219_DECODE_MODE
  let MAX7219_DATA_1 = $00 ; Don't use decode mode for any data.
  gosub MAX7219_SendCmdAndDataToBoth

  let MAX7219_DATA_1 = $03
  let MAX7219_DATA_2 = $0c
  gosub MAX7219_SetIntensity

  let MAX7219_REGISTER = MAX7219_SCAN_LIMIT
  let MAX7219_DATA_1 = $06 ; Using 7 digits (0-6); not using the 8th digit.
  gosub MAX7219_SendCmdAndDataToBoth

  gosub MAX7219_ShutdownOff
  goto MAX7219_TestModeOff ; Return will be peformed by this function


MAX7219_ShutdownOff:
  let MAX7219_REGISTER = MAX7219_SHUTDOWN
  let MAX7219_DATA_1 = $01
  goto MAX7219_SendCmdAndDataToBoth ; Return will be peformed by this function

#rem
MAX7219_ShutdownOn:
  let MAX7219_REGISTER = MAX7219_SHUTDOWN
  let MAX7219_DATA_1 = $00
  goto MAX7219_SendCmdAndDataToBoth ; Return will be peformed by this function
#endrem

MAX7219_TestModeOn:
  let MAX7219_REGISTER = MAX7219_TEST_MODE
  let MAX7219_DATA_1 = $01
  goto MAX7219_SendCmdAndDataToBoth ; Return will be peformed by this function

  
MAX7219_TestModeOff:
  let MAX7219_REGISTER = MAX7219_TEST_MODE
  let MAX7219_DATA_1 = $00
  goto MAX7219_SendCmdAndDataToBoth ; Return will be peformed by this function


MAX7219_SetIntensity:
; MAX7219_DATA_1 = Intensity level for top half.
; MAX7219_DATA_2 = Intensirt level for bottom half.

  let MAX7219_REGISTER = MAX7219_INTENSITY
  goto MAX7219_SendCmdAndSepData2Both ; Return will be peformed by this function


MAX7219_SendCmdAndDataToBoth:
; MAX7219_REGISTER = Register
; MAX7219_DATA_1 = Data

  gosub MAX7219_SendCmdAndData ; This will send to the second MAX7219
  gosub MAX7219_SendCmdAndData ; This will send to the first MAX7219

  pulsout MAX7219_LOAD, 1

  return  


MAX7219_SendCmdAndSepData2Both:
; MAX7219_REGISTER = Register
; MAX7219_DATA_1 = Data for chip 1
; MAX7219_DATA_2 = Data for chip 2
  
  swap MAX7219_DATA_1, MAX7219_DATA_2 ; We have to send the data for chip 2 first, so swap the Data values
  gosub MAX7219_SendCmdAndData ; Send to the second MAX7219
  swap MAX7219_DATA_1, MAX7219_DATA_2 ; Swap the values back.
  gosub MAX7219_SendCmdAndData ; Send to the first MAX7219

  pulsout MAX7219_LOAD, 1

  return  


MAX7219_SendCmdAndDataTo1:
; MAX7219_REGISTER = Register.
; MAX7219_DATA_1 = Data for chip 1.

  gosub MAX7219_SendCmdAndData

  pulsout MAX7219_LOAD, 1

  return


MAX7219_SendCmdAndDataTo2:
; MAX7219_REGISTER = Register.
; MAX7219_DATA_2 = Data for chip 2.

  swap MAX7219_DATA_1, MAX7219_DATA_2
  gosub MAX7219_SendCmdAndData ; Send data for chip 2.
  
  swap MAX7219_REGISTER, MAX7219_DATA_1 ; Need to preserve MAX7219_REGISTER.
  
  let MAX7219_REGISTER = MAX7219_NOOP ; Send NOOP for chip 1.
  gosub MAX7219_SendCmdAndData
  
  swap MAX7219_REGISTER, MAX7219_DATA_1 ; Restore MAX7219_REGISTER..
  
  pulsout MAX7219_LOAD, 1

  return
  

MAX7219_SendCmdAndData:
; MAX7219_REGISTER = Register.
; MAX7219_DATA_1 = Data.
; b3 & b4 used as temp.

  let MAX7219_SEND_DATA = MAX7219_REGISTER
  gosub MAX7219_Send

  let MAX7219_SEND_DATA = MAX7219_DATA_1
; *** Fall-through is intended. ***
MAX7219_Send:
; b3 = Data to send

  for MAX7219_SEND_COUNT = 0 to 7
    if MAX7219_SEND_DATA > 127 then
      high MAX7219_DIN
    else
      low MAX7219_DIN
    end if

    pulsout MAX7219_CLK, 1    
    MAX7219_SEND_DATA = MAX7219_SEND_DATA * 2
  next

  low MAX7219_DIN

  return 

This program is used on a PICAXE 08M2 to read the temperature from a DS18B20 temperature sensor and display it on a 3 digit 7 segment LED display. A MAX7219 LED driver is used to drive the display.

The main processor can also control this chip through 2 inputs; one to control the brightness level, and the other display the temperature in farenheit.

#picaxe 08m2
#no_data

; Project: Word Clock v2.
; File: ReadDisplayTemp.bas
; Description:
;     Read temperature from a DS18B20 and display on a three
;     digit 7 segment LED display using a MAX7219 LED display
;     driver.
;
; Copyright (C) 2014 Marc Symonds
; All rights reserved.
;
; This software may be used and redistributed, with or without
; modification, as long as it is understood that this software
; is provided as-is without any explicit or implied warranties
; of merchantablity or fitness of purpose.

    
; MAX7219 Pins
symbol MAX7219_LOAD = C.1
symbol MAX7219_CLK = C.0
symbol MAX7219_DIN = C.2

; MAX7219 Registers
symbol MAX7219_NOOP = $00
symbol MAX7219_DIGIT0 = $01
symbol MAX7219_DIGIT1 = $02
symbol MAX7219_DIGIT2 = $03
symbol MAX7219_DIGIT3 = $04
symbol MAX7219_DIGIT4 = $05
symbol MAX7219_DIGIT5 = $06
symbol MAX7219_DIGIT6 = $07
symbol MAX7219_DIGIT7 = $08
symbol MAX7219_DECODE_MODE = $09
symbol MAX7219_INTENSITY = $0A
symbol MAX7219_SCAN_LIMIT = $0B
symbol MAX7219_SHUTDOWN = $0C
symbol MAX7219_TEST_MODE = $0F

; DS18B20 Pins
symbol DS18B20_DQ = C.4

; Pins used by main processor to control display.
symbol DISPLAY_F = C.5 ; Display in Farenheit.
symbol DISPLAY_FP = pinC.5
symbol LOW_BRIGHTNESS = C.3 ; Display brightness high or low.
symbol LOW_BRIGHTNESSP = pinC.3


symbol P_Register = b0
symbol P_Data = b1
symbol V_Temp1 = b3
symbol V_Temp2 = b4

symbol V_Neg = b8
symbol V_TempFrac = b9
symbol V_TempWhole = b10
symbol V_Digit0 = b11
symbol V_Digit1 = b12
symbol V_Digit2 = b13

symbol V_Temperature = w3 ; b6 b7

symbol V_LowBrightness = b14

letThereBeLight:
  setfreq m4
  
  ; This long delay is to allow the chip to be more easily programmed later,
  ; because further down we "disconnect" the chip so that it stops looking for
  ; new downloads.

  pause 3000
  
  let V_LowBrightness = 0

  input DISPLAY_F
  input LOW_BRIGHTNESS
  
  gosub MAX7219_Initialise

  ; We are using C.5 (serial in) as an input (to display farenheit), so we need
  ; to stop the chip from scanning the serial in pin looking for a new download.
  ; Once the disconnect command has run, you have to do a hard-reset to download
  ; a new program; see the manual.
  
  disconnect

main:
  setfreq m4
  readtemp12 DS18B20_DQ, V_Temperature
  setfreq m8

  gosub displayTemp

  if LOW_BRIGHTNESSP <> V_LowBrightness then
    let V_LowBrightness = LOW_BRIGHTNESSP
    
    if V_LowBrightness = 0 then
      let P_Data = $05
    else
      let P_Data = $0E
    end if
    
    gosub MAX7219_SetIntensity
  end if
  
  ;pause 1000
  sleep 2
  goto main


; The 3 digit 7 segment display I'm using has some segments that don't work;
; notably the  e  segment of the first character, and the DP segments of the
; first and third characters.
; So the displaying of the temperature below is a bit more complicated than
; would normally be required, to work around the problems with the display.
  
displayTemp:
  let V_Neg = 0
  if V_Temperature > $7FFF then
    let V_Temperature = -V_Temperature
    let V_Neg = 1
  end if
  
  if DISPLAY_FP = 1 then
    ; Convert C to F. 115/64 = 1.7968 (1.8 ish), and 512 is 32 * 16 (temp from DS18B20 is in 16th's of a degree).
    let V_Temperature = V_Temperature * 115 / 64 + 512 
  end if
  
  let b9 = V_Temperature & 15 * 10 / 16 ; frac
  let b10= V_Temperature / 16 ; whole

  ; Work out what to display in each of the three digits.
  
  if V_Neg = 1 then
    let V_Digit0 = 10 ; minus sign.
    
    if b10 > 99 then ; Can't display anything below -99, so show EE. If it's below -99 we're probably dead anyway.
      let b12 = 11 ; E
      let b13 = 11 ; E
    elseif b10 > 9 then
      let b12 = b10 / 10
      let b13 = b10 % 10
    else
      let b12 = b10 | $80 ; Turn on DP
      let b13 = b9
    end if      
  else
    if b10 > 99 then
      let b11 = b10 / 100
      let b12 = b10 / 10 % 10
      let b13 = b10 % 10
    elseif b10 < 20 then
      if b10 > 9 then
        let b11 = b10 / 10
      else
        let b11 = 15 ; blank
      end if
      
      let b12 = b10 % 10 | $80 ; Turn on DP
      let b13 = b9
    else
      let b11 = 15
      let b12 = b10 / 10
      let b13 = b10 % 10
    end if
  end if

  ; Display it.
  
  let b0 = MAX7219_DIGIT0
  let b1 = b11
  gosub MAX7219_SendCmdAndDataTo1
  
  let b0 = MAX7219_DIGIT1
  let b1 = b12
  gosub MAX7219_SendCmdAndDataTo1

  let b0 = MAX7219_DIGIT2
  let b1 = b13
  gosub MAX7219_SendCmdAndDataTo1
  
  return

; ********************************************************************************
; *** MAX7219 Functions **********************************************************
; ********************************************************************************

MAX7219_Initialise:
  output MAX7219_LOAD
  output MAX7219_CLK
  output MAX7219_DIN
  
  low MAX7219_LOAD
  low MAX7219_CLK
  low MAX7219_DIN

  pause 10

  gosub MAX7219_TestModeOff
  gosub MAX7219_ShutdownOff

  let b0 = MAX7219_SCAN_LIMIT
  let b1 = $02 ; Using 3 digits.
  gosub MAX7219_SendCmdAndDataTo1

  let b0 = MAX7219_DECODE_MODE
  let b1 = $7 ; Use BCD for digits 0, 1 and 2.
  gosub MAX7219_SendCmdAndDataTo1

  let b1 = $0C ' Max brightness.
  gosub MAX7219_SetIntensity

  return


MAX7219_ShutdownOff:
  let b0 = MAX7219_SHUTDOWN
  let b1 = $01
  goto MAX7219_SendCmdAndDataTo1 ; Return will be peformed by this function

#rem
MAX7219_ShutdownOn:
  let b0 = MAX7219_SHUTDOWN
  let b1 = $00
  goto MAX7219_SendCmdAndDataToBoth ; Return will be peformed by this function
#endrem

MAX7219_TestModeOn:
  let b1 = $01
  goto MAX7219_TestModeSet ; Return will be peformed by this function  
MAX7219_TestModeOff:
  let b1 = $00
MAX7219_TestModeSet:
  let b0 = MAX7219_TEST_MODE
  goto MAX7219_SendCmdAndDataTo1 ; Return will be peformed by this function


MAX7219_SetIntensity:
; b1 = Intensity level for top half.
; b2 = Intensirt level for bottom half.

  let b0 = MAX7219_INTENSITY
; *** Fall-through ***


MAX7219_SendCmdAndDataTo1:
; b0 = Register.
; b1 = Data for chip 1.

  gosub MAX7219_SendCmdAndData

; For 7219 you only need to pulse the LOAD pin to load the data.
  
  pulsout MAX7219_LOAD, 1
  
  return  

MAX7219_SendCmdAndData:
; b0 = Register.
; b1 = Data.
; b3 & b4 used as temp.

  let b3 = b0
  gosub MAX7219_Send

  let b3 = b1
; *** Fall-through is intended. ***
MAX7219_Send:
; b3 = Data to send

  for b4 = 0 to 7
    if b3 > 127 then
      high MAX7219_DIN
    else
      low MAX7219_DIN
    end if

    pulsout MAX7219_CLK, 1    

    b3 = b3 * 2
  next

  low MAX7219_DIN

  return 


#rem
3 Digit/7 Segment display Pins

          1 1 1                      a
          2 1 0 9 8 7               ---
          | | | | | |             f|   |b
         +-----------+             | g |
         |           |              ---
         | 8. 8. 8.  |            e|   |c
         |           |             |   |
         +-----------+              ---  O 
          | | | | | |                d
          1 2 3 4 5 6

 1 = Digit 0 cathode
 2 = Segment e
 3 = Segment d
 4 = Digit 1 cathode
 5 = Segment c
 6 = DP
 7 = Digit 2 cathode
 8 = Segment b
 9 = Segment g
10 = Segment a
11 = Segment f
12 = NC

#endrem
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVLightning.cpp
 * Description:
 *     Movie -- mv_Lightning
 *         Dark clouds, and occasional flashes of lightning.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Lightning()
{
  unsigned long ts, m;
  unsigned long tl, tf;
  byte i, p, y, z, mode, cnt;
  Light cloud[9];
  Point l1, l2;
  
  y = 0;
  z = 0;
  for (i = 0 ; i < 9 ; i++)
  {
    p = POS(2, y, z);
    
    displayFrame[p] = random(1, 3);
    
    cloud[i].up = (displayFrame[p] < 2);
    cloud[i].t = millis() + random(250, 750);
    
    if (++y > 2)
    {
      y = 0;
      ++z;
    }
  }
  
  ts = millis() + (DEF_MOVIE_TIME * 3);

  tl = millis() + random(2000, 5000);
  mode = 0;
  
  y = 0;
  z = 0;
  i = 0;
  m = millis();
  while (m < ts)
  {
    if (m > cloud[i].t)
    {
      p = POS(2, y, z);
      displayFrame[p] += (cloud[i].up ? 1 : -1);
      if (displayFrame[p] < 2)
        cloud[i].up = true;
      else if (displayFrame[p] > 1)
        cloud[i].up = false;

      cloud[i].t = m + random(250, 750);
    }
   
    if (mode > 0)
    {
      if (m > tf)
      {
        if (mode == 1)
        {
          displayFrame[l1.Pos()] = 7;
          displayFrame[l2.Pos()] = 7;
          mode = 2;
        }
        else
        {
          displayFrame[l1.Pos()] = 0;
          displayFrame[l2.Pos()] = 0;
          
          if (--cnt < 1)
          {
            mode = 0;
            tl = millis() + random(2000, 5000);
          }
          else
            mode = 1;
        }
        
        tf = m + 30;
      }
    }
    else if (m > tl)
    {
      l1.x = 1;
      l1.y = random(0, 3);
      l1.z = random(0, 3);
 
      l2.x = 0;
      do
      {
        l2.y = l1.y + random(0, 3) - 1;
      } while (l2.y > 2);
      
      do
      {
        l2.z = l1.z + random(0, 3) - 1;
      } while (l2.z > 2);
      
      mode = 1;
      cnt = random(5, 8);

      tf = m;
    }
  
    if (++y > 2)
    {
      y = 0;
      if (++z > 2)
        z = 0;
    }
    
    if (++i > 8)
      i = 0;
      
    m = millis();
  }
  
  df_fadeOut();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVBoxy.cpp
 * Description:
 *     Movie -- mv_Boxy
 *         Randonly display a growing and shrinking box.
 *         (as well as you can do in 3x3x3).
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Boxy()
{
  unsigned long ts;
  unsigned long tbox = 1; // 0xB000000000000000000000000001;
  unsigned long sbox = 13851; //0XB000000000000011011000011011;
  unsigned long lbox = 129948655; //0XB111101111101101101111101111;
  int spd;
  POSTransFPtr trans;

  trans = GetPOSTrans(random(0, 8));
  
  ts = millis() + (DEF_MOVIE_TIME / 2);

  while (millis() < ts)
  {
    spd = random(50, 90);
 
    df_DisplayMonoFrame(trans, tbox);   
    delay(spd);
    df_DisplayMonoFrame(trans, sbox);   
    delay(spd);
    df_DisplayMonoFrame(trans, lbox);   
    delay(spd * 4);
    
    trans = GetPOSTrans(random(0, 8));
    df_DisplayMonoFrame(trans, sbox);
    delay(spd);
    df_DisplayMonoFrame(trans, tbox);
    delay(spd);
    df_clear();
    
    delay(spd);
  }
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: Movies.h
 * Description: Definitions for movie functions.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#ifndef Movies_h
#define Movies_h

#define DEF_MOVIE_TIME 10000 

void mv_TwinkleAll();
void mv_Glitter();
void mv_Pulse();
void mv_Jitter();
void mv_EdgeChase();
void mv_EdgeChase2();
void mv_Rain();
void mv_AllOn();
void mv_Flashy();
void mv_EdgeTumble();
void mv_Counter();
void mv_Snake();
void mv_Boxy();
void mv_Lightning();
#endif
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVTwinkleAll.cpp
 * Description:
 *     Movie -- mv_TwinkleAll
 *         All LEDs are randomly faded in an out.
 *         Similar to mv_Flashy, but with a subtle difference.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_TwinkleAll()
{
  unsigned long ts;
  byte x;
  byte m[27];
  
  ts = millis() + DEF_MOVIE_TIME;

  for (x = 0 ; x < 27 ; x++)
    m[x] = (displayFrame[x] == MAXBRIGHTNESSLEVEL);

  while (millis() < ts)
  {
    for (x = 0 ; x < 27 ; x++)
    {
      if (m[x])
      {
        if (random(10) > 6)
        {
          if (--displayFrame[x] == 0)
           m[x] = false;
        }
      }
      else
      {
        if (random(10) > 4)
        {
          if (++displayFrame[x] >= MAXBRIGHTNESSLEVEL)
            m[x] = true;
        }
      }
    }
    
    delay(50);
  }

  df_fadeOut();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVSnake.cpp
 * Description:
 *     Movie -- mv_Snake
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

#define SNAKELEN 4

void mv_Snake()
{
  unsigned long ts, t;
  int ss, sl, sp, i, j, k;
  byte nx, ny, nz;
 
  ts = millis() + DEF_MOVIE_TIME;

  points[0] = Point((byte)random(0, 3), (byte)random(0, 3), (byte)random(0, 3));
  sl = 1;
  ss = 0;
  displayFrame[points[0].Pos()] = MAXBRIGHTNESSLEVEL;
  
  sp = random(100, 500);
  
  while (millis() < ts)
  {
    t = millis() + sp;
    
    do
    {
      nx = points[ss].x;
      ny = points[ss].y;
      nz = points[ss].z;
      
      switch(random(0, 3))
      {
        case 0:
          nx = nx + random(0, 3) - 1;
          break;
          
        case 1:
          ny = ny + random(0, 3) - 1;
          break;
          
        default:
          nz = nz + random(0, 3) - 1;
          break;
      }
    } while (millis() < t && (nx > 2 || ny > 2 || nz > 2 || displayFrame[POS(nx, ny, nz)] != 0));
    
    while (millis() < t)
      ;
      
    if (nx > 2 || ny > 2 || nz > 2 || displayFrame[POS(nx, ny, nz)] != 0)
    {
       for (i = 0 ; i < 27 ; i++)
         displayFrame[i] = MAXBRIGHTNESSLEVEL;
         
       delay(50);
       
       df_clear();

       points[0] = Point((byte)random(0, 3), (byte)random(0, 3), (byte)random(0, 3));
       sl = 1;
       ss = 0;
       displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
    }
    else
    {
      i = ss;
      for (j = 0 ; j < sl ; j++)
      {
        k = points[i].Pos();
        if (displayFrame[k] > 0)
          --displayFrame[k];
        if (displayFrame[k] > 0)
          --displayFrame[k];

        if (i == 0)
          i = SNAKELEN - 1;
        else
          --i;
      }
      
      if (++ss >= SNAKELEN)
        ss = 0;
      
      points[ss].Set(nx, ny, nz);
      displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
      
      if (sl < SNAKELEN)
        ++sl;
    }
  }
  
  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVRain.cpp
 * Description:
 *     Movie -- mv_Rain
 *         A number of LEDs are lit on the top plane, and they
 *         then "fall down" the cude.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

class Drop
{
  public:
    byte pos;
    byte count;
    unsigned long time;
    byte spd;
    
  void reset()
  {
    pos = POS(2, random(0, 3), random(0, 3));
    count = 2;
    spd = random(30, 100);
    time = millis() + spd;
  }
  
  void displayDrop(boolean state)
  {
    displayFrame[pos] = state ? MAXBRIGHTNESSLEVEL : 0;
  }
  
  void drop()
  {
    if (millis() > time)
    {
      if (spd == 0)
      {
        reset();
        displayDrop(true);
      }
      else
      {
        displayDrop(false);
        if (count == 0)
        {
          spd = 0;
          time = millis() + random(50, 100);
        }
        else
        {
          --count;
          pos -= 3;
          displayDrop(true);
          time = millis() + spd;
        }
      }
    }
  }
};

void mv_Rain()
{
  unsigned long ts;
  byte y;
  Drop drops[2];
  
  ts = millis() + (DEF_MOVIE_TIME * 2);

  for (y = 0 ; y < 2 ; y++)
  {
    drops[y].reset();
    drops[y].displayDrop(true);
  }
 
  while (millis() < ts)
  {
    for (y = 0 ; y < 2 ; y++)
    {
      drops[y].drop();
    }
  }

  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVPulse.cpp
 * Description:
 *     Movie -- mv_Pulse
 *         Fade in and out all LEDs together.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Pulse()
{
  unsigned long ts;
  byte x, y;
  
  ts = millis() + (DEF_MOVIE_TIME / 2);

  while (millis() < ts)
  {
    for (y = 0 ; y <= MAXBRIGHTNESSLEVEL ; y++)
    {
      for (x = 0 ; x < 27 ; x++)
        displayFrame[x] = y;
        
      delay(50);
    }

    delay(100);
    
    for (y = MAXBRIGHTNESSLEVEL - 1 ; y > 0 ; y--)
    {
      for (x = 0 ; x < 27 ; x++)
        displayFrame[x] = y;
        
      delay(50);
    }

    df_clear();
    
    delay(500);
  }
}