Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Anyone have experience with the 16F77?

Status
Not open for further replies.

Jon Wilder

Active Member
I'm coming to find that for simple MIDI switching stuff you really don't need anything higher level than the mid-range line of PIC MCUs. I didn't realize up until recently that Microchip made any sort of 40 pin uC with 4/5 ports within the mid-range line.

That being said, I'm designing a rather elaborate MIDI switching device that involves switching 8 transistor driven relays on/off as well as relay state indicator LEDs, a 4 digit LED display to indicate things such as assigned MIDI channel and current patch, and a 4 x 2 switch matrix.

I figure the relay transistors as well as the relay state LEDs will be latch driven by a 74HCT373 off of Port D. Port E can drive the latch enable pins on both. RA0 - RA5 will run the 4 x 2 switch matrix while port B drives the LED display and RC0 - RC3 drive the transistors which pulse the LED displays on/off.

Does the 16F77 sound like the ticket for this device?
 
No experience with the 16F77, but for driving the relays, consider the ULN2003. It's a "peripheral" driver with 8 sets of Darlington pair transistors, the base resistors and built-in doides for the back-emf of relays. It's a low side switch that will handle 300 mA per section, with a voltage of up to 42 volts I believe. Thus the relays could have 12 volt coils but be switched by a 5 volt logic level.
 
Last edited by a moderator:
Hi Jon Wilder,

I'd just like to mention a couple design alternatives. (1) Consider using one of many different serial-to-parallel driver ICs to drive the display. You only need 5 I/O pins for a 4-digit display with full fade-to-black PWM brightness control. (2) Consider using the display column driver lines to drive your 2x4 switch matrix since only one of those four lines is driven low during each digit display interval. You'll need 2 I/O pins to read the switch matrix. (3) Consider using an 8-bit serial-to-parallel relay driver IC like the MIC5841. Use the existing serial-to-parallel <clk> and <dat> lines and one additional I/O pin connected to the <lat> pin on the MIC5841.

Regards, Mike

mosaic-1-png.44791

Code:
/********************************************************************
 *                                                                  *
 *  Project: LED 4-Digit 595                                        *
 *   Source: LED_4-Digit_595.c                                      *
 *   Author: Mike McLaren, K8LH                                     *
 *  (C)2010: Micro Application Consultants                          *
 *     Date: 18-Jul-10                                              *
 *  Revised: 26-Dec-10, 6-bit gamma corrected brightness control    *
 *                                                                  *
 *  16F88 + TPIC6C595 + 4-Digit 7-Seg CA Display Demo (5 pin buss)  *
 *                                                                  *
 *                                                                  *
 *      IDE: MPLAB 8.56 (tabs = 4)                                  *
 *     Lang: SourceBoost BoostC v7.01, Lite/Free version            *
 *                                                                  *
 ********************************************************************/

#include <system.h>

#pragma DATA _CONFIG1, _CCP1_RB0&_LVP_OFF&_MCLR_OFF&_WDT_OFF&_INTRC_IO
#pragma DATA _CONFIG2, _IESO_OFF&_FCMEN_OFF

#pragma CLOCK_FREQ 8000000      // 8 MHz INTOSC

//--< function prototypes >------------------------------------------
//--< typedef and defines >------------------------------------------

typedef unsigned char u08;
typedef unsigned int u16;

#define r08 const rom unsigned char

#define clk portb.1             // the RB1 pin
#define dat portb.2             // the RB2 pin

//--< variables >----------------------------------------------------

u08 colsel = 2;                 // column select bit mask
u08 colnbr = 0;                 // colnbr, 0..3
u08 display[] = { 0,0,0,0 };    // display data, 0..9

u08 swnew = 0;                  // switch sample
u08 swold = 0;                  // switch state latch
u08 flags = 0;                  // switch flag bits

u08 work = 0;                   // isr work variable

r08 segdata[] = { 0b00111111,   // "0"   -|-|F|E|D|C|B|A
                  0b00000110,   // "1"   -|-|-|-|-|C|B|-
                  0b01011011,   // "2"   -|G|-|E|D|-|B|A
                  0b01001111,   // "3"   -|G|-|-|D|C|B|A
                  0b01100110,   // "4"   -|G|F|-|-|C|B|-
                  0b01101101,   // "5"   -|G|F|-|D|C|-|A
                  0b01111101,   // "6"   -|G|F|E|D|C|-|A
                  0b00000111,   // "7"   -|-|-|-|-|C|B|A
                  0b01111111,   // "8"   -|G|F|E|D|C|B|A
                  0b01101111,   // "9"   -|G|F|-|D|C|B|A
                  0b00000000,   // " "   -|-|-|-|-|-|-|-
                  0b01000000 }; // "-"   -|G|-|-|-|-|-|-

