Binary Clock using pic16f88 help

Thread Starter

Carl03

Joined Oct 27, 2012
10
Hi, I am new to the forum and I am in desperate need of help.
I built the hardware to my binary clock I am making, my problem the code. I am new to the programming world and i am using MPLab to write the code, asm code. I would like to know if anyone can please help me in writing this code?
I am using a 3x6 LED matrix display
one row for hours, minutes and seconds with 6 LEDs in each row
the hours is connected to RA0
the minutes to RA1
the seconds to RA2
starting from right to left each LED in the row is connected to RB0(far right) up until RB5(far left)
i also have to buttons for setting the time connected to RA3(hours set) and RA4(minutes set) when pressed should clear seconds

Programming with PICKit3

Any help would be welcomed.
Thanx in advance
 

Attachments

Last edited:

spinnaker

Joined Oct 29, 2009
7,830
Hi, I am new to the forum and I am in desperate need of help.
I built the hardware to my binary clock I am making, my problem the code. I am new to the programming world and i am using MPLab to write the code, asm code. I would like to know if anyone can please help me in writing this code?
I am using a 3x6 LED matrix display
one row for hours, minutes and seconds with 6 LEDs in each row
the hours is connected to RA0
the minutes to RA1
the seconds to RA2
starting from right to left each LED in the row is connected to RB0(far right) up until RB5(far left)
i also have to buttons for setting the time connected to RA3(hours set) and RA4(minutes set) when pressed should clear seconds
Programming with PICKit3

Any help would be welcomed.
Thanx in advance
Doesn't anyone use capital letters anymore? Capital letters at the start of sentences makes it easier for others to help you.

Also what makes things easier is if you post your schematic, code and something other than "I have a problem with the code".

What is the problem exactly and what have you done to debug it?

Others might argue but I think with advanced languages such as C and BASIC (at least advanced to the mcu world) it is better to learn using mcus with one of those first. Learning mcus and reading data sheets is already a huge learning curve. It is my opinion (and again others will argue) that you learn the basics in a higher level language you can then come back to assembler once you have mcus tackled.
 

JohnInTX

Joined Jun 26, 2012
4,787
.. on to the programming then. As any programmer does, you need to break down the overall project into smaller, easier to understand (and implement) blocks. If you try to do it all at once, its a daunting task particularly if you are not experienced.

For your clock, I would begin by breaking it down into something like this:

0) PIC hardware configuration i.e. init the IO ports, configure the oscillator with CONFIG directives, etc.
1) Timing i.e. generate a tik every second that is counted by--
2) Internal timekeeping i.e. count up to 60, clear the seconds, increment minutes etc. then--
3) Display the current count in the desired fashion
4) i.e. Modify the current count using the buttons

You may want to break it down further but you get the idea. Once you do, rough out a program flow for each part. Next code it and test in MPLAB. Note that you don't have to have it all at once. For example, 0 is simple enough.

Make a subroutine called initIO that does all of the PORT and TRIS settings. Step through it using MPLAB and make sure all of the IO is ready to use before proceeding (LEDS off etc).

Next, I would uses TMR2/PR2 to generate a periodic interrupt. It doesn't have to be a full second as you can count the interrupts like you count time. You can test this as well on MPLAB. When you are done, use the seconds output to flash one LED as a test..

Continue with each of the other blocks, stitching them together when they work. Don't hesitate to write some disposable test code to exercise them both on MPLAB/MPSIM and on your target board. That's why you write things in small functional blocks rather than long drawn out code.

Pretty much everything you need to write each of the blocks as I have described them have been discussed in depth on this forum, in the tutorial e-books on this forum and in various Microchip literature (tutorials, coding frameworks etc). Take small steps and you'll be on your way before you know it.

A performer that ate an entire piano was asked how he did it. "One bite at a time" was the answer. That's how you do programming as well.

Post your progress and questions here and you'll likely get all of the help you need.

Have fun.

