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

4017 Decade Counter

Counter_4017.h

/* Project: 4017 Decade Counter Interfacing.
 * File: Counter_4017.h
 * Description: Interface with a 4017 decade counter.
 *     Uses the DirectIO library.
 *
 * 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 Counter_4017_h
#define Counter_4017_h

#include "Arduino.h"
#include "DirectIO.h"

class Counter_4017
{
  public:
    Counter_4017(byte clockPin, byte lastElementDetectPin);

    void clock();
    void clockPrime();
    void clockTrigger();
    void clear();
    boolean onLastElement();

  private:
    DIO_SetPinFPtr _clockPin;
    byte _lastElementDetectPin;
};

#endif

Counter_4017.cpp

/* Project: 4017 Decade Counter Interfacing.
 * File: Counter_4017.cpp
 * Description: Interface with a 4017 decade counter.
 *     Uses the DirectIO library.
 *
 * 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 merchantability or fitness of purpose.
 */

#include "Arduino.h"
#include "Counter_4017.h"

Counter_4017::Counter_4017(byte clockPin, byte lastElementDetectPin)
{
  pinMode(clockPin, OUTPUT);
  pinMode(lastElementDetectPin, INPUT);

  _clockPin = DIO_GetSetPinFunction(clockPin);
  _lastElementDetectPin = lastElementDetectPin;

  clear();
}

void Counter_4017::clock()
{
  _clockPin(false);
  _clockPin(true);
}

void Counter_4017::clockTrigger()
{
  _clockPin(true);
}

void Counter_4017::clockPrime()
{
  _clockPin(false);
}

void Counter_4017::clear()
{
  while (!onLastElement())
    clock();

  while (onLastElement())
    clock();
}

boolean Counter_4017::onLastElement()
{
  return (digitalRead(_lastElementDetectPin) == HIGH ? true : false);
}

keywords.txt

Counter_4017	KEYWORD1

clock	KEYWORD2
clear	KEYWORD2
onLastElement	KEYWORD2

Directly setting IO pins high or low

See 7495 Shift Register for an example of how these can be used.

DirectIO.h

/* Project: Direct digital pin access for Arduino UNO.
 * File: DirectIO.h
 * Description: Manipulate digital IO pins directly. Depending
 *     method used can be much faster than digitalWrite().
 *
 * 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 merchantability or fitness of purpose.
 */

#ifndef DirectIO_h
#define DirectIO_h

#include "Arduino.h"

/* These are about 4 times faster than digitalWrite(); */

void DIO_SetPin4(boolean);
void DIO_SetPin7(boolean);
void DIO_SetPin8(boolean);
void DIO_SetPin9(boolean);
void DIO_SetPin10(boolean);
void DIO_SetPin11(boolean);
void DIO_SetPin12(boolean);
void DIO_SetPin13(boolean);

void DIO_SetPinNull(boolean);

void DIO_SetPin(int pin, boolean state);

typedef void (*DIO_SetPinFPtr)(boolean);

DIO_SetPinFPtr DIO_GetSetPinFunction(byte);


/* These are about 30 times faster than using digitalWrite(); */

static inline void PIN4H() { PORTD = PORTD | B00010000; }
static inline void PIN4L() { PORTD = PORTD & B11101111; }

static inline void PIN7H() { PORTD = PORTD | B10000000; }
static inline void PIN7L() { PORTD = PORTD & B01111111; }

static inline void PIN8H() { PORTB = PORTB | B00000001; }
static inline void PIN8L() { PORTB = PORTB & B11111110; }

static inline void PIN9H() { PORTB = PORTB | B00000010; }
static inline void PIN9L() { PORTB = PORTB & B11111101; }

static inline void PIN10H() { PORTB = PORTB | B00000100; }
static inline void PIN10L() { PORTB = PORTB & B11111011; }

static inline void PIN11H() { PORTB = PORTB | B00001000; }
static inline void PIN11L() { PORTB = PORTB & B11110111; }

static inline void PIN12H() { PORTB = PORTB | B00010000; }
static inline void PIN12L() { PORTB = PORTB & B11101111; }

static inline void PIN13H() { PORTB = PORTB | B00100000; }
static inline void PIN13L() { PORTB = PORTB & B11011111; }


/* The inline functions above are as fast as the code below. */

#define PIN13Hi PORTB = PORTB | B00100000
#define PIN13Li PORTB = PORTB & B11011111

#define PIN13Ha asm("SBI 5, 5\n")
#define PIN13La asm("CBI 5, 5\n")

#endif

DirectIO.cpp