r08 gamma[] = { 250,249,248,248,248,247,247,247,246,246,
                246,245,245,244,243,243,242,241,240,239,
                239,238,236,235,234,233,231,230,228,226,
                224,222,220,218,215,213,210,207,204,200,
                197,193,189,185,180,175,170,164,158,152,
                145,138,131,123,114,105, 95, 85, 74, 63,
                 51, 38, 25, 10 };

//--< functions >----------------------------------------------------

void brightness(u08 level)      // gamma brightness level, 0..63
{ ccpr1l = gamma[level];        // pwm duty cycle, 10..250
}                               //

//--< main >---------------------------------------------------------

void main()
{
  ansel = 0;                    // a2d off, digital i/o
  osccon = 0b01110000;          // set INTOSC to 8 MHz
  while(!osccon.IOFS);          // wait 'til oscillator stable
  trisb = 0b11100000;           // set RB4..RB0 to outputs
  trisa = 0b00000000;           // set all pins to outputs
  portb = 0b00000000;           // set all output latches to '0'
  porta = 0b00000000;           // set all output latches to '0'

//  setup PWM for a 2 msec period (8 MHz Clock) for a 1/4th or 25%
//  display duty cycle and a 125 Hz (8 msec) display refresh rate

  ccp1con = 0b00001100;         // '00------' unimplemented bits
                                // '--00----' CCP1X:CCP1Y (pwm)
                                // '----1100' pwm mode, active hi
  ccpr1l = 250;                 // 100% duty cycle, 0% brightness
  tmr2 = 0;                     // clear Timer 2 register
  t2con = 0b00000110;           // '0-------' unimplemented bit
                                // '-0000---' TOUTPS<3:0>, postscale 1
                                // '-----1--' TMR2ON, turn Timer 2 on
                                // '------10' T2CKPS<1:0>, prescale 16
  pr2 = 250-1;                  // 250 x 8-usec 'ticks' = 2 msecs
  pir1 = 0;                     // clear peripheral interrupt flags
  pie1.TMR2IE = 1;              // set Timer 2 interrupt enable bit
  intcon = 0b11000000;          // '1-------' GIE, enable global and
                                // '-1------' PEIE, peripheral ints
                                // '--0-----' T0IE, TMR0 ints off
                                // '---0----' INTE, off
                                // '----0---' GPIE, IOC disabled
                                // '-----000' T0IF/INTF/GPIF flags
  while(1)
  {
    brightness(32);             // brightness range 0..63

    display[0] = 0;             // display "0123"
    display[1] = 1;             //
    display[2] = 2;             //
    display[3] = 3;             //
  }
}

/********************************************************************
 *  interrupt service routine                                       *
 ********************************************************************/

void interrupt()                // 2-msec Timer 2 interrupts
{ pir1.TMR2IF = 0;              // clear timer 2 interrupt flag
 /*                                                                 *
  *  retask the column driver lines while PWM is hi (display off)   *
  *  as <clk> and <dat> lines to load the display shift register    *
  *  then write new column select bit pattern to portb before PWM   *
  *  goes lo (display on) to resume RB4-RB1 column driver duties.   *
  *                                                                 */
  for(u08 i = 0; i < 8; i++)    // load the driver shift register
  { clk = 0; dat = 0;           // clk = 0, dat = 0
    if(work.7) dat = 1;         // if bit == 1 then dat = 1
    clk = 1; work >>= 1;        // send bit, shift 'work' var
  }
  portb = ~colsel;              // select new column
 /*                                                                 *
  *  sample and debounce each switch at 8-msec debounce intervals   *
  *                                                                 *
  *  swnew  ___---___---___---___  sample active lo switches        *
  *  swold  ____---___---___---__  switch state latch               *
  *  swnew  ___-__-__-__-__-__-__  changes, press or release        *
  *  swnew  ___-_____-_____-_____  filter out 'release' bits        *
  *  flags  ___------______------  toggle flag bits for main        *
  *                                                                 */
  swnew = swold | colsel;       // set bit to '1' (pressed)
  if(portb.5)                   // if not pressed
    swnew ^= colsel;            // set bit to '0' (released)
  swnew ^= swold;               // changes, press or release
  swold ^= swnew;               // update switch state latch
  swnew &= swold;               // filter out 'release' bits
  flags ^= swnew;               // toggle flag bits for main
 /*                                                                 *
  *  prepare for next column update interrupt                       *
  *                                                                 */
  if(colnbr == 3)               // if last column
  { colnbr = 0;                 // reset column number
    colsel = 2;                 // reset column select for RB1
  }
  else                          // else, not last column, so
  { colnbr++;                   // bump column number, 0..3
    colsel <<= 1;               // bump column select bit mask
  }
  work = segdata[display[colnbr]];
}
 
Last edited:
The 16f77 is a easy to use chip It has more then enough pin to do the job I would use Mikes design with it you'll end up with a nice display
 
No experience with the 16F77, but for driving the relays, consider the ULN2003.

