I love my 2-wire LCDs and use them all the time, but since I started tinkering with the PIC10F322 chip that has only four I/O pins I decided to have a closer look at Roman Black's "Shift1" system. I could really use that one extra pin!

Shift1 uses a 74HC595 shift register with a couple RC circuits and careful code timing to allow one pin from the microcontroller to control seven I/O pins on the 74HC595.

I breadboarded the circuit and wrote some code. It wasn't terribly difficult. The orange wire is the 1-wire control line. Yellow is to the scope - you can see the red wire in the corner getting ground for the scope.

I'm still fine tuning the timing in the code. Sometimes it gets garbled as shown in the photo below. It's also possible that my interrupt service routines are messing up the Shift1 timing. It could even be that one of the ISRs is stepping on a variable that I used in the LCD code. I'll have to go through the code very carefully and see if I can get it to not be so finicky.

I do know that having the scope connected affects the thing. It definitely runs better without the scope connected. So I think probably breadboard capacitance may also be messing things up some. I think I'll draw up a PC board and get some made.

Anyway, not too bad for an evening's work.

A few days later...

The garbled display is definitely caused by the ISR routines. Shift1 has delays that are 1uS, 15uS, 30uS, 200uS and 300uS. Interrupt any of the short delays with an ISR, especially the 1uS, and bad things happen. I disabled interrupts and the problem completely went away. So that's simple enough to fix. I haven't fixed it yet in the code below, but I will post the fixed code when I do.

Next day...

Here's the code with the LED blinking interrupt fixed. But I can't get the interrupt on change for the button to work anymore. I guess the lcd_nybble subroutine has interrupts disabled so much of the time that catching a button press edge in the tiny space between is almost impossible. I'll probably have to just poll that or something:

processor 10f322
include "p10f322.inc"
__config _CP_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _LVP_OFF

  cblock  0x40
  i,nyb,rs,letter,count,count2,flag,temp,saves,savew,binh,binl,ascii_1,ascii_2,ascii_3,ascii_4,d1,d2,d3
  endc

sig     equ     0

org     0x00
goto    start

;-------------------------
;interrupt service routine
org     0x04
movwf   savew           ;save context (w & status)
swapf   STATUS,w
movwf   saves
  btfsc   IOCAF,3         ;is RA3 IOC flag set?
  goto    buttisr         ;yes, go to button code
blink:                      ;no, must be Timer0
  movlw   0x04            ;toggle LED pin RA2
 xorwf   LATA,f
  bcf     INTCON,TMR0IF   ;clear interrupt flag
  goto    done
buttisr:
  bsf     flag,0          ;yes, flag = 1
  bcf     IOCAF,3         ;clear the RA3 IOC flag
done:
swapf   saves,w         ;restore context
movwf   STATUS
swapf   savew,f
  swapf   savew,w
retfie

;-------------
; main program
start:
  movlw   b'01110000'     ;16MHz clock
  movwf   OSCCON
  clrf    ANSELA          ;all pins digital
  clrf    TRISA           ;led pins outputs
  movlw   b'10000111'     ;setup Timer0,1:256
  movwf   OPTION_REG
  bsf     INTCON,TMR0IE   ;enable Timer0 interrupt
  clrf    flag            ;zero my IOC flag
  bsf     IOCAN,3         ;enable IOC on RA3
  bsf     INTCON,IOCIE    ;enable interrupt on change
  bsf     INTCON,GIE      ;enable global interrupts
  bsf     LATA,sig        ;set RA0 (signal) high
  clrf    count
  clrf    count2
  call    lcd_init

  call    line1
bleh:
  movf    count,w     ;count = table pointer
  call    table1      ;get byte from table
  xorlw   0x00        ;returned zero?
  btfsc   STATUS,Z
  goto    next        ;yes, then done
  movwf   letter      ;no, output byte to LCD
  call    lcd_char
  incf    count,f     ;increment table pointer
  goto    bleh        ;and go again

next:
  clrf    count
  clrf    count2