Others might argue but I think with advanced languages such as C and BASIC (at least advanced to the mcu world) it is better to learn using mcus with one of those first. Learning mcus and reading data sheets is already a huge learning curve. It is my opinion (and again others will argue) that you learn the basics in a higher level language you can then come back to assembler once you have mcus tackled.
spinnaker makes some excellent points here. A higher level language will relieve the programmer of much of the infernal details of managing the PIC 16Fs 'interesting' architecture and get things moving faster. I would add though that some familiarity with the underlying hardware is necessary regardless of the language used (in order to configure the PIC, program the peripherals, configuring the I/O etc) and being proficient in assembler makes that easier (and easy for ME to say because I'm proficient in assembler). One thing that a higher level language will not compensate for is bad programming practices nor will it relieve the programmer of the necessity of breaking the program down into manageable pieces and to have a plan to implement/test them. And, its sadly true that compilers and libraries can introduce their own bugs into the equation. When using C, I (and some other experienced guys here as well) write many of our own library functions, particularly for peripherals.

The OP mentioned assembler so.. I like that, of course. But, yeah. Consider 'C' as well.
 
Last edited:

Thread Starter

Carl03

Joined Oct 27, 2012
10
Thanx for the responses guys, I'm a student and have chosen this as my project. I am still learning C and assembler. For the project we have to code in assembler and all we have done in assembler was very basic like interrupt service routines, button debouncing, writing delay loops using counters. And I am learning now that I'll have to be using the TMR2 which we have not encountered as of yet but I came seeking help because I am enthusiastic about completing this project and seeing my work in action. As of yet I haven't done coding:(, well not much anyway.

Rich (BB code):
LIST	p=16F88				
#INCLUDE <P16F88.INC>

  __CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
  __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

	CBLOCK 0x20

	sec
	min
	hour
	button_hr
	button_min
	debounce
	counter
	ENDC

#define HOUR	PORTA,0
#define MIN 	PORTA,1
#define SEC 	PORTA,2
#define BUTTON1 PORTA,3
#define BUTTON2 PORTA,4

#define LED0 PORTB,0
#define LED1 PORTB,1
#define LED2 PORTB,2
#define LED3 PORTB,3
#define LED4 PORTB,4
#define LED5 PORTB,5


ORG 0x0000
nop
goto start
ORG 0x0004
isr

start
	clrf	TRISA
	clrf	PORTB
	bsf STATUS, RP0
	movlw b'00011000'		;RA0-2 outputs, RA3-4 inputs
	movwf TRISA
	movlw b'00000000'
	movwf TRISB
	bcf STATUS, RP0
	bcf INTCON, TMR0IF
	bsf INTCON, TMR0IE	
	bsf INTCON, GIE
That's all I have at the moment. I have also attached the schematic.

Thanx again for taking the time out to reply.
 

Attachments

spinnaker

Joined Oct 29, 2009
7,830
You still haven't stated your problem or what you have done to troubleshoot the problem.


As JohnInTX suggested, break the problem down into small pieces. If you have to write a small program for the piece that is giving you problems. Get that working then move on.

You need to do something to help yourself. We are happy to offer advice, answer questions and get you over the rough spots.

Folks around here are happy to help but we are not going to do your work for you especially when it comes to a school project. The way to learn is to make mistake then fix them now. I'd rather you do that then do it on the navigation system for an airplane that I might be flying on some day. ;)
 

JohnInTX

Joined Jun 26, 2012
4,787
Some housekeeping first:
Tab your ORG stuff off of column 1. That's reserved for labels.
Terminate your labels with a colon :
clrf PCLATH before you goto your start routine.
Do not set TMR0IE yet, you haven't configured the timer.
Do NOT set GIE yet. You don't have an interrupt service routine.
(some of these don't matter yet, but eventually they might).

In your start routine, move the IO initialization to a subroutine like:

Rich (BB code):
initIO:
  do stuff
return
then call it from your start routine.

In initIO be sure to configure all of it:

You'll need to clear ANSEL to make the IO digital (lots miss this).
Also, your TRIS registers are in bank 1. Your clrf TRISA is in the wrong bank.
Use banksel to select the banks. Avoid STATUS,RPx unless you track the current bank or manually maintain both RP1 and RP0.
Select bank 0 and set up all of your outputs then select bank 1, do all of the TRIS/ANSEL stuff.
Consider not relying on power-on defaults but initializing ALL of the IO/ADC/Comparator/Timers et al explicitly. You may have occasion to re-run inits from an unknown (not power on) condition. Plus, visiting all of the chip's registers is a good introduction to them.

Once you have that, call it from start followed by a couple of nops. Set a PK3 breakpoint there and run the program. Use a watch window to look at the IO setup (TRISx, ANSEL etc.) to be sure the values match what you expect.

After that, try a few outputs to the LEDs followed by breakpoints (so that you don't have to write more code) Write 1 to RA0 and 0 to RB5 followed by some nops, break and verify that the LED lights. Buzz out the LED IO as necessary and confirm that you can address each LED.

Same with the switches. Write a loop at the end of the LED stuff that reads the buttons to W. Break after that and examine W while pressing the buttons to make sure that the bits show up.

Wow! you just buzzed out all of your hardware.

Since you are stepping and stopping, I would be tempted to write the code that outputs hours,mins,secs on the LEDs next. Since you are scanning it, start by writing the scan routine as a subroutine and call it from a loop in start using breakpoints after each one.

When you have that working, configure TMR0 to interrupt the PIC at some interval (10ms or so) and call the scanner each IRQ. The main loop now is a dummy loop that initsIO, does some nops, clears WDT then loops to somewhere after initIO. The IRQ does the rest. You can manually poke in values for hours,min,sec and watch the LEDs.

After that, add TMR2 secs counter to run the h,m,s registers.

Small steps, each building on tested stuff. Not all you need to know but you get the idea. Since you are learning as you go along, each thing you do should be bite sized and easily testable. Once happy with it, build on it.

Of course, the whole thing is guided by your software design / flow chart. If you haven't yet, use this as guide and do it.

I'd rather you do that then do it on the navigation system for an airplane that I might be flying on some day. ;)
+1 That's why I get grumpy about things like full initializations, modularity and all the rest!
 
Last edited:

Thread Starter

Carl03

Joined Oct 27, 2012
10
I am not sure how to start the coding. Like you said initialize the IO ports, I'm a total noob to all of this. Needing help to start my code, I get what I'm suppose to do but atm it's not clear to me how I should start. The way you explaining to me is not how I've been taught and I know you trying to lead me in the right direction but I've never heard of banksel and ansel, I'm learning slowly but surely how to use it, just not fast enough.
 

t06afre

Joined May 11, 2009
5,934
I am not sure how to start the coding. Like you said initialize the IO ports, I'm a total noob to all of this. Needing help to start my code, I get what I'm suppose to do but atm it's not clear to me how I should start. The way you explaining to me is not how I've been taught and I know you trying to lead me in the right direction but I've never heard of banksel and ansel, I'm learning slowly but surely how to use it, just not fast enough.
You would not have gotten this assignment without having some preparation both by lessons and lab work. And I think this is some final course assignment. Where you shall use what you have learned before. I think you are confused because this is your first real world task. You do not have some text to lean on. A very common way to make a clock. Is to use a 32.768kHz crystal on Timer1. https://www.sparkfun.com/products/540?
Then let Timer1 generate an interrupt every second. Start simple by using this to toggle a LED every second
 

spinnaker

Joined Oct 29, 2009
7,830
I am not sure how to start the coding. Like you said initialize the IO ports, I'm a total noob to all of this. Needing help to start my code, I get what I'm suppose to do but atm it's not clear to me how I should start. The way you explaining to me is not how I've been taught and I know you trying to lead me in the right direction but I've never heard of banksel and ansel, I'm learning slowly but surely how to use it, just not fast enough.

You are confused? How do you think we feel. You already have a lot of code. What does it do? Who wrote it?

Look at what you have and then try to define what about it does not work.
 

JohnInTX

Joined Jun 26, 2012
4,787
The way you explaining to me is not how I've been taught and I know you trying to lead me in the right direction but I've never heard of banksel and ansel, I'm learning slowly but surely how to use it, just not fast enough.
The good news here is that the PIC does not give a rat's patoot what you've been taught or if BANKSEL / ANSEL is news to you. That simple fact sets you free to begin. But begin you must.

So write initIO as a subroutine. Read the 16F88 databook - not the whole thing. Read Ch 5 - I/O ports. In there is everything you need to know about setting up the IO, including dealing with ANSEL, highlighted in a grey box.

Ch 12 describes ANSEL and its worth a look but just clear it for now to set the pins to digital IO.

BANKSEL is used all over the databook but its not explained. That's because it is an assembler directive, not a PIC instruction. Like ORG, CBLOCK and END, it tells the assembler to do something for you.

BANKSEL name

asks the assembler to generate the code (in this case flipping RPx bits in STATUS) to select the RAM bank that 'name' is in. BANKSEL and other directives are documented in MPASM help. You don't have to use it but the goal is to make your code easier to read and understand. bcf STATUS,RPx tells you that you changed RAM banks. BANKSEL ANSEL not only tells you that you are thinking about ANSEL but also guarantees that RPx will be set up correctly.

Here's something to get you started. Its only the framework. You have code initIO.
(I didn't try to assemble this so you might see some syntax errors.)

Rich (BB code):
;************************ CARL03.asm  **********************************

LIST    p=16F88                
#INCLUDE <P16F88.INC>

  __CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
  __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

    CBLOCK 0x20

    sec
    min
    hour
    button_hr
    button_min
    debounce
    counter
    ENDC

#define HOUR    PORTA,0
#define MIN     PORTA,1
#define SEC     PORTA,2
#define BUTTON1 PORTA,3
#define BUTTON2 PORTA,4

#define LED0 PORTB,0
#define LED1 PORTB,1
#define LED2 PORTB,2
#define LED3 PORTB,3
#define LED4 PORTB,4
#define LED5 PORTB,5


    ORG 0x0000
    nop
    clrf    PCLATH        
    goto     start

    ;**************** INTERRUPT SERVICE *********************

    ORG 0x0004
    ; IRQ service TBD
    nop
    nop
    ; You never should be here since you don't have any IRQs set up yet
    ; but if you ARE, this will stop it gracefully.
    bcf    INTCON,GIE    
    return

    ;**************** MAIN PROGRAM **************************
start:
    call    initIO

sysLoop:
    BANKSEL    sec        ; every routine should set up its own banks, so we do it
    nop
    nop            ; set a breakpoint here and examine the results of initIO
    nop
    
    bsf    HOUR        ; turn on one LED
    bcf    LED0

    nop
    nop            ; set a breakpoint here
    nop
    goto    sysLoop

    ;******************* INIT IO  ****************************
    ; Inits all of the PIC IO
    ; Call any bank
    ; Rets undefined bank    <- your indication that you must maintain banks 
    ;    yourself.
    ; Fill in the blanks    

initIO:
    BANKSEL    PORTA
    ;init port A and B output bits (same bank for both so one BANKSEL will do

    BANKSEL TRISA
    ; init port A and B TRIS

    clrf    ANSEL        ; ANSEL is in bank 1 too.
    
    ; note that initIO changes the RAM bank selection, a potentially nasty side-effect
    ; Personally, I like each routine to set up its banks as required so leaving banks
    ; in any state is OK since the next routine will set up as required.
    ;
    ; Others like to enforce a default bank that each routine must return. A typical default
    ; bank would be 0 to bring the IO ports back into scope.
    ; There are advantages/disadvantages to either approach. 

    ; For a beginner, I would recommend the latter approach i.e. always return bank 0
    ; The important thing is to decide on a method and apply it consistently.

    return

    END        ; required for MPASM
If you are fuzzy on debugging with MPLAB, stop down and take a look at this:
Step by step
 
Last edited:

Thread Starter

Carl03

Joined Oct 27, 2012
10
So this is what I've done so far. The 'state machine' part in my code is the multiplexing part of my code and I am able to displaying numbers as I would like but can only do so manually through my programming.

Rich (BB code):
;************************ CARL03.asm  **********************************

	LIST    p=16F88                
#INCLUDE <P16F88.INC>

  __CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
  __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

    CBLOCK 0x20

    sec
    min
    hour
    button_hr
    button_min
    debounce
    counter
	current_state
    ENDC

#define HOUR_EN PORTA,0
#define MIN_EN  PORTA,1
#define SEC_EN  PORTA,2
#define BUTTON1 PORTA,3
#define BUTTON2 PORTA,4

#define LED0 PORTB,0
#define LED1 PORTB,1
#define LED2 PORTB,2
#define LED3 PORTB,3
#define LED4 PORTB,4
#define LED5 PORTB,5

#define incw	addlw d'1'


    ORG 0x0000
    nop
    clrf    PCLATH        
    goto     start

    ;**************** INTERRUPT SERVICE *********************

    ORG 0x0004
    ; IRQ service TBD
    nop
    nop
    ; You never should be here since you don't have any IRQs set up yet
    ; but if you ARE, this will stop it gracefully.
    bcf    INTCON,GIE    
    return

    ;**************** MAIN PROGRAM **************************
start:
    call    initIO

sysLoop:
	call	state_machine
    goto    sysLoop

state_machine:
	movlw	.0
	xorwf	current_state, w
	btfsc	STATUS, Z
	goto	state_machine_hours
	movlw	.1
	xorwf	current_state, w
	btfsc	STATUS, Z
	goto	state_machine_min
	goto	state_machine_sec

state_machine_hours:
	bcf		SEC_EN
	comf	hour, w
	movwf	PORTB
	bsf		HOUR_EN
	incf	current_state, f
	goto	state_machine_end
state_machine_min:
	bcf		HOUR_EN
	comf	min, w
	movwf	PORTB
	bsf		MIN_EN
	incf	current_state, f
	goto	state_machine_end
state_machine_sec:
	bcf		MIN_EN
	comf	sec, w
	movwf	PORTB
	bsf		SEC_EN
	clrf	current_state
state_machine_end:
	return
    ;******************* INIT IO  ****************************
    ; Inits all of the PIC IO
    ; Call any bank
    ; Rets undefined bank    <- your indication that you must maintain banks 
    ;    yourself.
    ; Fill in the blanks    

initIO:
    BANKSEL PORTA
	clrf	PORTA
	clrf	PORTB
	movlw	b'00011000'
	movwf	PORTA
	movlw	b'11111111'
	movwf	PORTB
    ;init port A and B output bits (same bank for both so one BANKSEL will do

    BANKSEL TRISA
    ; init port A and B TRIS
	clrf	TRISA
	clrf	TRISB

    clrf    ANSEL        ; ANSEL is in bank 1 too.
	movlw	b'00000000'
	movwf	OSCCON
	banksel	sec
	movlw	.00
	movwf	sec
	movlw	.00
	movwf	min
	movlw	.12
	movwf	hour
    
    ; note that initIO changes the RAM bank selection, a potentially nasty side-effect
    ; Personally, I like each routine to set up its banks as required so leaving banks
    ; in any state is OK since the next routine will set up as required.
    ;
    ; Others like to enforce a default bank that each routine must return. A typical default
    ; bank would be 0 to bring the IO ports back into scope.
    ; There are advantages/disadvantages to either approach. 

    ; For a beginner, I would recommend the latter approach i.e. always return bank 0
    ; The important thing is to decide on a method and apply it consistently.

    return

    END        ; required for MPASM
I would like to know what I have to do next, how to go about the timing and so on. thanks
 

JDT

Joined Feb 12, 2009
657
If you are a beginner with PICs start with small steps. Don't copy other peoples code - you won't learn that way.

From the Microchip website you can download the assembly code template for this device. (Nice to see you using assembler BTW).

Also download the datasheet and if possible, print out the whole thing.

Using this template the first thing you need to do for a digital clock is to program an interrupt that occurs every second. Using one of the counter timers. Most of your code for the clock will be in the interrupt.

Non-interrupt code will be a one-time initialization and then stick in an endless loop.

Then make it do something simple like toggle a LED every second in your interrupt code. Just to prove that your interrupt is working. Program the chip. Test it.

Then add a bit more code. Repeat.

If you try to do too much at once and it doesn't work it can be very difficult to debug.

Good luck!
 

Thread Starter

Carl03

Joined Oct 27, 2012
10
In my code I've put my state machine section into a timer 0 interrupt. So now I have started a timer 2 interrupt for the actual time keeping but my problem is that I have all the lights flashing row by row after putting in my own delay, where it should actually increment the sec by a second

Here is the full interrupt:
Rich (BB code):
   ;**************** INTERRUPT SERVICE *********************
	ORG 0x0004
isr:   
 	 	movwf   W_TEMP            ; save off current W register contents
    	movf    STATUS,W          ; move status register into W register
 	   	movwf   STATUS_TEMP       ; save off contents of STATUS register
 	   	movf    PCLATH,W          ; move pclath register into W register
   		movwf   PCLATH_TEMP       ; save off contents of PCLATH register
		
; Test to see if the interrupt was generated by TMR0

      	btfsc   INTCON,TMR0IF  	 ; TMR0 interrupt?
       	goto    tmr0int		  	 ; Yes
		btfsc	PIR1, TMR2IF
		goto	tmr2int
		goto	intout


tmr0int:
		bcf		INTCON, TMR0IF	 ; clear interrupt flag (HAVE TO DO THIS)

state_machine:
		movlw	.0
		xorwf	current_state, w
		btfsc	STATUS, Z
		goto	state_machine_hours
		movlw	.1
		xorwf	current_state, w
		btfsc	STATUS, Z
		goto	state_machine_min
		goto	state_machine_sec

state_machine_hours:
		bcf		SEC_EN
		comf	hour, w
		movwf	PORTB
		bsf		HOUR_EN
		incf	current_state, f
		goto	state_machine_end
state_machine_min:
		bcf		HOUR_EN
		comf	min, w
		movwf	PORTB
		bsf		MIN_EN
		incf	current_state, f
		goto	state_machine_end
state_machine_sec:
		bcf		MIN_EN
		comf	sec, w
		movwf	PORTB
		bsf		SEC_EN
		clrf	current_state
state_machine_end:
		goto	intout

tmr2int:
		bcf 	PIR1, TMR2IF	 ; clear interrupt flag (HAVE TO DO THIS)
	    banksel sec
        movf    sec,W       ; Bump the seconds count by one
        addlw   .7
        btfss   STATUS,DC
        addlw   -.6
        movwf   sec
        xorlw   h'60'           ; Have we reached 60?

intout:
    	movf    PCLATH_TEMP,W     ; retrieve copy of PCLATH register
    	movwf   PCLATH            ; restore pre-isr PCLATH register contents
    	movf    STATUS_TEMP,W     ; retrieve copy of STATUS register
    	movwf   STATUS            ; restore pre-isr STATUS register contents
    	swapf   W_TEMP,F
    	swapf   W_TEMP,W          ; restore pre-isr W register contents
    	retfie                    ; return from interrupt

;Delay - this is the delay I made
delay:
	movlw .255
	movwf counter
	del_loop:
	nop
	nop
	decfsz counter, f
	goto del_loop
	return

long_delay:
	movlw .255
	movwf counter_1
	long_del:60
	call delay
	decfsz counter_1, f
	goto long_del
	return
Any ideas to take a step forward and then as well how to bump the minutes by 1 after the seconds has reached 60?
 

Thread Starter

Carl03

Joined Oct 27, 2012
10
Ok. So I've my time keeping going really well with increments of 1 by the seconds every second...working perfectly. Now, on to the button programming. Can someone help me with it please?
 
Top