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.

Frustrating PIC coding

Status
Not open for further replies.

asmpic

New Member
I'm relatively new as far as assembly programming for PIC's go, actually yesterday was the first day I used assembly language. I'm trying to program a PIC16F84 to perform pretty simple operations. I need it to increment when the opcode(stored in 0x22) is 0x00, decrement when its 0x01, complement when its 0x02, and clear when it's 0x03. The operations need to be performed on registers 0x30 through 0x37, depending on what 0x20 is. If 0x20 is a value of 2, then operations will be performed twice (the value of the register will be decremented until 0 is reached) I coded what I thought would be the correct way of doing this but for some reason it the program likes to run the clear operation repeatedly. If anyone can just look over my code and see where the problem is that would be very much appreciated.

My code is as follows:


list P=16F84
include P16F84.INC

; Define the direction bit types
f equ 1
w equ 0

; Define the data storage locations
org 0x20
cnt res 1 ;the number of instructions to execute(1<=n<=8)
acc res 1 ;the accumulator


org 0x30
prg res 8 ;location of the program

; start defining the program
; no interrupts, so start at 0x0


org 0x0

movf 0x20, w
movwf 0x40

Fetch movlw 0x20
movwf FSR
movf INDF, w
movwf 0x22
call EXE
incf FSR,f
decfsz 0x40, f
goto Fetch
goto endless

EXE
BTFSS 0x22, 0
goto Bit_0_is_0
goto Bit_0_is_1

Bit_0_is_0 BTFSS 0x22,1
incf 0x30,0
comf 0x30,0

Bit_0_is_1 BTFSS 0x22,1
decf 0x30, 0
clrf 0x30
return


endless nop
goto endless
end
 
For a start I suggest you look at my tutorials, and see how to define your variables - the ORG statement isn't the way to do it!. There are various ways, but I would suggest the 'cblock' method.
Code:
	cblock 	0x20 			;start of general purpose registers
		count1 			;used in delay routine
		counta 			;used in delay routine 
		countb 			;used in delay routine
	endc

Also I can't make much sense of the code, what are the first two lines doing? - and why aren't you using names for the two variables? (0x20 and 0x40).
 
Nigel Goodwin said:
For a start I suggest you look at my tutorials, and see how to define your variables - the ORG statement isn't the way to do it!. There are various ways, but I would suggest the 'cblock' method.
Code:
	cblock 	0x20 			;start of general purpose registers
		count1 			;used in delay routine
		counta 			;used in delay routine 
		countb 			;used in delay routine
	endc

Also I can't make much sense of the code, what are the first two lines doing? - and why aren't you using names for the two variables? (0x20 and 0x40).

The first two lines are storing the current value of the register 0x20 into 0x40 so it can act as a counter without actually decrementing the 0x20
register.

I've updated my code alittle bit, after realizes several flaws at the subroutine section. Now my problem is that after one operation is performed it needs to somehow increment up from the current register it's getting it's values from and perform the operation on the new register. So when it performs the operation initially on 0x30 it needs to get perform the next operation on 0x31 and so on, until the value in 0x20 reaches 0. I hope this makes sense because it seems kind of confusing.



Code:
	list P=16F84
	include	P16F84.INC

; Define the direction bit types
f	equ	1
w	equ	0

; Define the data storage locations
			org	0x20
cnt			res	1	;the number of instructions to execute (1<=n<=8)
acc			res 1	;the accumulator

			org	0x30
prg			res	8	;location of the program

; start defining the program
; no interrupts, so start at 0x0

	org	0x00
movf 0x20, w
movwf 0x40

Fetch	movlw 0x20
		movwf FSR
		movf INDF, w
		movwf 0x22
			call EXE
		decf INDF,f
		decfsz 0x40, f
		goto Fetch
		goto endless

EXE
	BTFSS 0x22, 0
	goto Bit_0_is_0
	goto Bit_0_is_1

Bit_0_is_0	BTFSS 0x22,0
			goto INC
			goto COM

Bit_0_is_1	BTFSS 0x22,1
			goto DEC
			goto CLR


INC
	incf 0x30,1
return

COM
	comf 0x30,1
return

DEC
	decf 0x30,1
return

CLR
	clrf 0x30
return

endless	nop
		goto	endless
		end
 
asmpic said:
The first two lines are storing the current value of the register 0x20 into 0x40 so it can act as a counter without actually decrementing the 0x20
register.

But why?, the value in 0x20 is never used again!. Also what value does 0x20 have.

I've updated my code alittle bit, after realizes several flaws.


You still haven't defined your variables correctly, nor have you used names for the variables - this makes it very difficult to read, and prone to errors.

You also don't set the config fuses in your source file, this is very important. Here's a disassembled listing of what your first listing produces after it's been assembled.

