Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

16F628 timing problem

Status
Not open for further replies.

ahydra

New Member
Got my PIC programmer today at long last, and below is my first program for my 16F628, designed to flash an LED connected to output RA1 at 1Hz...

Code:
  LIST      P=16F628     	
  #INCLUDE <P16F628.INC> 	
  __config 0x3D18 ; set up config bits (code protection off etc)	

C1 EQU 0x20
  
  ORG 0x00


MAIN
  CLRF PORTA 

  MOVLW 0x07 
  MOVWF CMCON

  BCF STATUS, RP1 
  BSF STATUS, RP0 	; go to bank 1

  MOVLW 0x11 
  MOVWF TRISA 		; A: 4 and 0 as inputs, others out
  MOVLW 0x00
  MOVWF TRISB		; B: all out

  MOVLW d'249'
  MOVWF PR2 		; set Timer2 to count up to 250

  BCF STATUS, RP0 	; go back to bank 0
  MOVLW 0x7E
  MOVWF T2CON 		; enable timer2 with a prescaler of 16 (postscaler too, but this isn't used)

  MOVLW d'125'
  MOVWF C1		; initialise C1

LOOP			; main loop begins here
  MOVF TMR2, 0		; 1000000 / 16 = 62500 hz
  BTFSC STATUS, Z 	; test zero flag, if not zero goto FLASH
  GOTO FLASH
  GOTO LOOP

FLASH  
  ; gets to here 62500 / 250 = 250 hz
  ; so we use C1 to divide by 125, and we get 2Hz
  ; that is one flash (one on/off sequence) every second 
  
  DECF C1, 0
  MOVWF C1		; store C1
  MOVWF PORTB		; also copy C1 to port B (debug)
  BTFSS STATUS, Z	; test to see if C1 was 0
  GOTO LOOP2		; if it wasn't, go to final loop
  
  MOVLW d'125' 	
  MOVWF C1		; reset C1
    
  MOVF PORTA, 0		; read PORTA
  XORLW 0x2		; toggle bit 1
  MOVWF PORTA 		; write PORTA
 
LOOP2  
  MOVF TMR2, 0		; test tmr2 for non-zero before returning
  BTFSS STATUS, Z	; test zero flag, when not zero resume the loop
  GOTO LOOP			
  GOTO LOOP2
  
  END

The circuit at first appears to work - but the frequency of the LED flash is slightly faster than 1Hz. I'm not sure what the exact frequency is, but I measured 48.5 seconds for 50 flashes (1.03 Hz) and then 62 flashes in a minute (1.03 Hz again). The maths is correct according to the datasheet, and I've run the code in a simulator which agrees with me and turns RA1 on and off every second - so where have I gone wrong?

There's no way the code takes an entire second to "reset" after the bit toggling part, so is there something wrong with my 16F628's internal clock? Note that the initial __config sets the chip to use the internal 4MHz oscillator; the source frequency of Timer2 is FOsc/4 = 1MHz.

Help would be much appreciated :) Merry Christmas/happy holidays to all.

ahydra
 
Last edited:
3v0 posted in another thread "ensure WDT is disabled". I have checked and it is (the config word has the WDT enable bit, bit 2, off).

ahydra
 
The internal oscillator on a 628 can vary by up to 6% and so your 3% error is to be expected. The 628A has a factory calibrated oscillator that is accurate to 1%. Without a crystal you will get large variations between chips and even when the temperature changes.

Mike.
 
They guys who read PIC asm are off doing what people do on holidays. I gave it my best shot and it looks good.:)

Is the part a 16F628 or 16F628A. The A version has a percison 1% 4MHz internal osc. The non A version does not. According to your code it is the non A. Which makes sense if it is off by 3%.

If you have the non A check to see if this works for your chip.

PICmicro™
Mid-Range MCU Family
Reference Manual


2.5 Internal 4 MHz RC Oscillator
The internal RC oscillator (not on all devices) provides a fixed 4 MHz (nominal) system clock at
VDD = 5V and 25°C, see the device data sheet’s “Electrical Specifications” section for information
on variation over voltage and temperature.
The value in the OSCCAL register is used to tune the frequency of the internal RC oscillator. The
calibration value that Microchip programs into the device will “trim” the internal oscillator to
remove process variation from the oscillator frequency. The CAL3:CAL0 bits are used for fine calibration
within a frequency window. Higher values of CAL3:CAL0 (from 0000 to 1111) yields
higher clock speeds.
When a 4 MHz internal RC oscillator frequency cannot be achieved by a CAL3:CAL0 value, the
RC oscillator frequency can be increased or decreased by an offset frequency. The CALFST and
CALSLW bits are used to enable a positive or negative frequency offset to place the internal RC
frequency within the CAL3:CAL0 trim window.
 
