View Full Version : Display values from two PIC16F series controllers with one LCD display
  
CBRUN17
- 20th March 2017, 22:14
Has anyone ever attempted to display outputs of two PIC16F series controllers to one LCD display? I'm looking for a way to interface two PIC16F76 controllers that output the position of a Quadrature encoder (two axes values).
I'm using a 2x16 LCD and would like to display each axes per line. The outputs are simply a 2 decimal place angular value such as 35.25. I though maybe connecting the data and control lines from one controller to another and have the second process to the display. Any ideas how can accomplish this?
Thanks in advance...
Chris
Jerson
- 21st March 2017, 04:49
Assuming you connect the data lines together and the control signals together, you still need a way to arbitrate access to the common resource (the LCD).  Therefore one extra handshake line from each cpu to the other should be enough to indicate ownership of the resource and thus be able to arbitrate control effectively.  Never done this before, but, this could be a way forward.
pedja089
- 21st March 2017, 11:06
For me, that is wrong way forward....
Just use some serial protocol to transfer data from one to other pic, and then display all data to LCD.
This will use less resource, hardware and software. If you have interrupts, then use I2C like protocol.
CBRUN17
- 21st March 2017, 13:33
I know this can be done without using I2C protocol. I found a device that uses two PICs with one LCD just don't know how it's done in software. 
Lets call the PIC connected to the LCD the slave and other master
I see that pins RB0-RB3 are connected from slave PIC to the master(the only interconnections) and RA0-RA3 pins of slave are connected to data lines of LCD. Additionally, the slave PIC uses all three control lines (RW, E, RS). Maybe using enable pin to multiplex data from both PICs? I'm sure there are many cases when using multiple PICs with a single display would be desirable. I really don't want to dedicate two LCD displays for each axes.
I appreciate the responses so far.
Chris
picster
- 21st March 2017, 15:40
You could have a simple one-wire bus between the PICs that would "request the bus", which would change the TRIS bits from inputs to outputs and vice-versa for both?
pedja089
- 21st March 2017, 17:34
If two pics share RW,E and RS line, then E pin of master pic(master is connected to DATA LCD pins), enables slave with E=0 and disables LCD. So pic could talk with each other using synchronous comm, or async. Eg I2C, or like, half of SPI etc, or Tx and Rx for async. When master want to free comm, just send dome break signal, or on transition from 0 to 1 on E pin slave pic just relist RW and RS pins by setting them to inputs. 
And I don't see why is I2C problem? 
It simple, robust protocol that can be done on that 3 pins. Most LCD have pull up on all pin, so it is very well suited for that task.
Art
- 23rd March 2017, 04:52
Having two controllers connected to the LCD sounds horrible in both software and hardware.
The two controllers would have to be able to talk to each other anyway, to make sure both don’t talk to the LCD at the same time.
So long as you have to connect pins between the controllers, why not just send information from one controller to the other serially?
I would go for making both programs identical, both outputting to the LCD pins, but neither assuming the LCD is connected,
and both programs listening on a serial port for information from another chip.
Then maybe one flag set at compile time to set which has the LCD.
That’s if I were in that position, which wouldn’t happen because one chip would be watching both rotary encoders :D
CBRUN17
- 24th March 2017, 18:02
Art - 
I completely agree with your assessment. Interfacing two channels of quadrature input is what I would prefer to do, however, I had huge issues just getting one to work reliably and fast! The current code interprets the quadrature signal (360 CPR) using ASM coded interrupt within PBP3 and it doesn't miss a beat! I was looking at another manufactures device and they actually do use two separate PICs with one LCD as I described in my original post. I agree this is a poor way to handle two inputs but after struggling for a couple of months with the existing code, I'm looking for whatever works at this point. I have to admit that the limitations I have are no more than my inexperience with PIC coding so far.
The code I use is similar to this:
'Read Quadrature Encoder and display value on LCD in .25 increments. for pic16f76
 '************************ DEFINES HERE *************************************
 DEFINE OSC 20 ' set to 20mhz
 DEFINE LCD_DREG PORTA ' Set Data Registers port
 DEFINE LCD_DBIT 0 ' Set starting data bit
 DEFINE LCD_RSREG PORTB ' Set LCD RS Port
 DEFINE LCD_RSBIT 1 ' Set LCD RS Bit
 DEFINE LCD_EREG PORTB ' Set LCD Enable Port
 DEFINE LCD_EBIT 2 ' Set LCD Enable Bit
 DEFINE LCD_BITS 4 ' Set LCD 4bit Mode
 DEFINE LCD_LINES 2 ' Set number of LCD Lines
 DEFINE LCD_COMMANDUS 2000 ' Set Command Delay time in uS
 DEFINE LCD_DATAUS 60 ' Set Data delay time in uS 
 clear 'clear out variables
 '*********************** CHIP REGISTER SETUP *******************************
 ADCON0 = 7 ' turn off analog porta, set to digital IO
 ADCON1 = 7 '
 CCP1CON = 0
 CCP2CON = 0
 
 TRISA = %00000000 ' output ports
 TRISB = %11000000 ' set input and output ports
 TRISC = %00100000 ' output ports
  
 portB.4 = 0
 portB.5 = 0
 
 '************************ PROGRAM VARIABLES HERE ***************************
 symbol   ledUp     = portC.2 ' status led
 symbol   ledDown = portC.3 ' status led2
 symbol   lcdbkl     = portC.4 ' lcd pannel backlight
 symbol   sw1        = portC.5 ' encoder switch
 symbol   ledTest   = portC.6
 
 enc_old                VAR BYTE
 enc_new              VAR BYTE
 enc_tmp              VAR byte
 enc_counter         VAR word bank0
 enc_counter_old   VAR word bank0
 enc_scaler            VAR word
 enc_dir                VAR enc_old.bit7
 Counter               VAR word bank0
 enc_SIGN            VAR counter.bit15
 Sign                    VAR BIT
 '*********************** ASSEMBLY INTERUPT VARIABLES ***********************
 wsave     VAR  byte $20 system
 wsave1   VAR  byte $a0 system ' Necessary for devices with RAM in bank1
 wsave2   VAR  byte $120 system ' Necessary for devices with RAM in bank2
 wsave3   VAR  byte $1a0 system ' Necessary for devices with RAM in bank3
 ssave     VAR  byte bank0 system
 psave     VAR  byte bank0 system 
 goto start 'skip over interupt handler
 '*********************** ASSEMBLY INTERUPT HANDLER *************************
 define INTHAND myint
 Asm