Sorry to nitpick Jon, but the ULN2003/2004 has 7 transistors, the ULN2803/2804 has 8 transistors (I thought it was important to mention before the OP buys one).

And I agree it's an ideal solution for driving 8 relays. :)
 
Sorry to nitpick Jon, but the ULN2003/2004 has 7 transistors, the ULN2803/2804 has 8 transistors (I thought it was important to mention before the OP buys one).

And I agree it's an ideal solution for driving 8 relays. :)

I never could figure why they made it with 7 transistors maybe it's just a 5x7 world LOL
 
They were originally made for driving print heads on impact printers. Back in the olden days of Epson MX80s, the print head had a row of 7 pins. I believe that's why there are 7 drivers in the package.

Go ahead and nitpick RB. My mistake, sorry about that. But 2 ULN2003's is still easier than 8 transistors, 8 base resistors and 8 EMF diodes :)
 
They were originally made for driving print heads on impact printers. Back in the olden days of Epson MX80s, the print head had a row of 7 pins. I believe that's why there are 7 drivers in the package.

Go ahead and nitpick RB. My mistake, sorry about that. But 2 ULN2003's is still easier than 8 transistors, 8 base resistors and 8 EMF diodes :)

You have watch out for them 8 EMF diodes some chips don't have them

Make sure the data sheet has this in it
Integral suppression diodes for inductive loads
 
Last edited:
I think all of the variants have the clamp diodes Burt. I think that's part of the spec for the ULN2003/ULN2803 chips. I know a bunch of companies make these.

Here is the data sheet for TI ULN2308A, and clamp diodes are listed in the features section in big print right at the top of the data sheet. STMicro also makes the chip and it too has the diodes.

One caution however. The clamp diodes are connected to a common terminal which must be connected to the + voltage source to work.
 
Think all you want not all do It's better to make sure that the data sheet tells you
Integral suppression diodes for inductive loads
I really don't see why you want to make a big deal Rb pointed out the OP may want one with 8 transistors in it. And I pointed out that the OP needs to make sure it has suppression diodes for inductive loads

And there are some darlington array chips that don't have suppression diodes for inductive loads

So I figured I would inform the OP of that
 
Last edited:
Burt, the ULN2003 and ULN2803 are the same chip with a different number of sections. RB pointed out that I had given the wrong part number for the 8 section chip.

Sure, there are many other buffer chips, some with diodes, some without. Some won't have open collector outputs and won't switch high voltages. But my comments specifically cover the 2 chips mentioned in the discussion.
 
John I know what a ULN2003 and ULN2803 are That's not the point He may use some thing else besides the ULN2003

So that's why I said make sure it has Integral suppression diodes for inductive loads The ULN2003 is a darlington array chip

It not the only one in the world I have used a bunch some have suppression diodes some don't and there is a ULNxxxx chip I don't remember witch that didn't have diodes in it it been years back.

This is not about Burt Ratliff Jon so lets leave it at that.
 
Thanks for all the ideas guys! The relays I'll be driving are the Omron G5V-2-H1-DC12V. These are a high sensitivity small signal relay as this device will simply be switching line level audio signals in/out of a signal path. The coil current on them is only about 12.5mA on the 12V version.

Originally I was going to do a discrete transistor/base resistor/back EMF diode setup as with the board having to have PCB mount 1/4" jacks there would be plenty of physical real estate for the components. However, the ULN2003 looks like it would be the perfect integrated device for this as there will be two relay boards and each board will host 4 of the 8 relays.

@Mike...in regards to the driver ICs...I like that idea. However, those appear to work on a serial data input. I'm assuming I would have to bit bang a serial routine off of another pin in order to send data to them (please correct me if there's something I'm not seeing here)? Also, I noticed you included an ISR written in C language. However, not only have I never programmed in C, I wouldn't even know where to start lol. Would love to learn it though as it would definitely save me some coding time.

Also...in regards to bit banging...I've never written a bit bang routine and I've tried googl'ing serial bit banging for PICs and haven't found what I'm looking for so if someone could direct me to some sample bit bang routine codes and/or reference sites where I could read up on it I would greatly appreciate it.
 
... I've never written a bit bang routine and I've tried googl'ing serial bit banging for PICs and haven't found what I'm looking for so if someone could direct me to some sample bit bang routine codes and/or reference sites where I could read up on it I would greatly appreciate it.

Hi Jon,

Here's the assembly language version of that program, minus gamma correction. I apologize if the code seems a bit deep.

Good luck on your project. Cheerful regards, Mike