Code:
; Generated by WinPicProg 1.95c, (c) Nigel Goodwin Jun 2004.

            LIST      P=16F84, F=INHX8M
            include "P16F84.inc"
            __CONFIG 0x3FFF

; Variable definitions
Var_0001    EQU     0x20
Var_0002    EQU     0x40
Var_0003    EQU     0x22
Var_0004    EQU     0x30

            ORG     0x0000

            MOVF    Var_0001  , W
            MOVWF   Var_0002
Label_0002  MOVLW   0x20
            MOVWF   FSR
            MOVF    INDF      , W
            MOVWF   Var_0003
            CALL    Label_0001
            INCF    FSR       , f
            DECFSZ  Var_0002  , f
            GOTO    Label_0002
            GOTO    Label_0003
Label_0001  BTFSS   Var_0003  , 00
            GOTO    Label_0004
            GOTO    Label_0005
Label_0004  BTFSS   Var_0003  , 01
            INCF    Var_0004  , W
            COMF    Var_0004  , W
Label_0005  BTFSS   Var_0003  , 01
            DECF    Var_0004  , W
            CLRF    Var_0004
            RETURN
Label_0003  NOP
            GOTO    Label_0003
 
            END
 
Yes your are correct I do not need the register 0x40 to decrement since it's not used again but the instruction specifically say not to modify locations 0x20. Tthe value in 0x20 will be 08 (the number of instructions that have to be performed). I'm not really sure how I would implement your method of defining variables though, I'm a little confused on that. But I think my bigger problem is how to increment the registers so I'm performing each operation on a different register.

Updated code using names for variables
Code:
	list P=16F84
	include	P16F84.INC

; Define the direction bit types
f	equ	1
w	equ	0

; Define the data storage locations
			org	0x20
cnt			res	1	;the number of instructions to execute (1<=n<=8)
acc			res 1	;the accumulator
opc			equ 0x22 ;opcode is stored here
counter		equ 0x40 ;allows register 0x20 to decrement without value change.

			org	0x30
prg			res	8	;location of the program

; start defining the program
; no interrupts, so start at 0x0

	org	0x00
movf cnt, w
movwf counter

Fetch	movlw cnt
		movwf FSR
		movf INDF, w
		movwf opc
			call EXE
		decf INDF,f
		decfsz counter, f
		goto Fetch
		goto endless

EXE
	BTFSS opc, 0
	goto Bit_0_is_0
	goto Bit_0_is_1

Bit_0_is_0	BTFSS opc,0
			goto INC
			goto COM

Bit_0_is_1	BTFSS opc,1
			goto DEC
			goto CLR


INC
	incf 0x30,1
return

COM
	comf 0x30,1
return

DEC
	decf 0x30,1
return

CLR
	clrf 0x30
return

endless	nop
		goto	endless
		end
 
asmpic said:
Yes your are correct I do not need the register 0x40 to decrement since it's not used again but the instruction specifically say not to modify locations 0x20. Tthe value in 0x20 will be 08 (the number of instructions that have to be performed). I'm not really sure how I would implement your method of defining variables though, I'm a little confused on that. But I think my bigger problem is how to increment the registers so I'm performing each operation on a different register.

I'm still very confused as to what you are trying to do, your definitions at the beginning of the code still don't make any sense?. You also don't load any of your variables with the values you require?, and what's with the RES instruction? - that's not how you allocate GPR's.

I've posted two different methods above - CBLOCK and EQU, either work perfectly, and are the usual method of doing it.

You don't need to define 'w' and 'f', they are already defined in the INCLUDE file - which is what it's there for!.

But you MUST load any variables with the values they start with, otherwise they will just be random.

For examples of how to write PIC assembler, have a look at my tutorials.

Updated code using names for variables
Code:
	list P=16F84
	include	P16F84.INC

; Define the direction bit types
f	equ	1
w	equ	0

; Define the data storage locations
			org	0x20
cnt			res	1	;the number of instructions to execute (1<=n<=8)
acc			res 1	;the accumulator
opc			equ 0x22 ;opcode is stored here
counter		equ 0x40 ;allows register 0x20 to decrement without value change.

			org	0x30
prg			res	8	;location of the program

What's with the last two lines?, why is it commented ";location of the program"?.
 
Ok, basically the entire beginning of the code was provided to me by my professor. He told the class to use this as a starting point and add to it to make the actual program. I did not define w,f, or prg. My actual code start with "movf cnt, w" So I'm not really sure I'm suppose to edit his part...
 
