Maker Pro
Maker Pro

Problem with programming PIC multiplexing

Hi. I am quite new to PIC programming (as well as this forum ^_^), and so naturally I stumble across issues that I seem to be unable to figure out, and so I would like to ask for your assistance with something. Jumping into multiplexing, working on 3 7-segment displays, I seem to have trouble even getting the PIC to run my code. So let me start with that issue.

The PIC seems to not like to just run. With a prior program, which simply went through a list of pre-programmed outputs, it would require me to remove Vss, reset several times (using a direct link of +5 volts to ground (which is how this Velleman programmer does it, so why not)), and then it would run. Then I would re-attach Vss. Now, this is odd, but I guess the CPU is able to run without Vss connected via other routes to ground (through the LCD itself). This is in contrast with example programs, which ran fine on the board, however this program I wrote would not at all. The difference here is that I am running the chip off of the internal RC oscillator, which could have possibly been damaged or something, as I haven't always been so nice with inputting power. (Indeed, adding a fresh 9-volt battery to the circuit actually caused the LM7805 VRM to get quite hot; since then, added 100ohm resister in-line with the +9volts to it).

In terms of how the circuit is actually set up, it is as follows:
470uF capacitor in parallel from power input
100 ohm resistor connected to the LM7805 input; of course ground to ground and;
+5 volts leading to Vdd and through 1000 ohm resistor to MCLR
Momentary switch connected from the Vdd rail directly to ground for reset
470 ohm resistor connected from ground to all 3 collectors of NPN transistors (series)

Each base goes to pins A0-A2 on the PIC. The emitters, of course, are to the LCD's grounds.
Each positive terminal of all LCDs are wired together, with each respective pin connected from pins B0-B6. B7 is not wired for the period.

The point of this setup is that the PIC will use the transistors to turn on and off each display in order, and once it is changed, display what is needed. Obviously since the others will be turned off, this will allow for each character to be displayed over a shard bus, thus limiting pins used (as you know).

And please hold back criticism of how I wired it. I know not a lot about actual electronics, as now a primary interest is knowing how CPU's work. Programming a small computer in assembly is kind of cool, and the PIC makes this simple. But any addition to how I should set it up for more effectiveness, let me know.

A picture of the breadboard is as an attached picture. Not the random characters of the display; I did not program any character like that, and also each display is to output different characters. Sometimes LCD's 1 and 3 are lit, sometimes just 1. Prior to the newest revision of the code, ONLY LCD 3 would light up. So something must be working, but now as fully as it should.

In terms of the software, I am going about it by calling an interrupt using timer 0. In the interrupt, it basically should decide based on a counter register which display to output next, set up the output in a subroutine, reset the interrupt, then exit the interrupt routine. Each LCD output is assigned to a register, so basically setting up the output involves moving the contents of this register to the B output pins, and setting the appropriate transistor to display it. This allows me to build main programs that simply place whatever needs to be shown into a register, and the interrupt takes care of outputting it and multiplexing. This leaves me a problem of how I can make a simple counter (as I am also using a table of the proper outputs to make the right decimal character), but to that when it actually works.

To stop rambling, here is the entire program. This is a PIC 16F627, BTW:
Code:
ORG 0x04
call LCD_INT