Code:
;******************************************************************
;*                                                                *
;*  Filename: 16F88 4-Digit.asm                                   *
;*    Author: Mike McLaren, K8LH                                  *
;*   (C)2010: Micro Application Consultants                       *
;*      Date: 18-Jul-10                                           *
;*                                                                *
;*   Mux'd 4-digit display & switch demo for Mosaic               *
;*                                                                *
;*     MPLab: 8.50    (tabs=8)                                    *
;*     MPAsm: 5.35                                                *
;*                                                                *
;******************************************************************

        #include <p16f88.inc>
        errorlevel -302
        list st=off
        radix   dec

  __CONFIG  _CONFIG1, _CCP1_RB0&_LVP_OFF&_MCLR_OFF&_WDT_OFF&_INTRC_IO
  __CONFIG  _CONFIG2, _IESO_OFF & _FCMEN_OFF

;--< variables >---------------------------------------------------

        cblock  0x20
disp:4                          ; char disp[4]
colnbr                          ; char colnbr, 0..3
colsel                          ; char colsel, bit mask
bitctr                          ; general purpose counter
work                            ; isr work variable
beep                            ; beep msec counter

latch                           ; switch state latch
flags                           ; switch flag bits
        endc
        cblock  0x70
w_isr                           ;
s_isr                           ;
p_isr                           ;
f_isr                           ;
        endc

;--< constants >---------------------------------------------------

spkr    equ     6               ; speaker bit index (RB6)

#define clk     PORTB,1         ; TPIC6C595 <clk> on RB1
#define dat     PORTB,2         ; TPIC6C595 <dat> on RB2
#define swx     PORTB,5         ; switch matrix input (RB5)

;******************************************************************
;  reset vector                                                   *
;******************************************************************
        org     0x000
vReset
        clrf    STATUS          ; force bank 0, IRP 0             |B0
        goto    init            ;                                 |B0

;******************************************************************
;  interrupt vector                                               *
;******************************************************************
        org     0x004
vInt
        movwf   w_isr           ; save WREG                       |B?
        swapf   STATUS,W        ; don't change STATUS bits        |B?
        movwf   s_isr           ; save STATUS                     |B?
        clrf    STATUS          ; bank 0                          |B0
        movf    PCLATH,W        ;                                 |B0
        movwf   p_isr           ; save PCLATH                     |B0
        movf    FSR,W           ;                                 |B0
        movwf   f_isr           ; save FSR                        |B0
        bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag    |B0
;
;  re-task RB4:RB1 column driver lines as <clk> and <dat> lines
;  while PWM is high (display off) to load the TPIC6C595 driver
;  shift register then write new column select bit pattern onto
;  the pins to resume column driver duties before PWM goes low.
;
        movlw   8               ;                                 |B0
        movwf   bitctr          ; bitctr = 8                      |B0
loadSR  bcf     clk             ; clk = 0                         |B0
        bcf     dat             ; dat = 0                         |B0
        btfsc   work,7          ; bit = 1? no, skip, else         |B0
        bsf     dat             ; dat = 1                         |B0
        bsf     clk             ; clk = 1, shift bit into SR      |B0
        rlf     work,F          ; shift 'work'                    |B0
        decfsz  bitctr,F        ; done? yes, skip, else           |B0
        goto    loadSR          ; loop                            |B0
        movlw   b'00011110'     ;                                 |B0
        iorwf   PORTB,W         ; turn off column select bits     |B0
        xorwf   colsel,W        ; active lo column select bit     |B0
        movwf   PORTB           ; select new column               |B0
;
;  beep task (32-msec 500-Hz tone @ 1-msec interrupts)
;
isrBeep
        movf    beep,W          ; beep task running?              |B0
        bz      isrSw           ; no, branch, else                |B0
        movlw   1<<spkr         ; speaker pin mask                |B0
        xorwf   PORTB,F         ; toggle speaker pin              |B0
        decf    beep,F          ; decrement beep msec counter     |B0
;
;  switch management (sample switch at current selected column)
;
;   wreg ___---___---___---____  sample active lo switches
;  latch ____---___---___---___  switch state latch
;   wreg ___-__-__-__-__-__-___  changes, press or release
;   wreg ___-_____-_____-______  filter out 'release' bits
;  flags ___------______-------  toggle flag bits for main
;
isrSw
        movf    latch,W         ; W = switch state latch          |B0
        iorwf   colsel,W        ; set SWn bit to '1' (pressed)    |B0
        btfsc   PORTB,5         ; pressd (0)? yes, skip, else     |B0
        xorwf   colsel,W        ; set SWn bit to '0' (released)   |B0
        xorwf   latch,W         ; W = changes, press or release   |B0
        xorwf   latch,F         ; update switch state latch       |B0
        andwf   latch,W         ; filter out "new release" bits   |B0
        skpz                    ; a "new press"? no, skip, else   |B0
        bsf     beep,5          ; task a new press beep           |B0
        xorwf   flags,F         ; toggle switch flags for main    |B0