blah:
  call    delay250ms
  btfsc   flag,0      ;has button been pushed?
  call    rset        ;yes, go handle it

  movf    count,w     ;set up and go convert
  movwf   binh        ;to ascii for display
  movf    count2,w
  movwf   binl
  call    bin2hex16

  call    line2       ;display the four hex digits
  movf    ascii_1,w
  movwf   letter
  call    lcd_char
  movf    ascii_2,w
  movwf   letter
  call    lcd_char
  movf    ascii_3,w
  movwf   letter
  call    lcd_char
  movf    ascii_4,w
  movwf   letter
  call    lcd_char

  incf    count2,f    ;increment low byte
  btfss   STATUS,Z    ;is zero set, meaning wrapped around?
  goto    blah        ;no, goto blah
  incf    count,f     ;yes, increment high byte
  goto    blah        ;and go again

;---------------------
; table
table1  addwf   PCL,f
  retlw 'P'
  retlw 'I'
  retlw 'C'
  retlw '1'
  retlw '0'
  retlw 'F'
  retlw '3'
  retlw '2'
      retlw   '2'
  retlw ' '
  retlw 'L'
  retlw 'C'
  retlw 'D'
  retlw 0x00
;---------------------
; subroutines
rset:
  clrf    flag
  clrf    count
  clrf    count2
  return
;---------------------
;16-bit binary passed in binh/binl
;Ascii hex values returned in ascii_1 thru ascii_4
bin2hex16:
  swapf   binh,w
  andlw   0x0f
  movwf   ascii_1
  movlw   0x0a
  subwf   ascii_1,w
  movlw   0x30
  btfsc   STATUS,C
  movlw   0x37
  addwf   ascii_1,f
;-----------
  movf    binh,w
  andlw   0x0f
  movwf   ascii_2
  movlw   0x0a
  subwf   ascii_2,w
  movlw   0x30
  btfsc   STATUS,C
  movlw   0x37
  addwf   ascii_2,f
;-----------
  swapf   binl,w
  andlw   0x0f
  movwf   ascii_3
  movlw   0x0a
  subwf   ascii_3,w
  movlw   0x30
  btfsc   STATUS,C
  movlw   0x37
  addwf   ascii_3,f
;-----------
  movf    binl,w
  andlw   0x0f
  movwf   ascii_4
  movlw   0x0a
  subwf   ascii_4,w
  movlw   0x30
  btfsc   STATUS,C
  movlw   0x37
  addwf   ascii_4,f

  return
;---------------------
lcd_cmd:
  movf    letter,w        ;do high nybble first
  andlw   0xf0            ;mask out lower nybble
  movwf   nyb             ;load nyb for lcd_nybble
  bsf     nyb,3           ;set E high
  bcf     nyb,2           ;set rs bit low
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;set E low
  call    lcd_nybble      ;send second time

  swapf   letter,w        ;now do low nybble
  andlw   0xf0            ;mask out lower nybble
  movwf   nyb             ;load nyb for lcd_nybble
  bsf     nyb,3           ;set E high
  bcf     nyb,2           ;set rs bit low
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;set E low
  call    lcd_nybble      ;send second time
  return
;---------------------
lcd_char:
  movf    letter,w        ;do high nybble first
  andlw   0xf0            ;mask out lower nybble
  movwf   nyb             ;load nyb for lcd_nybble
  bsf     nyb,3           ;set E high
  bsf     nyb,2           ;set rs bit high
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;set E low
  call    lcd_nybble      ;send second time

  swapf   letter,w        ;now do low nybble
  andlw   0xf0            ;mask out lower nybble
  movwf   nyb             ;load nyb for lcd_nybble
  bsf     nyb,3           ;set E high
  bsf     nyb,2           ;set rs bit high
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;set E low
  call    lcd_nybble      ;send second time
  return
;---------------------
;nyb=4bits of data (first 4 shifted out)
;e = 5th shifted
;rs=command/character (6th shifted)
lcd_nybble:
  movlw   7       ;set to send 7 bits
  movwf   i
  bcf     INTCON,GIE  ;disable interrupts
lcdloop1:
  btfsc   nyb,7       ;bit 7 high?
  goto    one         ;yes, goto one
  bcf     LATA,sig    ;no, clock out a zero - set signal low

movlw   0x13        ;delay15us
movwf   d1
Delay_0a:
decfsz  d1, f
goto    Delay_0a

  bsf     LATA,sig    ;set sig high again

movlw   0x25        ;delay30us
movwf   d1
Delay_0b:
decfsz  d1, f
goto    Delay_0b

  goto    increm      ;done