#include <Z:\P16F627.inc>
OPTION EQU 0x81 ;Some assembler issue; not even used anyway :(

	__CONFIG  _BODEN_OFF & _CP_OFF & _DATA_CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF & _MCLRE_ON & _INTRC_OSC_NOCLKOUT

;Table of LCD outs; each will be loaded into the approprate LCD out register when needed, to be displayed during interrupt

RESET

bcf STATUS,RP0

ZERO equ 0x20
movlw b'01111101'
movwf ZERO

ONE equ 0x21
movlw b'00110000'
movwf ONE

TWO equ 0x22
movlw b'01101110'
movwf TWO

THREE equ 0x23
movlw b'01111010'
movwf THREE

FOUR equ 0x24
movlw b'00110011'
movwf FOUR

FIVE equ 0x25
movlw b'01011011'
movwf FIVE

SIX equ 0x26
movlw b'01011111'
movwf SIX

SEVEN equ 0x27
movlw b'01110000'
movwf SEVEN

EIGHT equ 0x28
movlw b'01111111'
movwf EIGHT

NINE equ 0x29
movlw b'01111011'
movwf NINE

DOT equ 0x2A
movlw b'10000000'
movwf DOT

;Set all B pins output
bsf STATUS,RP0
movlw 0x00
movwf TRISB

;Set first 3 A pins output=
movlw b'11111000'
movwf TRISA
bcf STATUS,RP0

;Declare LCD registers
LCD_1 equ 0x70
LCD_2 equ 0x71
LCD_3 equ 0x72

;Set up timer0 and interrupt
LCD_STATUS equ 0x7F ;Status of which LCD to ouput on next interrupt
clrf TMR0
bsf STATUS,RP0
bcf 0x81,5 ;Set timer0 to timer mode
bcf 0x81,3 ;enable prescalar
bsf 0x81,0 ;prescalar options
bsf 0x81,1
bsf 0x81,2

bcf INTCON,7 ;Setting up the interrupts, clearing GIE
bcf INTCON,5 ;Then T0IE
bcf INTCON,2 ;And finally T0IF to enable countdown

Start

;Main Program

DELAY_ROUTINE
LOOP equ 0x73 ;Define a register for the countdown
movlw 0xFF ;Set W to FF
movwf LOOP ;Set LOOP to FF
DEL_INCRIM
incfsz LOOP ;Incriment until overflow; if overflowed, skip to return, else repeat decriment
goto DEL_INCRIM
RETLW 0


movf EIGHT,W ;Set a value from the chart to LCD output registers. Will be replaced with counter
movwf LCD_1
movf ZERO,W
movwf LCD_2
movf ONE ,W
movwf LCD_3

LCD_INT ;LCD Interrupt Routine
bcf STATUS,RP0 ;Make sure were on bank 0
btfss LCD_STATUS,0 ;Is the first bit of LCD_STATUS set? If not, go to LCD_1_INIT, else check next bit
goto LCD_1_INIT
btfss LCD_STATUS,1 ;Is the second bit of LCD_STATUS set? If not, go to LCD_2_INIT, else go to LCD_3_INIT
goto LCD_2_INIT
goto LCD_3_INIT

LCD_1_INIT
incf LCD_STATUS ;Incriment LCD_STATUS
movf LCD_1,W ;Move whatever was put in LCD_1 to W
movwf PORTA ;Then to PORTA
movlw 0x01 ;Next, load the appropriate output
movlw PORTB ;And apply it
call DELAY_ROUTINE ;A breif pause...
bsf STATUS,RP0 ;Move to bank 1
bcf INTCON,7 ;Re-enable interrupts
bcf INTCON,5
bcf INTCON,2 
RETFIE;Return; each for delay

LCD_2_INIT
incf LCD_STATUS 
movf LCD_2,W
movwf PORTA
movlw 0x02
movlw PORTB
call DELAY_ROUTINE
bsf STATUS,RP0
bcf INTCON,7
bcf INTCON,5
bcf INTCON,2
RETFIE


LCD_3_INIT
movf LCD_3,W
movwf PORTA
movlw 0x03
movlw PORTB
call DELAY_ROUTINE
movlw 0x00  ;clear counter, as it needs to repeat
movwf LCD_STATUS
bsf STATUS,RP0
bcf INTCON,7
bcf INTCON,5
bcf INTCON,2
RETFIE

goto Start ;Go back to where the program gets nasty

end

Any suggestions or comments, feel free to post. I will possibly try getting a crystal on here, as it seems to be the difference between working without issue and working with issue, and so perhaps that is just the problem. Not sure if I should run timer 0 on an external source either.

EDIT: Trying it with a 4MHz oscillator did not work either, nor did an external resistor. This has me stumped..
 

Attachments

  • IMG_20121201_135327.jpg
    IMG_20121201_135327.jpg
    146.1 KB · Views: 200
Last edited:
You need to specify the oscillator in your configuration word. Try adding

& _FOSC_INTOSCIO

to the __CONFIG directive. This selects the internal oscillator.

I won't comment on the problems with your wiring since you asked me not to.

Bob
 
The oscillator is specified with the config word '_INTRC_OSC_NOCLKOUT', for internal anyway. I did change them for the corresponding oscillators I tried.
And I didn't say not to comment on it, but rather not to criticize. If you have some useful information, please, share :)

EDIT: Also, disabling pull-ups in the option register worked to not make floating grounds. Pretty cool
EDIT: I just realized that I set the counter for the timer to FF before doing an 'incfsz'. It would only increment once before returing; perhaps too fast to even see if to does actually run. Changing it to defsz may help...
 
Last edited:
OK, I have since modified the code to as follows:
Code:
ORG 0x04
call LCD_INT

#include <Z:\P16F627.inc>
OPTION EQU 0x81 ;Some assembler issue; not even used anyway :(
	__CONFIG  _BODEN_ON & _CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_ON & _INTRC_OSC_NOCLKOUT

;Table of LCD outs; each will be loaded into the approprate LCD out register when needed, to be displayed during interrupt

bcf STATUS,RP0

ZERO equ 0x20
movlw b'01111101'
movwf ZERO

ONE equ 0x21
movlw b'00110000'
movwf ONE

TWO equ 0x22
movlw b'01101110'
movwf TWO

THREE equ 0x23
movlw b'01111010'
movwf THREE

FOUR equ 0x24
movlw b'00110011'
movwf FOUR

FIVE equ 0x25
movlw b'01011011'
movwf FIVE

SIX equ 0x26
movlw b'01011111'
movwf SIX

SEVEN equ 0x27
movlw b'01110000'
movwf SEVEN

EIGHT equ 0x28
movlw b'01111111'
movwf EIGHT

NINE equ 0x29
movlw b'01111011'
movwf NINE

DOT equ 0x2A
movlw b'10000000'
movwf DOT

;Set all B pins output
bsf STATUS,RP0
movlw 0x00
movwf TRISB

;Set first 3 A pins output=
movlw b'11111000'
movwf TRISA
bcf STATUS,RP0
movlw 0x00
movwf PORTA
movwf PORTB

;Declare LCD registers
LCD_1 equ 0x70
LCD_2 equ 0x71
LCD_3 equ 0x72

;Set up timer0, interrupt, and options
LCD_STATUS equ 0x7F ;Status of which LCD to ouput on next interrupt
clrf TMR0
bsf STATUS,RP0
movlw b'00000111' ;Disable comparitors
movwf CMCON 
bcf 0x81,7 ;Disable pull-ups
bcf 0x81,5 ;Set timer0 to timer mode
bcf 0x81,3 ;enable prescalar
bsf 0x81,0 ;prescalar options
bsf 0x81,1
bsf 0x81,2

bcf INTCON,7 ;Setting up the interrupts, clearing GIE
bcf INTCON,5 ;Then T0IE
bcf INTCON,2 ;And finally T0IF to enable countdown

;Main Program
Start
bsf PORTB,7 ;Enable 'working LED'

movf EIGHT,W ;Set a value from the chart to LCD output registers. Will be replaced with counter
movwf LCD_1
movf ZERO,W
movwf LCD_2
movf ONE ,W
movwf LCD_3


goto Start ;Go back to where the program gets nasty

;LCD Interrupt Routine
LCD_INT 
bcf STATUS,RP0 ;Make sure were on bank 0
movlw 0x00
movwf LCD_STATUS
btfss LCD_STATUS,0
call LCD_1_INIT
btfss LCD_STATUS,1
call LCD_2_INIT
call LCD_3_INIT
bcf STATUS,RP0 ;Make sure tmr0 is clear
clrw TMR0 
bsf STATUS,RP0 ;Move to bank 1
bcf INTCON,7 ;Setting up the interrupts, clearing GIE
bcf INTCON,5 ;Then T0IE
bcf INTCON,2 ;And finally T0IF to enable countdown
RETFIE ;Return

LOOP equ 0x73 ;Define a register for the countdown
DELAY_ROUTINE
movlw 0xFF ;Set W to FF
movwf LOOP ;Set LOOP to FF
DEL_INCRIM
decfsz LOOP ;Incriment until overflow; if overflowed, skip to return, else repeat decriment
goto DEL_INCRIM
RETLW 0

LCD_1_INIT
incf LCD_STATUS ;Incriment LCD_STATUS
movlw 0x00
movwf PORTA ;Clear PORTA
movwf PORTB ;And port B
movlw 0x01 ;Next, load the appropriate output
movwf PORTA ;Then to PORTA
movf LCD_1,W ;Move whatever was put in LCD_1 to W
movwf PORTB ;And apply it
call DELAY_ROUTINE ;A breif pause...
RETLW 0

LCD_2_INIT
incf LCD_STATUS 
movlw 0x00
movwf PORTA ;Clear PORTA
movwf PORTB ;And port B
movlw 0x02
movwf PORTA
movf LCD_2,W
movwf PORTB
call DELAY_ROUTINE
RETLW 0



LCD_3_INIT
bcf PORTB,7 ;Disable LED
movlw 0x00
movwf PORTA ;Clear PORTA
movwf PORTB ;And port B
movlw 0x03
movwf PORTA
movf LCD_3,W
movwf PORTB
call DELAY_ROUTINE
RETLW 0

end

However, now nothing happens in terms of the LCD's (even trying different things), however, the status LED I put in (to let me know when I am in and out of interrupts) is very dimly lit. The only other pin that has an output potential is pin A0, indicating the interrupt proceeds to that point only. I also forgot to mention I never even cleared PORTA or B during the whole time, which partially explains the odd behavior of before.
 
Last edited:
Seem to have it working:
Code:
ORG 0x04
call LCD_INT

#include <Z:\P16F627.inc>
OPTION EQU 0x81 ;Some assembler issue; not even used anyway :(
	__CONFIG  _BODEN_ON & _CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_ON & _INTRC_OSC_NOCLKOUT

;Table of LCD outs; each will be loaded into the approprate LCD out register when needed, to be displayed during interrupt

bcf STATUS,RP0

ZERO equ 0x20
movlw b'01111101'
movwf ZERO

ONE equ 0x21
movlw b'00110000'
movwf ONE

TWO equ 0x22
movlw b'01101110'
movwf TWO

THREE equ 0x23
movlw b'01111010'
movwf THREE

FOUR equ 0x24
movlw b'00110011'
movwf FOUR

FIVE equ 0x25
movlw b'01011011'
movwf FIVE

SIX equ 0x26
movlw b'01011111'
movwf SIX

SEVEN equ 0x27
movlw b'01110000'
movwf SEVEN

EIGHT equ 0x28
movlw b'01111111'
movwf EIGHT

NINE equ 0x29
movlw b'01111011'
movwf NINE

DOT equ 0x2A
movlw b'10000000'
movwf DOT

;Set all B pins output
bsf STATUS,RP0
movlw 0x00
movwf TRISB

;Set first 3 A pins output=
movlw b'11111000'
movwf TRISA
bcf STATUS,RP0
movlw 0x00
movwf PORTA
movwf PORTB

;Declare LCD registers
LCD_1 equ 0x70
LCD_2 equ 0x71
LCD_3 equ 0x72

;Set up timer0 and options

clrf TMR0 ;Ensure TMR0 is clear
bsf STATUS,RP0
movlw b'00000111' ;Disable comparitors
movwf CMCON 
bcf 0x81,7 ;Disable pull-ups
bcf 0x81,5 ;Set timer0 to timer mode
bcf 0x81,3 ;enable prescalar
bcf 0x81,0 ;prescalar options
bcf 0x81,1
bcf 0x81,2

START:
bcf STATUS,RP0
LCD_STATUS equ 0x73
movf EIGHT,W ;Set a value from the chart to LCD output registers. Will be replaced with counter
movwf LCD_1
movf ZERO,W
movwf LCD_2
movf ONE ,W
movwf LCD_3

bsf INTCON,7 ;Setting up the interrupts, clearing GIE
bsf INTCON,5 ;Then T0IE
bcf INTCON,2 ;And finally T0IF to enable countdown

goto START 

;LCD Interrupt Routine
LCD_INT:
bcf STATUS,RP0 ;Make sure were on bank 0 for next run
btfss LCD_STATUS,0
call LCD_1_INIT
call LCD_2_INIT
btfss LCD_STATUS,1
call LCD_3_INIT
movlw 0x00;Reset LED_STATUS
movwf LCD_STATUS
bsf STATUS,RP0 ;Move to bank 1
bcf INTCON,2 ;T0IF to enable countdown
bcf STATUS,RP0 ;Make sure were on bank 0 for next run
RETFIE ;Return

LOOP equ 0x73 ;Define a register for the countdown
LOOP_2 equ 0x74
DELAY_ROUTINE:
movlw 0xFF ;Set W to FF
movwf LOOP ;Set LOOP to FF
movwf LOOP_2
DEL_INCRIM
decfsz LOOP ;Incriment until overflow; if overflowed, skip to return, else repeat decriment
goto DEL_INCRIM
DEL_INCRIM_2
decfsz LOOP_2
goto DEL_INCRIM_2
RETLW 0

LCD_1_INIT:
movlw 0x00
movwf PORTA ;Clear PORTA
movwf PORTB ;And port B
movlw 0x01 ;Next, load the appropriate output
movwf PORTA ;Then to PORTA
movf LCD_1,W ;Move whatever was put in LCD_1 to W
movwf PORTB ;And apply it
call DELAY_ROUTINE
incf LCD_STATUS
RETLW 0

LCD_2_INIT:
movlw 0x00
movwf PORTA
movwf PORTB 
movlw 0x02
movwf PORTA
movf LCD_2,W
movwf PORTB
call DELAY_ROUTINE
incf LCD_STATUS
RETLW 0




LCD_3_INIT:
movlw 0x00
movwf PORTA 
movwf PORTB 
movlw 0x04
movwf PORTA
movf LCD_3,W
movwf PORTB
call DELAY_ROUTINE
RETLW 0


end

Tweaking may be needed. Make the program smaller, etc. Also need to figure out a timer...
 
Top