/* Project: Direct digital pin access for Arduino UNO.
 * File: DirectIO.cpp
 * Description: Manipulate digital IO pins directly. Depending
 *     method used can be much faster than digitalWrite().
 *
 * 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 merchantability or fitness of purpose.
 */

#include "Arduino.h"
#include "DirectIO.h"

/*
  Direct digital port manipulation using the PORT variables.
  About 4 times quicker than using digitalWrite().
*/

/* 
  Returns a pointer to the function that can turn set the specified port high or low.

    DIO_SetPinFPtr func;

    func = DIO_getSetPinFunction(4);
    func(true); // Set high.
    func(low); // Set low.

  You could, of course, just as easily call the DIO_set4 fuction directly.
*/

DIO_SetPinFPtr DIO_GetSetPinFunction(byte pin)
{
  switch(pin)
  {
    case 4:
      return DIO_SetPin4;
      break;
      
    case 7:
      return DIO_SetPin7;
      break;
      
    case 8:
      return DIO_SetPin8;
      break;
      
    case 9:
      return DIO_SetPin9;
      break;
      
    case 10:
      return DIO_SetPin10;
      break;
      
    case 11:
      return DIO_SetPin11;
      break;
      
    case 12:
      return DIO_SetPin12;
      break;

    case 13:
      return DIO_SetPin13;
  }

  return DIO_SetPinNull;
}

void DIO_SetPinNull(boolean state)
{
  // Do nothing
}

void DIO_SetPin4(boolean state)
{
  if (state)
    PIN4H();
  else
    PIN4L();
}

void DIO_SetPin7(boolean state)
{
  if (state)
    PIN7H();
  else
    PIN7L();
}

void DIO_SetPin8(boolean state)
{
  if (state)
    PIN8H();
  else
    PIN8L();
}

void DIO_SetPin9(boolean state)
{
  if (state)
    PIN9H();
  else
    PIN9L();
}

void DIO_SetPin10(boolean state)
{
  if (state)
    PIN10H();
  else
    PIN10L();
}

void DIO_SetPin11(boolean state)
{
  if (state)
    PIN11H();
  else
    PIN11L();
}

void DIO_SetPin12(boolean state)
{
  if (state)
    PIN12H();
  else
    PIN12L();
}

void DIO_SetPin13(boolean state)
{
  if (state)
    PIN13H();
  else
    PIN13L();
}

void DIO_SetPin(int pin, boolean state)
{
  if (state)
  {
    switch(pin)
    {
      case 4:
        PIN4H();
        break;
			 
      case 7:
        PIN7H();
        break;
		     
      case 8:
        PIN8H();
        break;
		     
      case 9:
        PIN9H();
        break;
		     
      case 10:
        PIN10H();
        break;
		     
      case 11:
        PIN11H();
        break;
		     
      case 12:
        PIN12H();
        break;
		     
      case 13:
        PIN13H();
        break;
    }
  }
  else
  {
    switch(pin)
    {
      case 4:
        PIN4L();
        break;
			 
      case 7:
        PIN7L();
        break;
		     
      case 8:
        PIN8L();
        break;
		     
      case 9:
        PIN9L();
        break;
		     
      case 10:
        PIN10L();
        break;
		     
      case 11:
        PIN11L();
        break;
		     
      case 12:
        PIN12L();
        break;
		     
      case 13:
        PIN13L();
        break;
    }
 }	    
}

keywords.txt

ShiftReg_7495	KEYWORD1

DIO_SetPin4	KEYWORD2
DIO_SetPin7	KEYWORD2
DIO_SetPin8	KEYWORD2
DIO_SetPin9	KEYWORD2
DIO_SetPin10	KEYWORD2
DIO_SetPin11	KEYWORD2
DIO_SetPin12	KEYWORD2
DIO_SetPin13	KEYWORD2

DIO_SetPin	KEYWORD2

PIN4L	KEYWORD2
PIN4H	KEYWORD2

PIN7L	KEYWORD2
PIN7H	KEYWORD2

PIN8L	KEYWORD2
PIN8H	KEYWORD2

PIN9L	KEYWORD2
PIN9H	KEYWORD2

PIN10L	KEYWORD2
PIN10H	KEYWORD2

PIN11L	KEYWORD2
PIN11H	KEYWORD2

PIN12L	KEYWORD2
PIN12H	KEYWORD2

PIN13L	KEYWORD2
PIN13H	KEYWORD2

SW7495 4-Bit (non-latching) Shift Register

Library for sending data to the 7495 shift register. This is a simple 4-bit non-latching type which can work in parallel or serial mode.

