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.

Assembly delay problem (PIC16F88)

Status
Not open for further replies.

no1010

New Member
Hi,

I'm trying to call a delay of 25mS and I have googled and found a couple of websites with tutorials explaining delays, most of their delays look something like this:

Code:
_0mS
	retlw 0x00 	;delay 0mS - return immediately
_1mS
	movlw d'1' 	;delay 1mS
	 goto Delay
_5mS
	movlw d'5' 	;delay 5mS
	 goto Delay
_10mS
	movlw d'10' 	;delay 10mS
	 goto Delay
_20mS
	movlw d'20' 	;delay 20mS
	 goto Delay
_25mS
	movlw d'25' 	;delay 25mS
	 goto Delay
_50mS
	movlw d'50' 	;delay 50mS
	 goto Delay
_100mS
	movlw d'100' 	;delay 100mS
	 goto Delay
_250mS
	movlw d'250' 	;delay 250 ms
	 goto Delay
	 
Delay
	movwf counter0
d1 	movlw 0xC7 		;delay 1mS
	movwf counter1
	movlw 0x01
	movwf counter2
	decfsz counter2, f
	goto $+2
	decfsz counter2, f
	goto $-3
	 
	decfsz counter0 ,f
	goto d1
	retlw 0x00

The problem is that when I call a 25mS delay (I edited the delay routine I found here: MPASM Tutorial - Using Switches | MPASM Assembly) it just stalls, and never exits the delay. When I run the animate debug mode, I can see it keeps on looping here:
Code:
	decfsz counter2, f
	goto $+2
	decfsz counter2, f
	goto $-3
It skips the "goto $+2" and then jumps back up to "decfsz counter2, f" when it reaches "goto $-3".

This code:
Code:
_10mS
        movlw   0Ah
        movwf   temp2
        nop
        decfsz  temp1,f
        goto    $-2
        decfsz  temp2,f
        goto    $-4     
        retlw   00

From another site, does exactly the same thing...

I'm not sure why this is the way the delay routine is set up, I've seen it like this in a couple of places, but it just does not work!
...driving me nuts!

This works though... but the delay is roughly 140mS.
Was just experimenting and it worked. But if I change "movlw .1" to ".25" ... it stalls again... ???
Code:
_25mS
	movlw   .1
	movwf   counter0
	nop
	decfsz  counter1,f
	goto    $-2
	decfsz  counter0,f
	goto    $-4
	retlw   00

Thanks in advance?

For those interested:
Here is the entire .asm file - maybe you can spot where my mistake is.
I had the delays between ISR and Main, but put it last since I thought it might make a difference.
Code:
;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

     LIST      p=16F88              ; list directive to define processor
     #INCLUDE <P16F88.INC>          ; processor specific variable definitions

;------------------------------------------------------------------------------
; CONFIGURATION WORD SETUP
;------------------------------------------------------------------------------

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

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

    CBLOCK 0x20 ; Sample GPR variable registers allocated contiguously
        counter0  ; User variable
        counter1  ; User variable
        counter2  ; User variable
    ENDC

W_TEMP         EQU        0x7D  ; w register for context saving (ACCESS)
STATUS_TEMP    EQU        0x7E  ; status used for context saving (ACCESS)
PCLATH_TEMP    EQU        0x7F  ; variable used for context saving

;------------------------------------------------------------------------------
; EEPROM INITIALIZATION
;------------------------------------------------------------------------------

DATAEE    ORG  0x2100
    DE    "MCHP"  ; Place 'M' 'C' 'H' 'P' at address 0,1,2,3

;------------------------------------------------------------------------------
; RESET VECTOR
;------------------------------------------------------------------------------

RESET     ORG     0x0000            ; processor reset vector
          PAGESEL START
          GOTO    START             ; go to beginning of program

;------------------------------------------------------------------------------
; INTERRUPT SERVICE ROUTINE
;------------------------------------------------------------------------------

ISR       ORG     0x0004            ; interrupt vector location

;         Context saving for 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

;------------------------------------------------------------------------------
; USER INTERRUPT SERVICE ROUTINE GOES HERE
;------------------------------------------------------------------------------

;         Restore context before returning from interrupt
          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

