PDA

View Full Version : Display values from two PIC16F series controllers with one LCD display



CBRUN17
- 20th March 2017, 21: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, 03: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, 10: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, 12: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, 14: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, 16: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, 03: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, 17: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, 01: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, 19: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