This uses the Direct IO library to speed up the setting of the pins used to control the chip.

ShiftReg_7495.h

/* Project: 7495 4-Bit Shift Register Interfacing.
 * File: Shift_7495.h
 * Description: Interface with a 7495 4-bit shift register in
 *     parallel or serial mode.
 *     Uses the DirectIO library.
 *
 * 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 merchantability or fitness of purpose.
 */
 
#ifndef ShiftReg_7495_h
#define ShiftReg_7495_h

#include "Arduino.h"
#include "DirectIO.h"

class ShiftReg_7495
{
  public:
    ShiftReg_7495(byte clockPin, byte dataPin); // Serial mode.
    ShiftReg_7495(byte clockPin, byte aPin, byte bPin, byte cPin); // Parallel mode.

    void setBit(byte bit, boolean state);
    void send();
    void sendPrime();
    void sendTrigger();
    void clear();

  private:
    DIO_SetPinFPtr _clockPin;
    DIO_SetPinFPtr _dataPin;
    
    DIO_SetPinFPtr _aPin;
    DIO_SetPinFPtr _bPin;
    DIO_SetPinFPtr _cPin;

    boolean _currBits[3];
    boolean _newBits[3];

    boolean _serialMode;

    void sendBit(boolean state);
};

#endif

ShiftReg_7495.cpp

/* Project: 7495 4-Bit Shift Register Interfacing.
 * File: Shift_7495.cpp
 * Description: Interface with a 7495 4-bit shift register in
 *     parallel or serial mode.
 *     Uses the DirectIO library.
 *
 * 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 merchantability or fitness of purpose.
 */
 
#include "Arduino.h"
#include "ShiftReg_7495.h"
#include "DirectIO.h"

// Constructor for serial mode.

ShiftReg_7495::ShiftReg_7495(byte clockPin, byte dataPin)
{
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  _clockPin = DIO_GetSetPinFunction(clockPin);
  _dataPin = DIO_GetSetPinFunction(dataPin);

  _clockPin(true);

  _serialMode = true;

  clear();
}

// Constructor for parallel mode.

ShiftReg_7495::ShiftReg_7495(byte clockPin, byte aPin, byte bPin, byte cPin)
{
  pinMode(clockPin, OUTPUT);
  pinMode(aPin, OUTPUT);
  pinMode(bPin, OUTPUT);
  pinMode(cPin, OUTPUT);

  _clockPin = DIO_GetSetPinFunction(clockPin);
  _aPin = DIO_GetSetPinFunction(aPin);
  _bPin = DIO_GetSetPinFunction(bPin);
  _cPin = DIO_GetSetPinFunction(cPin);

  _clockPin(true);

  _serialMode = false;

  clear();
}

void ShiftReg_7495::setBit(byte bit, boolean state)
{
  _newBits[bit] = state;
}

// Send the data to the 7495 - Serial or parallel.

void ShiftReg_7495::send()
{
  if (_serialMode)
  {
    if (_currBits[0] == _newBits[0] && _currBits[1] == _newBits[1] && _currBits[2] == _newBits[2])
      ; // Don't need to do anything, the register aleady contains the bits we are going to show.
    else
    {
      if (_newBits[2] == _currBits[1] && _newBits[1] == _currBits[0])
        sendBit(_newBits[0]);
      else
      {
        if (_newBits[2] != _currBits[0])
          sendBit(_newBits[2]);

        sendBit(_newBits[1]);
        sendBit(_newBits[0]);
      }

      _currBits[0] = _newBits[0];
      _currBits[1] = _newBits[1];
      _currBits[2] = _newBits[2];
    }
  }
  else
  {
    sendPrime();
    sendTrigger();
  }
}

void ShiftReg_7495::sendPrime()
{
  _clockPin(true);

  _aPin(_newBits[0]);
  _bPin(_newBits[1]);
  _cPin(_newBits[2]);
}

void ShiftReg_7495::sendTrigger()
{
  _clockPin(false);
}

void ShiftReg_7495::sendBit(boolean state)
{
  _clockPin(true);
  _dataPin(state);
  _clockPin(false);
}

void ShiftReg_7495::clear()
{
  for (byte i = 0 ; i < 3 ; i++)
  {
    _currBits[i] = true;
    _newBits[i] = false;
  }

  send();
}

keywords.txt

ShiftReg_7495	KEYWORD1

setBit	KEYWORD2
send	KEYWORD2
sendPrime	KEYWORD2
sendTrigger	KEYWORD2
clear	KEYWORD2

Some Arduino code that I have written for various projects:-