;
;  prep for next column update interrupt
;
isrNext
        incf    colnbr,F        ; bump column number              |B0
        bcf     colnbr,2        ; 0..3 inclusive                  |B0
        clrc                    ;                                 |B0
        rlf     colsel,W        ; advance column select bit       |B0
        btfsc   colsel,4        ; last column? no, skip, else     |B0
        movlw   b'00000010'     ; reset to first column           |B0
        movwf   colsel          ;                                 |B0
        movlw   disp            ; W = &disp[0]                    |B0
        addwf   colnbr,W        ; add column number, 0..3         |B0
        movwf   FSR             ; FSR = &disp[colnbr]             |B0
        movf    INDF,W          ; W = disp[colnbr], 0..9          |B0
        call    SegTable        ; get segment data                |B0
        movwf   work            ; SR data for next column         |B0
;
;  restore context
;
        movf    f_isr,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        movf    p_isr,W         ;                                 |B0
        movwf   PCLATH          ; restore PCLATH                  |B0
        swapf   s_isr,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   w_isr,f         ; don't screw up STATUS           |B?
        swapf   w_isr,W         ; restore WREG                    |B?
        retfie                  ; return from interrupt           |B?

;******************************************************************

SegTable
        addwf   PCL,F           ;                                 |B0
        retlw   b'00111111'     ; "0"   -|F|E|D|C|B|A             |B0
        retlw   b'00000110'     ; "1"   -|-|-|-|C|B|-             |B0
        retlw   b'01011011'     ; "2"   G|-|E|D|-|B|A             |B0
        retlw   b'01001111'     ; "3"   G|-|-|D|C|B|A             |B0
        retlw   b'01100110'     ; "4"   G|F|-|-|C|B|-             |B0
        retlw   b'01101101'     ; "5"   G|F|-|D|C|-|A             |B0
        retlw   b'01111101'     ; "6"   G|F|E|D|C|-|A             |B0
        retlw   b'00000111'     ; "7"   -|-|-|-|C|B|A             |B0
        retlw   b'01111111'     ; "8"   G|F|E|D|C|B|A             |B0
        retlw   b'01101111'     ; "9"   G|F|-|D|C|B|A             |B0

;******************************************************************
;  main init                                                      *
;******************************************************************

init
        bsf     STATUS,RP0      ; bank 1                          |B1
        clrf    ANSEL           ; turn ADC functions off          |B1
        movlw   b'01110000'     ;                                 |B1
        movwf   OSCCON          ; select 8-MHz INTOSC clock       |B1
Stable  btfss   OSCCON,IOFS     ; Int Osc Freq Stable bit set?    |B1
        goto    Stable          ; no, branch and wait             |B1
;
;  setup various ISR, Port, Switch, Clock, and Display variables
;
        clrf    TRISA           ;                                 |B1
        movlw   b'11100000'     ;                                 |B1
        movwf   TRISB           ;                                 |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        movlw   b'00011110'     ;                                 |B0
        movwf   PORTB           ; turn column select lines off    |B0
;
;  setup CCP/TMR2 "PWM" mode with a 2-msec period (8-MHz INTOSC)
;
        clrf    TMR2            ; clear TMR2 register             |B0
        bsf     STATUS,RP0      ; select Bank 1                   |B1
        clrf    PIE1            ; mask all peripheral irqs        |B1
        bsf     PIE1,TMR2IE     ; except for TMR2 irqs            |B1
        movlw   d'250'-1        ; 250 x 8 usec 'ticks'            |B1
        movwf   PR2             ; for 1-msec interrupts           |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        clrf    PIR1            ; clear peripheral irq flags      |B0
        movlw   b'00000010'     ; pre 16, post 1, TMR2 off        |B0
                                ;    0-------  unused
                                ;    -0000---  postscale 1
                                ;    -----0--  TMR2 off
                                ;    ------10  prescaler 16
        movwf   T2CON           ; for 8 usec ticks (8-MHz clock)  |B0
        movlw   b'00001100'     ; setup CCP module for "PWM"      |B0
                                ;    00------  unused
                                ;    --00----  DC1B1:DC1B0
                                ;    ----1100  PWM mode
        movwf   CCP1CON         ; turn on PWM mode                |B0
        movlw   120             ; 50% brightness                  |B0
        movwf   CCPR1L          ; 20% duty cycle                  |B0
        bsf     INTCON,GIE      ; enable global irqs              |B0
        bsf     INTCON,PEIE     ; enable peripheral irqs          |B0
;
;  initialize program variables
;
        clrf    disp+0          ; disp[0] = 0                     |B0
        clrf    disp+1          ; disp[1] = 0                     |B0
        clrf    disp+2          ; disp[2] = 0                     |B0
        clrf    disp+3          ; disp[3] = 0                     |B0
        clrf    colnbr          ; colnbr = 0                      |B0
        movlw   2               ;                                 |B0
        movwf   colsel          ; colsel = b'00000010'            |B0
        clrf    bitctr          ; bitctr = 0                      |B0
        clrf    beep            ; beep = 0                        |B0
        clrf    latch           ;                                 |B0
        clrf    flags           ;                                 |B0
        bsf     T2CON,TMR2ON    ; start TMR2 (interrupts)         |B0