;------------------------------------------------------------------------------
; MAIN PROGRAM
;------------------------------------------------------------------------------

START

TURNA  equ b'00000001'
TURNB  equ b'00000010'
TURNC  equ b'00001000'
TURND  equ b'00010000'

	banksel TRISB
	
	movlw b'11100100'	;Set bits 0,1,3,4 as outputs 2 give weird signal
	movwf TRISB
	
	banksel PORTB
		
loop
	movlw TURNA
	movwf PORTB

	call _25mS

	movlw TURNB
	movwf PORTB

	call _25mS

	movlw TURNC
	movwf PORTB

	call _25mS

	movlw TURND
	movwf PORTB

	call _25mS

	goto loop

;-----------;
;---Delay---;
;-----------;

_0mS
	retlw 0x00 	;delay 0mS - return immediately
_1mS
	movlw d'1' 	;delay 1mS
	 goto Delay
_5mS
	movlw d'5' 	;delay 5mS
	 goto Delay
_10mS
	movlw d'10' 	;delay 10mS
	 goto Delay
_20mS
	movlw d'20' 	;delay 20mS
	 goto Delay
_25mS
	movlw d'25' 	;delay 25mS
	 goto Delay
_50mS
	movlw d'50' 	;delay 50mS
	 goto Delay
_100mS
	movlw d'100' 	;delay 100mS
	 goto Delay
_250mS
	movlw d'250' 	;delay 250 ms
	 goto Delay
	 
Delay
	movwf counter0
d1 	movlw 0x00 		;delay 1mS
	movwf counter1
	movlw 0x00
	movwf counter2
	decfsz counter2, f
	goto $+2
	decfsz counter2, f
	goto $-3
	 
	decfsz counter0 ,f
	goto d1
	retlw 0x00

          END
 
It seems I made a stupid mistake.

The stalling is gone, and the delays work, but... they seem way slower than they are supposed to be.

I had the wrong counter values...
This:
Code:
Delay
	movwf counter0
d1 	movlw 0x00 		;delay 1mS
	movwf counter1
	movlw 0x00
	movwf counter2
	decfsz counter2, f
	goto $+2
	decfsz counter2, f
	goto $-3

Should have been this:

Code:
Delay
	movwf counter0
d1 	movlw 0xC7 		;delay 1mS
	movwf counter1
	movlw 0x01
	movwf counter2
	decfsz counter1, f
	goto $+2
	decfsz counter2, f
	goto $-3
 
I used to enjoy figuring out the delay counters and think it is good for experience to do. But now, I just go to: and use its program. The times are pretty close and easily adjusted, if needed.

John
 
Last edited:
decfsz counter2, f
goto $+2
decfsz counter2, f
goto $-3

The mistake in the above code is that one should have been counter1 and the other counter 2. It got decremented to 0, then decremented to 255 when it should have exited.
 
Hi thanks everyone for the replies!
Yes, I noticed that little error, when I fixed it, it worked.

I'll be using that calculator for the delays from now on, but right now I am more concerned with enabling the speed of the motor to be changed with a potentiometer...

From what I've read I can wire the pot, but I'm not sure how exactly to implement the code to change the speed of the delays.
My schematic looks like this:
**broken link removed**
(R5,R6,R7,R8 is supposed to be 47K not 470K as stated in the parts list)
(Big oops here! I switched the 100nf cap. It should be connected to ground and not positive! Thanks ericgibbs)

R13 is connected to RA1, which is supposed to be an analog input...

This person has done it, with a 12F*** (Stepper Motor Controller)
I can see where he calls the pot and that it returns a value, but I'm not sure how to use that value, basically, I would like to have it check the pot's value once before starting the loop, if it's fast enough, it shouldn't affect the speed of the motor at all.
I know in what order I want the code, but I'm not sure exactly how to do it with assembly.
Something like this:
Code:
----------
Pot routine
----------

----------
Loop start
Call Pot
set delay time
Do loop
goto Loop
----------

I'll be able to adapt this (from link above) and use it, but I don't understand what it is doing. (ie. change the ports to fit mine etc.)
Code:
	;position of pot creates a value in PotValue
		