Meh, I was hoping to use the chip as an accurate timing source. Time to buy a 1MHz crystal or something :) You can wire the crystals up to the 16F628, so there's still hope for any type of clock project I may make in the future.

Thanks for the advice Mike.

ahydra

edit: 3v0, if only :( It appears the 16F627/8 don't have these registers.
 
Last edited:
For an accurate clock you can use a 32768Hz crystal as the timer1 source. If you look at the timer1 section of the 16F88 datasheet it has example code for doing a RTC.

<edit> You do of course still use the 4MHz intosc as the instruction clock.</edit>

Mike.
 
Last edited:
Looking at the datasheet I think I'd rather use timer0 as it leaves the entire of the PORTB untouched (timer0's source pin is RA4). In addition timer0 has a wider variety of prescaler options.

I do have a 32768 crystal and working circuitry for it, but lack of desk space is currently preventing me testing anything. :) If I wanted a higher resolution, what do you think the maximum supported speed would be (probably 1MHz or less, given the speed of the intosc)?

ahydra
 
To use timer0 you have to have a complete external oscillator whereas timer1 has the oscillator circuitry inbuilt. You can of course use any crystal up to 20MHz as the main oscillator and use the normal oscillator pins.

Mike.
 
For an accurate clock you can use a 32768Hz crystal as the timer1 source. If you look at the timer1 section of the 16F88 datasheet it has example code for doing a RTC.

<edit> You do of course still use the 4MHz intosc as the instruction clock.</edit>

Mike.

ahydra,

The above is the best way to go. Pommie is dead on.

The pics are wonderful chips, but they do not always have the internal options on the pin/port you might like.

Do you need a full port for reading parallel data in one read or something? Or like one port for inputs and port for outputs? It rarely works out for me, as they have small pin counts and so many features inside those little chips. :)
 
Got my PIC programmer today at long last, and below is my first program for my 16F628, designed to flash an LED connected to output RA1 at 1Hz...

Code:
  LIST      P=16F628     	
  #INCLUDE <P16F628.INC> 	
  __config 0x3D18 ; set up config bits (code protection off etc)
Here's a good tip. Don't use a raw number to do your config. That's stupid. When you do it forces you to look up all those values to see what you have enabled/disabled. Too much work.

Instead, use the equates defined in the include file that you included anyway. Like this:
Code:
	__config	_INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF
You can then tell at a glance what you have enabled and disabled.

If you want to get the list so you use the correct ones, scroll right down to the end of that include file. Some chips have it right at the bottom, some near the bottom. The include file is (typically) at c:\Program Files\Microchip\MPASM Suite. There's one for every supported PIC there.

The 16F628 one looks like this:
Code:
;==========================================================================
;
;       Configuration Bits
;
;==========================================================================

_BODEN_ON                    EQU     H'3FFF'
_BODEN_OFF                   EQU     H'3FBF'
_CP_ALL                      EQU     H'03FF'
_CP_75                       EQU     H'17FF'
_CP_50                       EQU     H'2BFF'
_CP_OFF                      EQU     H'3FFF'
_DATA_CP_ON                  EQU     H'3EFF'
_DATA_CP_OFF                 EQU     H'3FFF'
_PWRTE_OFF                   EQU     H'3FFF'
_PWRTE_ON                    EQU     H'3FF7'
_WDT_ON                      EQU     H'3FFF'
_WDT_OFF                     EQU     H'3FFB'
_LVP_ON                      EQU     H'3FFF'
_LVP_OFF                     EQU     H'3F7F'
_MCLRE_ON                    EQU     H'3FFF'
_MCLRE_OFF                   EQU     H'3FDF'
_ER_OSC_CLKOUT               EQU     H'3FFF'
_ER_OSC_NOCLKOUT             EQU     H'3FFE'
_INTRC_OSC_CLKOUT            EQU     H'3FFD'
_INTRC_OSC_NOCLKOUT          EQU     H'3FFC'
_EXTCLK_OSC                  EQU     H'3FEF'
_LP_OSC                      EQU     H'3FEC'
_XT_OSC                      EQU     H'3FED'
_HS_OSC                      EQU     H'3FEE'


---------------------------------------------------


OK. Tip number two. Defining your variables like this
Code:
C1	EQU	0x20
is dumb! Use a cblock, like this:
Code:
	cblock	0x20
	C1,c2,c3,d1,d2,d3
	endc
The 0x20 after cblock defines the start of the block of free RAM where you want your variables. I put in six of them as an example. You can have multiple blocks. You can define multi-byte variables.