;******************************************************************
;  main loop                                                      *
;******************************************************************

loop
        goto    loop            ;                                 |B0

;******************************************************************
        end
 
Hi Jon,

Here's the assembly language version of that program, minus gamma correction. I apologize if the code seems a bit deep.

Good luck on your project. Cheerful regards, Mike

Code:
;******************************************************************
;*                                                                *
;*  Filename: 16F88 4-Digit.asm                                   *
;*    Author: Mike McLaren, K8LH                                  *
;*   (C)2010: Micro Application Consultants                       *
;*      Date: 18-Jul-10                                           *
;*                                                                *
;*   Mux'd 4-digit display & switch demo for Mosaic               *
;*                                                                *
;*     MPLab: 8.50    (tabs=8)                                    *
;*     MPAsm: 5.35                                                *
;*                                                                *
;******************************************************************

        #include <p16f88.inc>
        errorlevel -302
        list st=off
        radix   dec

  __CONFIG  _CONFIG1, _CCP1_RB0&_LVP_OFF&_MCLR_OFF&_WDT_OFF&_INTRC_IO
  __CONFIG  _CONFIG2, _IESO_OFF & _FCMEN_OFF

;--< variables >---------------------------------------------------

        cblock  0x20
disp:4                          ; char disp[4]
colnbr                          ; char colnbr, 0..3
colsel                          ; char colsel, bit mask
bitctr                          ; general purpose counter
work                            ; isr work variable
beep                            ; beep msec counter

latch                           ; switch state latch
flags                           ; switch flag bits
        endc
        cblock  0x70
w_isr                           ;
s_isr                           ;
p_isr                           ;
f_isr                           ;
        endc

;--< constants >---------------------------------------------------

spkr    equ     6               ; speaker bit index (RB6)

#define clk     PORTB,1         ; TPIC6C595 <clk> on RB1
#define dat     PORTB,2         ; TPIC6C595 <dat> on RB2
#define swx     PORTB,5         ; switch matrix input (RB5)

;******************************************************************
;  reset vector                                                   *
;******************************************************************
        org     0x000
vReset
        clrf    STATUS          ; force bank 0, IRP 0             |B0
        goto    init            ;                                 |B0

;******************************************************************
;  interrupt vector                                               *
;******************************************************************
        org     0x004
vInt
        movwf   w_isr           ; save WREG                       |B?
        swapf   STATUS,W        ; don't change STATUS bits        |B?
        movwf   s_isr           ; save STATUS                     |B?
        clrf    STATUS          ; bank 0                          |B0
        movf    PCLATH,W        ;                                 |B0
        movwf   p_isr           ; save PCLATH                     |B0
        movf    FSR,W           ;                                 |B0
        movwf   f_isr           ; save FSR                        |B0
        bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag    |B0
;
;  re-task RB4:RB1 column driver lines as <clk> and <dat> lines
;  while PWM is high (display off) to load the TPIC6C595 driver
;  shift register then write new column select bit pattern onto
;  the pins to resume column driver duties before PWM goes low.
;
        movlw   8               ;                                 |B0
        movwf   bitctr          ; bitctr = 8                      |B0
loadSR  bcf     clk             ; clk = 0                         |B0
        bcf     dat             ; dat = 0                         |B0
        btfsc   work,7          ; bit = 1? no, skip, else         |B0
        bsf     dat             ; dat = 1                         |B0
        bsf     clk             ; clk = 1, shift bit into SR      |B0
        rlf     work,F          ; shift 'work'                    |B0
        decfsz  bitctr,F        ; done? yes, skip, else           |B0
        goto    loadSR          ; loop                            |B0
        movlw   b'00011110'     ;                                 |B0
        iorwf   PORTB,W         ; turn off column select bits     |B0
        xorwf   colsel,W        ; active lo column select bit     |B0
        movwf   PORTB           ; select new column               |B0
;
;  beep task (32-msec 500-Hz tone @ 1-msec interrupts)
;
isrBeep
        movf    beep,W          ; beep task running?              |B0
        bz      isrSw           ; no, branch, else                |B0
        movlw   1<<spkr         ; speaker pin mask                |B0
        xorwf   PORTB,F         ; toggle speaker pin              |B0
        decf    beep,F          ; decrement beep msec counter     |B0