myint 
 ; Save W, STATUS and PCLATH registers
 movwf wsave
 swapf STATUS, W
 clrf STATUS
 movwf ssave
 movf PCLATH, W
 ;movwf psave 
 CLRF PCLATH
 ;====== BEGINNING OF THE ROTARY ENCODER CODE ========
 ;The Rotary Encoder is connected to PORTB 
 ;The A signal of the encoder connected to the PIN portB.7
 ;The B signal of the encoder connected to the PIN portB.6
 ;
 ;The 4 variables used are declared in the PicBasic code.
 ;
 ; enc_new VAR BYTE
 ; enc_old VAR BYTE
 ; enc_tmp VAR BYTE
 ; enc_counter VAR WORD
 ;
 ;================================================
 ;Read latest input from PORTB & put the value in _enc_new.
 movf PORTB,W
 movwf _enc_new
 ;Strip off all but the 2 MSBs in _enc_new.
 movlw 0xc0 ;Create bit mask (bits 7 & 6). b'11000000' ?
 andwf _enc_new,F ;Zero bits 5 thru 0.
    ;Check to see if encoder has moved
        movf    _enc_old,W         ;move enc_old to W
        movwf   _enc_tmp           ;put W to enc_tmp
        movf    _enc_new,W         ;move enc_new to W for XOR
        xorwf   _enc_tmp,F         ;XOR enc_tmp to detect encoder movement
        btfsc   STATUS,Z           ;if result is not zero, encoder moved.      
        goto    Continue           ;no movement exit isr
 ;Determine the direction of the Rotary encoder. 
 rlf _enc_old,F ;left shift it into _enc_old to align bit 6 of _enc_old with bit 7 of _enc_new.
 movf _enc_new,W ;Move the contents of _enc_new to W in order to XOR.
 xorwf _enc_old,F ;XOR previous inputs (in _enc_old) with latest
 ;inputs (in W) to determine CW or CCW.
 btfsc _enc_old,7 ;Test bit 7 of result (in _enc_old). Skip next line
 ;if it is 0 (direction is CCW).
 goto Up ;Bit is 1 (direction is CW). Go around Down and increment counter.
Down
 ;Decrements _enc_counter because the rotary encoder moved CCW.
 ;Decrements _enc_counter (16 bit value), sets Z on exit.
 decf _enc_counter,F ; Decrement low byte
 incfsz _enc_counter,W ; Check for underflow
 incf _enc_counter+1,F ; Update
 decf _enc_counter+1,F ; Fixup
 movf _enc_counter,W
 iorwf _enc_counter+1,W ; Set Z bit
 ;Add here code for the CCW LED if needed.
 bsf _ledDown ;turn on led
 goto Continue ;Branch around UP.
Up
 ;Increments _enc_counter because the rotary encoder moved CW.
 ;Increments _enc_counter (16 bit value), sets Z on exit.
 incfsz _enc_counter,W ; Add one to low byte
 decf _enc_counter+1,F ; No carry (negates next step)
 incf _enc_counter+1,F ; Add one to high byte
 movwf _enc_counter ; Store updated low byte back.
 iorwf _enc_counter+1,W ; Set Z flag
 ;Add here code for the CW LED if needed.
 bsf _ledUp ;turn on led
