PICBASIC and the SAA1064 Seven-Segment Module

A while back I purchased an I2C compatible 4-digit seven-segment module on eBay for next to nothing. The description said arduino compatible, so getting it to work with PICS should not be a problem.  The display module is based on NXP SAA1064 I2C controller.  The datasheet for the SAA1064 can be found here.  The modules I2C device address is factory-set at %01110000, or $70.  Initially, I2CWRITE seemed like the logical choice for getting the device to work.  For some reason I was unable to get I2CWRITE to work, and had to manually send commands via the SHIFTIN/SHIFOUT commands.  After the 16F88 was up and running on the breadboard, went about hooking up the module.  There are 4 pins on the board: VCC, SDA,SCL,GND.  VCC and GND were connected to their respective pins.  The SDA and SCL pins were then connected to PICs B.2 and B.3 pins with pullup resistors on both pins. 

Before the module can display anything the SAA1064 has to be initialized.   

First the I2c communication has to be started, which accomplished by calling the subroutine I2C_START:

 I2C_START:              
    HIGH SDA
    HIGH SCL
    LOW SDA
   RETURN

The SDA and SCL pins are both set HIGH, then the SDA is brought LOW, which lets the I2C bus know data is comming.

The SAA1064 expects the next byte to be the device byte, followed by the instruction byte, followed by either a control or data byte.  When the circuit is first powered on the SAA1064 must be initalized.   So first the device byte is sent:

i2c_out = %01110000 : GOSUB I2C_TX

 The program contains a variable i2c_out, that gets loaded with the byte to be shifted out.  The subroutine I2C_TX is then used to shift that data out:

I2C_TX:                           
    SHIFTOUT SDA,SCL,1,[i2c_out]  
    SHIFTIN SDA,SCL,0,[i2c_ack\1]          
    RETURN

SHIFTOUT is used to send the i2c_out byte, MSB first.  Next SHIFTIN is used to recieve the ACK bit from the SAA1064.

Next the instruction byte needs to be sent.  Refer to the datasheet for bit settings of the instruction byte.

 

The instruction byte is like a pointer to where the next byte is put on the SAA1064.  A value of %00000000 sets the pointer to the control register.  So the i2c_out is loaded with %00000000 and shifted out:

i2c_out = %00000000 : GOSUB I2C_TX

Now that the pointer is set to the control register, the control byte can be sent.  The control byte allows different settings of the chip to be used.  A value of %00010111 turns on dynamic mode, turn off blanking on segments 1-4, and sets the segment power at 3mA.  So again the i2c_out variable is loaded with  %00010111 and shifted out:

i2c_out = %00010111 : GOSUB I2C_TX       

Now that the control byte is sent, the I2C communication can be ended by calling the I2C_STOP subroutine:

I2C_STOP:
    HIGH SCL
    HIGH SDA
    PAUSE 1
   RETURN

The I2C_STOP subroutine brings the SDA pin high again which ends the I2C transmission.  Now the display is set up and is ready to use.  A simple example of use:

First use LOOKUP to translate segment bits into actual decimal numbers.  This is where things could be different depending on who built your module.  Mine came with a segment key:  


 
0 = b
1 = DP
2 = a
3 = c
4 = e
5 = d
6 = g
7 = f

 

 

So using a lookup table, bits can be translated into digits:

LOOKUP i, [189,9,117,109,201,236,248,13,253,205],seg_val

If the value (4) is loaded into the i variable, then the LOOKUP command would load (201) into the seg_val variable.  (201) is the decimal equvilant of %11001001, which turns on segments f,g,c,and b; or the number (4) on the display. Now the value can be sent to the display:

GOSUB I2C_START
   i2c_out = %01110000 : gosub i2C_tx 'device byte is sent
   i2c_out = %00000001 : gosub i2C_tx 'instruction byte is set to point to digit 1
   i2c_out = seg_val : gosub i2c_tx 'seg_val is sent to display
GOSUB I2C_STOP

First the I2C transmission is started.  Then the device byte is sent, followed by the  instruction byte %00000001, which sets the pointer to the first digit.  Then seg_val is then sent.  Finally the I2C transmission is stopped, and the number 4 should be displayed. 