Pot	bsf	status,rp0			
	bcf	trisio,0	;Make GP0 output
	bcf	status,rp0
	bcf	gpio,0		;make GP0 LOW	
	call	_1mS	;create delay to discharge 100n
	bsf	status,rp0			
	bsf	trisio,0	;Make GP0 input
	bcf	status,rp0	
	clrf	PotValue
	call	PotDel				
	incf	PotValue,f		
	btfss	gpio,0		;is input HIGH?	
	goto	$-3	
	retlw	00	;returns with a value in PotValue

Also, I cannot find where and how the potvalue influences the delay that is being called.

Also, I'm running the PIC in debug mode with the PICKIT connected. I had a little program that just had 4 LEDs running in sequence. When I programmed it and unplugged it from the PICKIT and gave it external power, it worked fine. However, if I try that now, it just doesn't work. Some LEDs don't power up etc.
Any ideas?
 
Last edited:
Ok,

I have edited the POT sub routine, does it look right?
I have some questions about it that I asked in comments in the first column.
Code:
Potentiometer				;position of pot creates a value in P_value
	bsf		STATUS,rp0			
	bcf		TRISA,2			;Make RA1 output
	bcf		STATUS,rp0
	bcf		PORTA,2			;make RA1 LOW	
	call	_1mS			;create delay to discharge 100n
;Ok so here it makes it an output so that the cap will discharge, why do you want it discharged?
	bsf		STATUS,rp0			
	bsf		TRISA,2			;Make RA1 input
	bcf		STATUS,rp0	
	clrf	P_value
;It clears any previous values?
	call	P_delay
;Why is a delay needed?
	incf	P_value,f
;This is where the value of the Pot is saved?
	btfss	PORTA,2			;is input HIGH?
;Why is this check done?
	goto	$-3
	retlw	0x00			;returns with a value in P_value
 
hi,
IF you are trying to discharge the 100n cap, your circuit diagram is incorrect...
 
The PIC in the original project from which you're basing your design did not have an ADC module. That's why the author was using the RC components.

Why not use the built-in ADC module on your 16F88 instead?

Regards, Mike
 
The PIC in the original project from which you're basing your design did not have an ADC module. That's why the author was using the RC components.

Why not use the built-in ADC module on your 16F88 instead?

Regards, Mike
The reason is quite simple, I don't know how!
:eek:
I've looked at the ADC in the datasheet, but honestly, it's gibberish to me...
I'd love to do it, I just need some pointers on where to start looking.
 
I'll take a quick look around for examples for you. The ADC module can be a bit tricky.

While I'm looking around, here's some code I used for a 12F683 which may give you an idea of what the ADC section of the Datasheet is trying to say;

Regards, Mike

Code:
;
;  setup ADC peripheral
;
        bsf     STATUS,RP0      ; bank 1                          |B1
        movlw   b'00010001'     ; 00010001                        |B1
                                ; 0-------  unused
                                ; -001----  Fosc/8 clock
                                ; ----0001  AN0 enabled
        movwf   ANSEL           ;                                 |B1
        movlw   b'00000001'     ; GP0/AN0 input                   |B1
        movwf   TRISIO          ;                                 |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
;
Code:
;
;  the sensor provides analog output of 0.0 to 5.0 volts or the
;  full ADC range of 0 to 1023 so we'll use left justification
;  with the 8 most significant bits of the result in ADRESH
;
Sensor  movlw   b'00000000'     ; setup ADC channel 0 (GP0)       |B0
                                ; 00000000
                                ; 0-------  left justified result
                                ; -0------  VDD reference
                                ; --00----  unused
                                ; ----00--  channel 0
                                ; ------0-  GO_DONE control bit
                                ; -------0  ADON control bit
        movwf   ADCON0          ;                                 |B0
;
;  turn on the ADC module, read Sensor on channel 0
;
        radix   dec

        bsf     ADCON0,ADON     ; turn ADC module on              |B0
        bcf     PIR1,ADIF       ; clear ADC interrupt flag        |B0
        movlw   20/4            ; 20 usecs / 4 cycle loop time    |B0
ACQ1    addlw   -1              ; delay 20 usecs to acquire       |B0
        skpz                    ; done? yes, skip, else           |B0
        goto    ACQ1            ; loop                            |B0
        bsf     ADCON0,GO_DONE  ; start conversion                |B0