Continue 
 ;Assign the latest encoder inputs (in _enc_new) to _enc_old.
 movf _enc_new,W
 movwf _enc_old
 ; Restore saved registers
 movf  psave, W
 movwf PCLATH
 swapf ssave, W
 movwf STATUS
 swapf wsave, F
 swapf wsave, W
 bcf INTCON, RBIF ; Clear the Interupt Flag
 RETFIE ; Return from interrupt
 endasm
 '************************************************* **************************
 '************************* PROGRAM STARTS HERE *****************************
 '************************************************* **************************
 START: ' Main Program starts here
 INTCON = %10001000 ' Enable PortB Change Interupts 
 LCDOUT $FE,1 'Init The LCD
 pause 500 'wait for LCD to start 
 lcdout $FE,$80,  "TEST ANGLE DISP " 'display splash screen
 LCDOUT $FE,$C0,  "Initializing... " 
 high lcdbkl ' turn on backlight
 '************************** SET DEFAULT SETTINGS HERE **********************
 pause 2000 ' just wait a bit to read splash screen
 lcdout $FE,1
 enc_counter = 0 ' set default encoder value
 enc_counter_old = 0 
 Counter = 0
 
 
 lcdout $FE,$80, "Measurement   "
 lcdout $FE,$C0,"TST ANGLE: "
 '************************** TESTING ROUTINES HERE **************************
 test:
   If portC.5 = 0 then clearall
   
if enc_counter <> enc_counter_old then 'see if value has changed
    enc_counter_old = enc_counter 'move new value to old
        Counter = enc_counter '(enc_counter */ 250)/2
        Sign = Counter.15   ' Save sign
        Counter = ((ABS Counter) * 25) ' Degrees is now 0 to 900
        
If Sign THEN Counter = -Counter ' Restore sign, value is now -900 to 900
If not sign AND (-Counter < 0 or Counter > 0) then
    lcdout $FE,$80, "Measurement   "
    lcdout $FE,$C0,"TST ANGLE:", "-",sdec Counter /100 , ".", sdec Counter // 100, " " 
else
    lcdout $FE,$80, "Measurement   "
    lcdout $FE,$C0,"TST ANGLE:", " ", sdec -Counter /100 , ".", sdec -Counter // 100, " "
endif 
 
 low ledUp 'turn off CCW led
 low ledDown 'turn off CW led
 endif
 
 goto test
 ClearAll:
 
    enc_counter = 0
    enc_counter_old = 0 
    Counter = 0
    clear
    lcdout $FE,$80, "Measurement   "
    lcdout $FE,$C0,"TST ANGLE:", " 0.00 "
        high ledtest
            pause 1000
        low ledtest
 goto test
 
@INT_RETURN
Art
- 25th March 2017, 02:38
I’ll put it to you this way, I don’t think you have any choice but to communicate between the two controllers in some fashion :D
If any one controller prints the entire display of both values, then one value has to be communicated from one controller to the other.
If you want both values displayed on the LCD at the same time, the only way to achieve that without one controller knowing what the other is thinking,
is to print half of the display with one controller, and the other half of the display with the other, but to do that, to some extent,
one controller still has to know what the other is thinking :D at the very least so they don’t try to print a the same time.
To do that you only need to use the shared LCD Enable pin.
Whenever a controller is not printing, the Enable pin is an input listening for highs on the Enable pin from the other controller.
Whenever a controller wants to print, so long as the enable has not been held high by the other controller for a duration
it can go ahead and print it’s line, by setting the Enable pin output, then high, then set the rest of the LCD data/control pins,
then send Enable low to latch the LCD data, and continue till the line is printed, then set Enable as an input again.
If one controller wants to print, but notices the other is printing because the Enable pin has been set high by the other controller
within a duration (the duration being a little longer than it takes to print a line to the LCD), it simply waits until the Enable pin
has been low for the duration before having a go at printing it’s data. That way neither controller is a master or a slave,
so long as each leaves a significant delay because there’s an awareness that it’s no the only controller trying to use the LCD.
At least that’s first way I wouldn’t go about not doing any of that.
This all assumes that the LCD data latches on the transition of the Enable pin from high to low,
and it’s the transition from low to high that is ignored (which is the way I remember it),
but I’m writing for a library currently, and haven’t had to deal with that for some time.
Demon
- 27th March 2017, 20:20
I'd keep things simple: 1 master to drive the LCD, 2 slaves to process your axis data.
Easier to develop and debug.
Robert
 
Powered by vBulletin® Version 4.1.7 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.