Connect with us

Problem with programming PIC multiplexing

Discussion in 'Microcontrollers, Programming and IoT' started by edplane, Dec 1, 2012.

Scroll to continue with content
  1. edplane

    edplane

    4
    0
    Dec 1, 2012
    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..
     

    Attached Files:

    Last edited: Dec 1, 2012
  2. BobK

    BobK

    7,682
    1,688
    Jan 5, 2010
    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
     
  3. edplane

    edplane

    4
    0
    Dec 1, 2012
    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: Dec 1, 2012
  4. edplane

    edplane

    4
    0
    Dec 1, 2012
    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: Dec 1, 2012
  5. edplane

    edplane

    4
    0
    Dec 1, 2012
    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...
     
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day

-