;
;  switch management (sample switch at current selected column)
;
;   wreg ___---___---___---____  sample active lo switches
;  latch ____---___---___---___  switch state latch
;   wreg ___-__-__-__-__-__-___  changes, press or release
;   wreg ___-_____-_____-______  filter out 'release' bits
;  flags ___------______-------  toggle flag bits for main
;
isrSw
        movf    latch,W         ; W = switch state latch          |B0
        iorwf   colsel,W        ; set SWn bit to '1' (pressed)    |B0
        btfsc   PORTB,5         ; pressd (0)? yes, skip, else     |B0
        xorwf   colsel,W        ; set SWn bit to '0' (released)   |B0
        xorwf   latch,W         ; W = changes, press or release   |B0
        xorwf   latch,F         ; update switch state latch       |B0
        andwf   latch,W         ; filter out "new release" bits   |B0
        skpz                    ; a "new press"? no, skip, else   |B0
        bsf     beep,5          ; task a new press beep           |B0
        xorwf   flags,F         ; toggle switch flags for main    |B0
;
;  prep for next column update interrupt
;
isrNext
        incf    colnbr,F        ; bump column number              |B0
        bcf     colnbr,2        ; 0..3 inclusive                  |B0
        clrc                    ;                                 |B0
        rlf     colsel,W        ; advance column select bit       |B0
        btfsc   colsel,4        ; last column? no, skip, else     |B0
        movlw   b'00000010'     ; reset to first column           |B0
        movwf   colsel          ;                                 |B0
        movlw   disp            ; W = &disp[0]                    |B0
        addwf   colnbr,W        ; add column number, 0..3         |B0
        movwf   FSR             ; FSR = &disp[colnbr]             |B0
        movf    INDF,W          ; W = disp[colnbr], 0..9          |B0
        call    SegTable        ; get segment data                |B0
        movwf   work            ; SR data for next column         |B0
;
;  restore context
;
        movf    f_isr,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        movf    p_isr,W         ;                                 |B0
        movwf   PCLATH          ; restore PCLATH                  |B0
        swapf   s_isr,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   w_isr,f         ; don't screw up STATUS           |B?
        swapf   w_isr,W         ; restore WREG                    |B?
        retfie                  ; return from interrupt           |B?

;******************************************************************

SegTable
        addwf   PCL,F           ;                                 |B0
        retlw   b'00111111'     ; "0"   -|F|E|D|C|B|A             |B0
        retlw   b'00000110'     ; "1"   -|-|-|-|C|B|-             |B0
        retlw   b'01011011'     ; "2"   G|-|E|D|-|B|A             |B0
        retlw   b'01001111'     ; "3"   G|-|-|D|C|B|A             |B0
        retlw   b'01100110'     ; "4"   G|F|-|-|C|B|-             |B0
        retlw   b'01101101'     ; "5"   G|F|-|D|C|-|A             |B0
        retlw   b'01111101'     ; "6"   G|F|E|D|C|-|A             |B0
        retlw   b'00000111'     ; "7"   -|-|-|-|C|B|A             |B0
        retlw   b'01111111'     ; "8"   G|F|E|D|C|B|A             |B0
        retlw   b'01101111'     ; "9"   G|F|-|D|C|B|A             |B0

;******************************************************************
;  main init                                                      *
;******************************************************************

init
        bsf     STATUS,RP0      ; bank 1                          |B1
        clrf    ANSEL           ; turn ADC functions off          |B1
        movlw   b'01110000'     ;                                 |B1
        movwf   OSCCON          ; select 8-MHz INTOSC clock       |B1
Stable  btfss   OSCCON,IOFS     ; Int Osc Freq Stable bit set?    |B1
        goto    Stable          ; no, branch and wait             |B1
;
;  setup various ISR, Port, Switch, Clock, and Display variables
;
        clrf    TRISA           ;                                 |B1
        movlw   b'11100000'     ;                                 |B1
        movwf   TRISB           ;                                 |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        movlw   b'00011110'     ;                                 |B0
        movwf   PORTB           ; turn column select lines off    |B0
;
;  setup CCP/TMR2 "PWM" mode with a 2-msec period (8-MHz INTOSC)
;
        clrf    TMR2            ; clear TMR2 register             |B0
        bsf     STATUS,RP0      ; select Bank 1                   |B1
        clrf    PIE1            ; mask all peripheral irqs        |B1
        bsf     PIE1,TMR2IE     ; except for TMR2 irqs            |B1
        movlw   d'250'-1        ; 250 x 8 usec 'ticks'            |B1
        movwf   PR2             ; for 1-msec interrupts           |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        clrf    PIR1            ; clear peripheral irq flags      |B0
        movlw   b'00000010'     ; pre 16, post 1, TMR2 off        |B0
                                ;    0-------  unused
                                ;    -0000---  postscale 1
                                ;    -----0--  TMR2 off
                                ;    ------10  prescaler 16
        movwf   T2CON           ; for 8 usec ticks (8-MHz clock)  |B0
        movlw   b'00001100'     ; setup CCP module for "PWM"      |B0
                                ;    00------  unused
                                ;    --00----  DC1B1:DC1B0
                                ;    ----1100  PWM mode
        movwf   CCP1CON         ; turn on PWM mode                |B0
        movlw   120             ; 50% brightness                  |B0
        movwf   CCPR1L          ; 20% duty cycle                  |B0
        bsf     INTCON,GIE      ; enable global irqs              |B0
        bsf     INTCON,PEIE     ; enable peripheral irqs          |B0