---------------------------------------------------


Tip number three! All uppercase code is NOT necessary, and it's difficult to read. But if you prefer ugly uppercase that's ok. :p Here's what I prefer:
Code:
loop	call	delay		;count one second
	incf	secs,f		;increment seconds
	movf	secs,w		;get secs in W
	sublw	60		;is it 60?
	btfss	STATUS,Z
	goto	loop2		;no, display clock
	clrf	secs		;yes, clear seconds


---------------------------------------------------


Rather than do your bank changes with this slightly cryptic piece of code:
Code:
	BCF	STATUS, RP1 
	BSF	STATUS, RP0	; go to bank 1
you can use the banksel directive to do it in one line, clearly and easily, like this:
Code:
init	banksel	TRISA		;bank 1
	movlw	b'01110001'	;8MHz internal clock
	movwf	OSCCON
	clrf	TRISA		;all outs
	clrf	TRISB
	movlw	0x07		;turn comparators off
	movwf	CMCON
	clrf	ANSEL		;all pins digital
	banksel	PORTA		;bank 0
	clrf	PORTA
	clrf	PORTB
Fairly self explanatory, I think. :p
 
ahydra,

The above is the best way to go. Pommie is dead on.

The pics are wonderful chips, but they do not always have the internal options on the pin/port you might like.

Do you need a full port for reading parallel data in one read or something? Or like one port for inputs and port for outputs? It rarely works out for me, as they have small pin counts and so many features inside those little chips. :)

Well I was at the time considering a 7seg display where it would be useful to have that connected to, for example, PORTB 6:0. Constructing an external oscillator is not hard (2 caps, 2 resistors, crystal) - and I've actually got the required components for it whereas I don't for the timer1 oscillator circuit :)

Not to mention that reading Timer1 is a lot more complex because it's a 2-byte timer, though I'm not exactly scared of that.

Right, now Futz's attack on my coding style... I thought the idea of a coding style was that each person has their own. I do prefer capital letters when writing asm code (it's not that ugly!), and I know about cblocks, but figured it wasn't necessary here as I've only got one variable. Hardly "dumb", right?

You have a valid point about the config bits, but they're one of those things that you decide at the start of the code and then leave. Also I'd like to attack your style of giving examples on a forum :) It's best to ensure that your replacement code is an equivalent copy of what you're replacing - if you miss bits out, put some dots or "etc" or something. Here you missed two of the config bits but didn't warn me, so if I'd copy/pasted your code into my program it may not have worked as expected. The replacement code should be

Code:
__config	_INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF

Thanks for the tip about banksel, I will check that out. :)

ahydra
 
Last edited:
Right, now Futz's attack on my coding style...
Not an attack. Merely some suggestions. Take em or leave em. Your choice. :D As you say, some of it is personal preference.

The post was maybe a tad grumpy. It was done early morning, pre-coffee. :D

(it's not that ugly!),
Yes it is. :p

You have a valid point about the config bits, but they're one of those things that you decide at the start of the code and then leave. Also I'd like to attack your style of giving examples on a forum :) It's best to ensure that your replacement code is an equivalent copy of what you're replacing - if you miss bits out, put some dots or "etc" or something. Here you missed two of the config bits but didn't warn me, so if I'd copy/pasted your code into my program it may not have worked as expected.
The reason I didn't bother to figure out what your number meant was that it's just too much work (I rest my case :p). I give you a simple example. It's up to you to work out how to do it for your program.
 
Last edited:
Well I was at the time considering a 7seg display where it would be useful to have that connected to, for example, PORTB 6:0. Constructing an external oscillator is not hard (2 caps, 2 resistors, crystal) - and I've actually got the required components for it whereas I don't for the timer1 oscillator circuit :)

Ahh now it make more sense your way. There was a lot of missing details. The display you can show states, or display what you RTC is doing (assuming that is the final intent). I normally blink an LED, cheap and lazy but works.

If you have an ICD2, you can use an equipped chip, like the 16F88 and debug it all in MPLABS, then flip it to the 16F628.
 
futz said:
Right, now Futz's attack on my coding style... I thought the idea of a coding style was that each person has their own.


Futz was not out of line. He was right. Bad style makes programs hard to read and debug.

Many software houses have style guidlines that they must follow. There are programs to enforce style such as StyleCop used by some C# developers.

As an individual we have a great freedom. But good style still works to our advantage.

3v0
 
ahydra,

I agree. Futz has taken the time to recommend "tips" to help you on your way to becoming a more proficient PIC programmer. It's difficult for me to see how you can interpret his "tips" as an attack.