I cant seem to get the auto increment to work, so each digit has to be pointed to manually with the instruction byte.

Here is the entire PICBASIC code for a PIC16F88, which counts from 0-9, on each of the digits:

-------------------------------------------------------------------------------
'// for 4MHZ
DEFINE OSC 4
OSCCON=%01101000
'// for 8MHZ
'DEFINE OSC 8
'OSCCON=%01111000
While OSCCON.2=0:Wend
CMCON         =  7                 '// PORTA = digital I/O
'OPTION_REG.7  =  0                 '// Enable PORTB pull-ups
ADCON1        =  7
ANSEL=%00000000                    '// set all analog pins to digital
ANSEL = 0                          '// disable ADCs
TRISB.3 = 0
TRISB.2 = 0
'----------------------------------------------------------------------------
led         var PORTA.1
SDA         VAR PORTB.3
SCL         VAR PORTB.2

i2c_out     VAR BYTE        'data to sent over I2C bus
i2c_ack     VAR BIT         'acknowledgement bit
i           var byte
segment     VAR byte
seg_val     var byte
'----------------------------------------------------------------------------
gosub startup 
GOSUB init_display   'initialize the display module
main:
   GOSUB COUNTER
   goto main
   END
    
counter:
    for i = 0 to 9
        lookup i, [189,9,117,109,201,236,248,13,253,205],seg_val
        for segment = 1 to 4
              GOSUB I2C_START
              if segment = 1 then
                 i2c_out = %01110000 : gosub i2C_tx : i2c_out = %00000001 : gosub i2C_tx
                 i2c_out = seg_val : gosub i2c_tx
                 gosub i2c_stop
              endif
              if segment = 2 then
                 i2c_out = %01110000 : gosub i2C_tx : i2c_out = %00000010 : gosub i2C_tx
                 i2c_out = seg_val : gosub i2c_tx
                 gosub i2c_stop
              endif
              if segment = 3 then    
                 i2c_out = %01110000 : gosub i2C_tx : i2c_out = %00000011 : gosub i2C_tx
                 i2c_out = seg_val : gosub i2c_tx
                 gosub i2c_stop
              endif
              if segment = 4 then    
                 i2c_out = %01110000 : gosub i2C_tx : i2c_out = %00000100 : gosub i2C_tx
                 i2c_out = seg_val : gosub i2c_tx
                 gosub i2c_stop
              endif
          next segment
        pause 100 
      next i
    return    
init_display:           'initialize the SAA1064 module and flash all segments
    GOSUB I2C_START     'Start the I2C communication
       i2c_out = %01110000 : GOSUB I2C_TX        'send device byte
       i2c_out = %00000000 : GOSUB I2C_TX        'send instruction byte 
       i2c_out = %00011111 : GOSUB I2C_TX        'send control byte    
    GOSUB I2C_STOP      'Stop i2C transmission
    pause 1000          ' leave segments on for 1 sec then turn off
    
    GOSUB I2C_START
       i2c_out = %01110000 : gosub i2C_tx
       i2c_out = %00000000 : gosub i2C_tx
       i2c_out = %00000001 : GOSUB I2C_TX
    GOSUB I2C_STOP
    pause 500
    
    GOSUB I2C_START
       i2c_out = %01110000 : gosub i2C_tx
       i2c_out = %00000000 : gosub i2C_tx
       i2c_out = %00010111 : GOSUB I2C_TX
    GOSUB I2C_STOP
  RETURN
I2C_START:              
    HIGH SDA
    HIGH SCL
    LOW SDA
   RETURN
 
I2C_STOP:               'I2C stop (terminate communication on I2C bus)
    HIGH SCL
    HIGH SDA
    PAUSE 1
    RETURN
 
I2C_TX:                           'I2C transmit -> send data to the slave
    SHIFTOUT SDA,SCL,1,[i2c_out]  'Shift out “i2c_out” MSBfirst
    SHIFTIN SDA,SCL,0,[i2c_ack\1] 'Receive ACK bit          
    RETURN
--------------------------------------------------------------

Leave a comment