I'm going to try my best to explain the program to you. I have to assign a value(N) to the 0x20 and that determines how many times the program is to be ran. Registers 0x30 through 0x30+(N-1) store the actual opcodes which specifies which operation to perform, 0=increment 0x21, 1=decrement 0x21, 2=complement 0x21 and 3=clear 0x21. When the program is executed it is not to modify location 0x21 and 0x30 through 0x30+(N-1). So, my code is bascially wrong because I just realized that the opcodes are stored in registers 0x30 through 0x30+(N-1) and not 0x22. But that's pretty easy to fix, however my biggest problem is how do I increment registers only and not there values?
 
asmpic said:
Ok, basically the entire beginning of the code was provided to me by my professor. He told the class to use this as a starting point and add to it to make the actual program. I did not define w,f, or prg. My actual code start with "movf cnt, w" So I'm not really sure I'm suppose to edit his part...

I should tell your professor to join these forums, if he's providing code like that as a teaching aid he's not much of a professor!.
 
Nigel Goodwin said:
asmpic said:
Ok, basically the entire beginning of the code was provided to me by my professor. He told the class to use this as a starting point and add to it to make the actual program. I did not define w,f, or prg. My actual code start with "movf cnt, w" So I'm not really sure I'm suppose to edit his part...

I should tell your professor to join these forums, if he's providing code like that as a teaching aid he's not much of a professor!.


Do you know how to increment actual registers and not their corresponding values?
 
asmpic said:
Do you know how to increment actual registers and not their corresponding values?

I'm not even sure what the question means?.

But a 14 bit PIC only has 35 instructions - so a very short glance at the MPASM helpfile shows you that there are only two possible commands

INCF
INCFSZ

The first increments a GPR, the second increments a GPR and skips the next line if the result equals zero.

So if you have a variable (GPR) called Test, then

INCF Test, f

Will increment it's value by one, and write the result back to Test.

INCF Test, w

Will increment the value by one, and put the result in the W register.

If you mean how do you do an index to the registers?, in general terms you don't! - however, there is a special register (not a GPR) called FSR which allows you to do this - which you look to be trying to use.

But because you're not setting variables up properly, nor initialising their values, your code will never work.

If you disassemble your code after you've compiled it (WinPicProg does this really well) you can see what your code is actually producing - in the earlier sample I disassembled the first two lines copy a variable to W (this variable has not been assigned a value though), and then writes it to another variable. This results in a random value being written to the second variable - which you really don't want!.
 
Nigel Goodwin said:
asmpic said:
Do you know how to increment actual registers and not their corresponding values?

I'm not even sure what the question means?.

But a 14 bit PIC only has 35 instructions - so a very short glance at the MPASM helpfile shows you that there are only two possible commands

INCF
INCFSZ

The first increments a GPR, the second increments a GPR and skips the next line if the result equals zero.

So if you have a variable (GPR) called Test, then

INCF Test, f

Will increment it's value by one, and write the result back to Test.

INCF Test, w

Will increment the value by one, and put the result in the W register.

If you mean how do you do an index to the registers?, in general terms you don't! - however, there is a special register (not a GPR) called FSR which allows you to do this - which you look to be trying to use.

But because you're not setting variables up properly, nor initialising their values, your code will never work.

If you disassemble your code after you've compiled it (WinPicProg does this really well) you can see what your code is actually producing - in the earlier sample I disassembled the first two lines copy a variable to W (this variable has not been assigned a value though), and then writes it to another variable. This results in a random value being written to the second variable - which you really don't want!.

The reason why I don't set the value of the variable in my code is because I do it externally. I'm using MPLAB and just enter the values for each register in the file registers window.
 
I know I can use FSR to index the registers but now that I'm already using it to perform another function(decrement 0x20) how can I add it again?
 
I had a quick glance at your code and it looks like your professor asked you to make a simple pseudo processor using a PIC16F84. This pseudo processor sequentially executes pseudo ops located in the file register range 0x30-0x37, max of 8 ops in all.

Using the FSR, to sequentially load the psuedo ops (Fetch) is of course the way to go. You load the FSR with the value of a pseudo program counter . This could be the file register "counter". It should be initialized to 0 at start.

Code:
Start:
    call        Init           ; Of course, you need to init 0x30-0x37 with codes
    clrf      counter       ; Init to 0
Loop:
    call       Fetch
    call       Execute       
;
    incf      counter,f     ; increment the program counter
;
    movf      cnt,w          ; contains the number of codes.
    subwf     counter,w    ; compare with the program counter
    btfss     status,carry
    goto      Loop
;
    goto      Stop
Fetch:
    movlw     prg             ; = 0x30, the start of op code storage
    addwf     counter,w     
    movwf     fsr 
;
    movf      indf,w          ; finally fetch the op code
    movwf     opc            ; and store in the opc holding tank for decode
    return

I hope this gets you started. I am leaving the execute portion of the code to the Jedi paduan.
 
Thanks motion and Nigel Goodwin for all your help, I think I finally figured this out. I truely appreciate all the help I recieved.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top