I would only point out that not all of Futz' "tips" are accepted by every accomplished assembly language programmer as "the correct way" or "the only way". For example, it's perfectly acceptable and in some cases an advantage using the "equ" directive to assign absolute variable addresses. There's also nothing wrong with using individual "bcf/bsf STATUS,RP0" and/or "bcf/bsf STATUS,RP1" instructions to select banks. Some assembly language programmers prefer these instructions to better track banking and in order to optimize the output code.

Good luck with your studies. Kind regards, Mike
 
Last edited:
In the old days we used a hi-level program design language (looked like BASIC and C). Then added the ASM between the statements. Assemblers ran for 20 minutes to a couple hours on a Super Mini VAX. Now things are so fast, emulators are under $99.

I do as Fitz says on all but equates as they have been on all the chips I've used back then and now.

If you are the only one maintaining your code, do it the way you like. Comments, lots of them are nice too. I have gone back to code I thought I would never see again and looked at it and thought "What was I thinking here and what does this routine do".. :D Fitz was just trying to help, and in a programming team you will need to have some standards.
 
Last edited:
Yep - I am part of a "programming team" so when we seriously get going we will need to agree style. But in terms of developing things for a 16F628, I'm the only one programming so I reserve the right to keep using capital letters :)

My ASM code is always full of comments, otherwise even I can't remember what the code's meant to do :D Sorry for use of the word "attack" but with terms like "dumb" and "stupid" being thrown around it felt like one. Still,

The post was maybe a tad grumpy. It was done early morning, pre-coffee.

probably explains it :)

I haven't decided what I'm going to build yet, first I would like to connect my oscillator circuit up. I read on another thread that I must keep MCLR high (i.e. resistor between pin 14 and pin 4) - is this always the case?

Thanks all,

ahydra
 
Well I was at the time considering a 7seg display where it would be useful to have that connected to, for example, PORTB 6:0. Constructing an external oscillator is not hard (2 caps, 2 resistors, crystal) - and I've actually got the required components for it whereas I don't for the timer1 oscillator circuit :)
ahydra

The crystal and 2 caps construction will work for the the main oscillator or for timer1 oscillator. For the timer0 input your external circuit needs to amplify the signal to supply a square wave to the pic. From your description of your requirements (so far) it sounds like you will struggle due to lack of I/O. Why not switch to a bigger chip such as the 16F886 which is a much more capable chip. It's only 5 pins longer and the same width as a 628.

On style, I use lower case but maintain upper case for the Microchip defined variables,
Code:
		movlw	B'01110000'	;select 8MHz clock
		movwf	OSCCON
		btfss	OSCCON,OSTS	;zero if on int osc
		bsf	OSCCON,SCS	;switch to internal clock
		bsf	STATUS,RP1
		movlw	b'00000011'
		movwf	ANSEL		;no ADCs
		clrf	ANSELH
		bcf	STATUS,RP1
		bcf	STATUS,RP0
		movlw	B'00000001'
		movwf	T1CON		;enable timer 1
I also hate the banksel macros as they waste a lot of rom space and you need to know which bank registers are in anyway.

Mike.
 
The crystal and 2 caps construction will work for the the main oscillator or for timer1 oscillator. For the timer0 input your external circuit needs to amplify the signal to supply a square wave to the pic. From your description of your requirements (so far) it sounds like you will struggle due to lack of I/O. Why not switch to a bigger chip such as the 16F886 which is a much more capable chip. It's only 5 pins longer and the same width as a 628.

My crystal circuit consists of one part of a 4069 (NOT gate), one each of 10M and 220k resistors, 2 33pf capacitors and a 32768Hz crystal. It can trigger a CMOS chip's input so surely it can work with the PIC as well?

Code:
  |-<32768>-----|
  |             |
  --[10M]-[220k]--> out     Legend: <32768> = crystal, [ ] = resistor, ( ) = capacitor
  |      |      |                All junctions are connections
  --|>o--|      |
  |             |
 (33)          (33)
  |             |
  0V            0V

(side note: will this also work if I replace the crystal with a 2Mhz one?)

I'm going to get a mammoth-sized 16F877A which is 40pins, it has 5 banks and an ADC. That combined with the two 16F628s I have will certainly be enough (at least for now) :)

I also hate the banksel macros as they waste a lot of rom space and you need to know which bank registers are in anyway.

Hmm, odd that the compiler wouldn't just compile them into the two lines of BCF/BSF'ing the status register. Even if it did that though, you are still occasionally going to save one line by doing it yourself (by choosing a good order to switch between the banks in).

ahydra
 
Last edited:
May I suggest the 887 rather than the 877A as this is the modern replacement. The 16F877A does not have an internal oscillator.

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top