;
;  initialize program variables
;
        clrf    disp+0          ; disp[0] = 0                     |B0
        clrf    disp+1          ; disp[1] = 0                     |B0
        clrf    disp+2          ; disp[2] = 0                     |B0
        clrf    disp+3          ; disp[3] = 0                     |B0
        clrf    colnbr          ; colnbr = 0                      |B0
        movlw   2               ;                                 |B0
        movwf   colsel          ; colsel = b'00000010'            |B0
        clrf    bitctr          ; bitctr = 0                      |B0
        clrf    beep            ; beep = 0                        |B0
        clrf    latch           ;                                 |B0
        clrf    flags           ;                                 |B0
        bsf     T2CON,TMR2ON    ; start TMR2 (interrupts)         |B0

;******************************************************************
;  main loop                                                      *
;******************************************************************

loop
        goto    loop            ;                                 |B0

;******************************************************************
        end



OK...I also pulled up the data sheet on the TPIC6C595. Basically it's not "timing critical" serial, but more like "triggered serial". Every time the clock pin is pulsed it shifts in whatever bit happens to be present on the data pin on the rising edge of the clock pulse. At least this is what I gather based on what I read in the data sheet. So the important thing is to ensure that the data send pin on the PIC is in the correct state prior to the PIC pulsing the clock pin on the driver IC.

From what I can tell from your code -

1) Bit counter register is loaded with the value of 8.

2) The segment data is fetched from the segment data lookup table and loaded into the GP register labeled "work".

3) Both the clk and data pins are cleared.

4) Bit 7 (MSB) of "work" is checked. If it's set, the data pin is set high, else data pin is set low.

5) Clock pin is driven high.

6) "Work" bits are rotated left one position.

7) Bit counter is decrimented once. When bit counter = 0, the program continues on, else it cycles back through, shifting the bits in register "work" once each time and basically mirroring whichever bit is the MSB in "work" on the data pin for that particular read cycle.

Am I seeing this correctly?
 
Last edited:
.I also pulled up the data sheet on the TPIC6C595. Basically it's not "timing critical" serial, but more like "triggered serial". Every time the clock pin is pulsed it shifts in whatever bit happens to be present on the data pin on the rising edge of the clock pulse.
Yeah, that's it Jon. Pretty simple, right?

<added>

It's synchronous serial...
 
Last edited:
Yeah, that's it Jon. Pretty simple, right?

Yeah that is simple actually.

The only other question I have is that this chip has two clock pins...Register Clock (RCK) and Shift Register Clock (SRCK). Do these two pins get tied together in practical application?
 
The SER pin is the data pin (I call it <dat>), the SRCK pin is the serial clock pin (i call it <clk>), and the RCK pin is the latch or strobe (I call it <lat>) which copies the contents of the shift register onto the output pins. These three pins are normally driven seperately.

If I wasn't using the PWM signal to drive the RCK pin, then code to load the TPIC6C595 might look something like this;

Code:
;
        movlw   8               ;                                 |B0
        movwf   bitctr          ; bitctr = 8                      |B0
loadSR  bcf     clk             ; clk = 0                         |B0
        bcf     dat             ; dat = 0                         |B0
        btfsc   work,7          ; bit = 1? no, skip, else         |B0
        bsf     dat             ; dat = 1                         |B0
        bsf     clk             ; clk = 1, shift bit into SR      |B0
        rlf     work,F          ; shift 'work'                    |B0
        decfsz  bitctr,F        ; done? yes, skip, else           |B0
        goto    loadSR          ; loop                            |B0
        movlw   b'00011110'     ;                                 |B0
        iorwf   PORTB,F         ; blank the display               |B0
        bsf     lat             ; pulse RCK pin to latch SR       |B0
        bcf     lat             ; data onto TPIC6C595 outputs     |B0
        xorlw   colsel,W        ; current column select bit       |B0
        movwf   PORTB           ; display new column              |B0
;
 
Last edited:
Jon Wilder-
Also...in regards to bit banging...I've never written a bit bang routine and I've tried googl'ing serial bit banging for PICs and haven't found what I'm looking for so if someone could direct me to some sample bit bang routine codes and/or reference sites where I could read up on it I would greatly appreciate it.

This page has PIC bitbang send and receive, they are in C but the principle is easy to understand (and convert to asm) and a clever TMR1 timing trick is shown for the serial period;
Bit bang serial - PIC 18F1320

These pages should be of interest to you, it's a minimal MIDI-out drums controller using a PIC 16F and just 2 resistors;
MIDIbash project concept
MIDIbash 2 with PIC source code
The second link has PIC source code where the MIDI-out code is bit banged and done in assembler.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top