one:
  bcf     LATA,sig    ;clock out a one

  nop                 ;delay1us
  nop
  nop
  nop

  bsf     LATA,sig

movlw   0x12        ;delay15us
movwf   d1
Delay_0c
decfsz  d1, f
goto    Delay_0c
nop

increm:
  rlf     nyb,f       ;move next bit into line
  decfsz  i,f         ;finished 7 bits?
  goto    lcdloop1    ;no, go again
latch:
  bcf     LATA,sig    ;send 8th latch bit
  call    delay200us
  bsf     LATA,sig
  call    delay300us
  rlf     nyb,f       ;put nyb byte back how it started
  rlf     nyb,f
  bsf     INTCON,GIE  ;enable interrupts
  return
;---------------------
line1:
  movlw   0x80
  movwf   letter
  call    lcd_cmd
  return
;---------------------
line2:
  movlw   0xc0
  movwf   letter
  call    lcd_cmd
  return
;---------------------
lcd_init:
  call    delay250ms
  movlw   0x30
  movwf   nyb
  bsf     nyb,3           ;E high
  bcf     nyb,2           ;RS low
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;E low
  call    lcd_nybble      ;send second time
  call    delay5ms
  bsf     nyb,3           ;E high
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;E low
  call    lcd_nybble      ;send second time
  call    delay160us
  bsf     nyb,3           ;E high
  call    lcd_nybble      ;send first time
  bcf     nyb,3           ;E low
  call    lcd_nybble      ;send second time
  call    delay160us
  movlw   0x20
  movwf   nyb
  bsf     nyb,3           ;E high
  bcf     nyb,2           ;RS low
  call    lcd_nybble      ;first time
  bcf     nyb,3           ;E low
  call    lcd_nybble      ;second time
  call    delay160us

  movlw   0x28            ;set 4-bit mode and 2 lines
  movwf   letter
  call    lcd_cmd
  call    delay160us
  movlw   0x10            ;cursor move & shift left
  movwf   letter
  call    lcd_cmd
  call    delay160us
  movlw   0x06            ;entry mode = increment
  movwf   letter
  call    lcd_cmd
  call    delay160us
  movlw   0x0c            ;display on - cursor blink off
  movwf   letter
  call    lcd_cmd
  call    delay160us
  movlw   0x01            ;clear display
  movwf   letter
  call    lcd_cmd
  call    delay250ms      ;30ms is enough
  return
;---------------------
delay160us:
movlw   0x33
movwf   d1
Delay_10:
decfsz  d1,f
goto    Delay_10
goto    $+1
return
;---------------------
delay200us:
movlw   0x9E
movwf   d1
movlw   0x01
movwf   d2
Delay_0d:
decfsz  d1, f
goto    $+2
decfsz  d2, f
goto    Delay_0d
goto    $+1
return
;---------------------
delay300us:
movlw   0xEb
movwf   d1
movlw   0x01
movwf   d2
Delay_0e:
decfsz  d1, f
goto    $+2
decfsz  d2, f
goto    Delay_0e
goto    $+1
return
;---------------------
delay5ms:
movlw   0xE6
movwf   d1
movlw   0x04
movwf   d2
Delay_02:
decfsz  d1,f
goto    $+2
decfsz  d2,f
goto    Delay_02
goto    $+1
nop
return
;---------------------
delay250ms:
movlw   0x4E
movwf   d1
movlw   0xC4
movwf   d2
Delay_01:
decfsz  d1,f
goto    $+2
decfsz  d2,f
goto    Delay_01
goto    $+1
nop
return
;---------------------
delay500ms:
movlw   0x03
movwf   d1
movlw   0x18
movwf   d2
movlw   0x02
movwf   d3
Delay_20:
decfsz  d1, f
goto    $+2
decfsz  d2, f
goto    $+2
decfsz  d3, f
goto    Delay_20
goto    $+1
return
;---------------------
delay1s:
movlw   0x07
movwf   d1
movlw   0x2F
movwf   d2
movlw   0x03
movwf   d3
Delay_4:
decfsz  d1, f
goto    $+2
decfsz  d2, f
goto    $+2
decfsz  d3, f
goto    Delay_4
goto    $+1
goto    $+1
goto    $+1
return
;---------------------

end

Next Post Previous Post