ADX1    btfsc   ADCON0,GO_DONE  ; conversion complete?            |B0
        goto    ADX1            ; no, loop                        |B0
        bcf     ADCON0,ADON     ; turn off AD module              |B0
;
;  use most significant 8-bits of ADC reading in ADRESH reg'
;
 
Ok,
I've delved a bit into the data sheet for the PIC16F88 and I found this:
I've added my questions after the ***

1. Configure the A/D module:
• Configure analog/digital I/O (ANSEL) *** This sets which pins are to be used as analog pins.
• Configure voltage reference (ADCON1) *** I can guess what this is for, but how do I use it?
• Select A/D input channel (ADCON0) *** ? channel ?
• Select A/D conversion clock (ADCON0) *** The speed at which it runs?
• Turn on A/D module (ADCON0) *** It starts running?

2. Configure A/D interrupt (if desired): *** Will this be neccesary?
• Clear ADIF bit
• Set ADIE bit
• SET PEIE bit
• Set GIE bit

3. Wait the required acquisition time. *** How long is required?

4. Start conversion:
• Set GO/DONE bit (ADCON0) *** Bit2 of Adcon0?

5. Wait for A/D conversion to complete, by either: *** Which is better, interrupt or polling?
• Polling for the GO/DONE bit to be cleared
(with interrupts disabled); OR
• Waiting for the A/D interrupt

6. Read A/D Result register pair *** This makes no sense...
(ADRESH:ADRESL), clear bit ADIF if required.

7. For next conversion, go to step 1 or step 2 as *** TAD? So I should only look up the values every x loops?
required. The A/D conversion time per bit is *** how would I do that?
defined as TAD. A minimum wait of 2 TAD is
required before the next acquisition starts
 
no1010, To answer your questions:

Yes, ANSEL is used for setting which pins will be used an analog pins. You'll also need to set the corresponding bits in TRIS to mark them as inputs.

You can probably ignore ADCON1 if you just want to use the ADC normally. The default is to use Vss and Vdd as the voltage range.

ADCON0 input channel is used for setting which channel is to be converted, because you have to convert one channel at a time.

After that, yes, turn on the ADC.

Ignore the interrupts for now, unless you know you need them. You can just poll the GO/DONE bit.

See the datasheet for acquisition times.

Yes, setting the GO/DONE bit starts the conversion.

And without interrupts you just poll the GO/DONE bit until it's clear. It really doesn't take long, so unless your program is time sensitive you can simply wait for it by looping until the bit is clear.

The result is put into a register pair, because the result is 10 bits and the registers are only 8 bits wide...so there's a HIGH byte and a LOW byte.

Often you can just go right into converting another channel (or the same one again), because of the instructions you'll be executing before starting another conversion.

Go through Nigel's tutorials, because I'm quite sure he has one on using the ADC.

Mike
 
Thanks Mike!

I've had a busy few days, only had a chance to reply now.
Thanks for pointing out that I don't have to worry about ADCON0, since it threw me entirely!
The project is due Friday, so hopefully I'll get it done by then!

Chris
 
Hi again.

I have not had the time to implement the A2D converter yet. I would still like to do so, but since my time is running out (the project presentation is on Friday) I thought about using a tactile switch to increase the speed one step at a time.

Ie, decrease the delay by one step for ten steps and then start at the bottom again.
Thus, the delay starts long, say 100mS (not realistic) and decrease in increments of 10mS per button press to 1mS minimum.

The problem is, I have no clue how to continuously monitor for a button press, while still running the loop that drives the stepper motor.
Interrupts?
How would I go about altering the delay with the button press?
I also want to use this to display the speed of the motor in ten steps on 4 LEDs in binary, so, same as the delay, I need to change which LEDs light up depending on the length of the delay.

Unfortunately I lost a week trying to get an amplifying stage in the stepper motor control to work. I reverted to using the transistors as switches thanks to the help found here regarding the resistor values needed.
So I'm in a bit of a pinch right now!
 
I just want to say thank you to everyone that had a hand in helping me with my project. I learned quite a bit thanks to all of you!

Regards,
1010
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top