Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

PIC16F887 I/O problem in assembly. please help urgent

Status
Not open for further replies.

Noman Yousaf

Full Member level 4
Joined
Nov 19, 2003
Messages
208
Helped
3
Reputation
6
Reaction score
2
Trophy points
1,298
Location
Lahore Pakistan
Activity points
1,763
hi all

i am not very new in assembly. but i am wondering that why i cant solve this very very simple I/O problem.



i want to use portc,2 and portc,3 as input an portc,4 and portc,5 as output and a simple delay.
(other all ports are set for their jobs)

when portc2 will hi, portc4 will lo and when portc2 will lo, portc4 will ho.

and same will be done with portc3 to portc5.

problem is, when only "chk_sw" routine is run, it works good and if i add a "delay", only portc,3 and portc,5 work, not portc2 and portc4.

I am getting mad that why delay is turning off the working of first portion of chk_sw routine.

can anybody help me.

i am stuck.

code is
Code:
LIST   P=PIC16F887
        PROCESSOR  16F887
        #include "P16F887.INC"

        BANKSEL        TRISC
            
        MOVLW        03FH
        MOVWF        TRISA
        MOVLW        0FH
        MOVWF        TRISB
        MOVLW        0CFH
        MOVWF        TRISC
        MOVLW        00H
        MOVWF        TRISD
        BANKSEL        ANSEL
        CLRF        ANSEL
        BANKSEL        ANSELH
        CLRF        ANSELH    


        BANKSEL        PORTA
        MOVLW        0FFH
        MOVWF        PORTA
        MOVWF        PORTB
        MOVWF        PORTD
        MOVLW        0CFH
        MOVWF        PORTC    

        
        
    


MAIN:
        CALL        CHK_SW         
        
        CALL        DELAY        ;MAKE A DELAY
        GOTO        MAIN

    
DELAY:
        
        MOVLW        08H
        MOVWF        30H

        MOVLW        0FFH
        
        MOVWF        31H
        MOVWF        32H

        DECFSZ        32H,F
        GOTO        $-1
        
        DECFSZ        31H,F
        GOTO        $-4
        
        DECFSZ        30H,F
        GOTO        $-6
        
        RETURN

CHK_SW:
        BANKSEL        PORTC

        BTFSC        PORTC,2
        GOTO        SW1_1
        BSF            PORTC,4
        GOTO        SW2_1
SW1_1:
        BCF            PORTC,4
SW2_1:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        BTFSC        PORTC,3
        GOTO        SW1_2
        BSF            PORTC,5
        GOTO        SW2_2
SW1_2:
        BCF            PORTC,5
SW2_2:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        
        RETURN


        


END
 
Last edited by a moderator:

A few more comments would be helpful. I suspect your problem is the watchdog timer though, if you have it enabled, add "clrwdt" to the delay routine so the WDT can't time out inside it.

Incidentally, there is no point in clearing the ANSEL registers because their values will be overwritten when you use them and they are both in the same bank so you can remove the second 'banksel' instuction.

Although what you have done isn't wrong, I would strongly advise you to use labels instead of '$-x' addresses. It will work as it is but isn't portable. For example:
Code:
DELAY:
        
        MOVLW        08H
        MOVWF        30H

        MOVLW        0FFH
        
        MOVWF        31H
        MOVWF        32H
DelayLoop
        clrwdt
        DECFSZ        32H,F
        GOTO        DelayLoop
        
        DECFSZ        31H,F
        GOTO        DelayLoop
        
        DECFSZ        30H,F
        GOTO        DelayLoop
        
        RETURN

Brian.
 

i changed the delay routine by copying and pasting your sent one in my code but nothing changed. problem is still there.

- - - Updated - - -

and very interesting thing is if i change 2nd portion (portc3) to first potion (portc2), even then only upper portion disables.
in both cases only lower portion works and upper disables.
 

I can't see any obvious reason why it wont work. If you are using real hardware to test it, make sure you have pull-up or pull-down resistors on the input pins so you can be sure the logic level requirements are being met. A switch from an input pin to CSS or VDD alone wont work, you need the resistor to ensure the level reverses whn the switch is opened.

There is a remote chance you are seeing an RMW effect because you write to and then immediately read PORTC. I doubt thats the problem but you an easily fix it by adding a 'nop' instruction after the line 'BCF PORTC,4'.

Hint: a better way of assigning addresses to variables (as opposed to you explicitly using 30H, 31H and 32H) is to use the 'cblock' directive. It will ensure you don't accidentally use the same address for something else. For example:
Code:
   cblock 30H
delay1
delay2
delay3
   endc
then use the names delay1, delay2 and delay3 instead of the numbers. It will work exactly the same, but if you later want to add more variables you just insert them in the cblock list and the assembler will take care of working out a suitable address for them.

Another hint:
your code will not work if you add interrupts because you are putting other instructions where the interrupt vector will be. If you start your program by copying the template provided with the assembler then add your own code to it, the interrupt vector will be dealt with automatically. Look for the file called '16F887TEMP.ASM' and add your code to it.

Brian.
 
As explained by betwixt in his post #2 and #4 please check the switch pull_up in the picture

Switch12.png
 

Use

Code:
	bsf	STATUS,RP0   ;Switch to Bank 1
	bcf	STATUS,RP0   ;Switch to Bank0
 
Last edited:

NO NO NO - that is both dangerous and counter intuitive. Use 'banksel' instead.

The difference is 'banksel' controls ALL the bank selection bits, not just RP0. Different PICs with different register banking schemes use different bank selecting bits. Specifying STATUS,RP0 is only valid for some devices but 'banksel' will be adapted by the assembler so it is always correct for the target device. You can use 'banksel x' where 'x' is the bank number you desire but using a register name is easier to read and always uses the bank for the named register, even if it maps to a different address in another device.

Brian.
 

thanks sir you helped me. actually the problem was RMW.
i added little delays somewhere and it started working.

well another problem is to be asked about.

what is meant by the ORG set in any ISR? and how to calculate the org #?

i am using timer1 interrupt and my code is long.

when i build it,
CORE-E0001: Stack over flow error occurred from instruction at 0x0008ed
CORE-E0001: Stack over flow error occurred from instruction at 0x000102

which org is to be set? guide please

code is here
Code:
org    0
goto   start

org   04h

BTFSS    PIR1,TMR1IF
RETFIE    
            
LCALL        SECONDS


org        100h

start:
...........main code

.
.
.
.
.
.
SECONDS
                ORG     1000H                
        ;;;;saving wreg values    
                BCF        INTCON,GIE
                MOVWF    WHAT
                SWAPF    STATUS,0
                MOVWF    QSTAT
        ;;;;end saving wreg values    


                BCF        PIR1,TMR1IF
                BCF        T1CON,TMR1ON
                
                DECFSZ    SECOND_REG
                GOTO    SCND
                MOVLW    DIVS
                MOVWF    SECOND_REG
                BSF        50H,0        ;goes hi after 1 sec
SCND
                
                MOVLW    TMRH
                MOVWF    TMR1H
                MOVLW    TMRL
                MOVWF    TMR1L
                BSF            56H,1
                BSF        T1CON,TMR1ON

;;;;retrieving wreg values        
                SWAPF    QSTAT,0
            MOVWF    STATUS
            SWAPF    WHAT,1
            SWAPF    WHAT,0
            BSF        INTCON,GIE
            ;;;;end retrieving wreg values

                RETURN
 
Last edited by a moderator:

NO NO NO - that is both dangerous and counter intuitive. Use 'banksel' instead.

The difference is 'banksel' controls ALL the bank selection bits, not just RP0. Different PICs with different register banking schemes use different bank selecting bits. Specifying STATUS,RP0 is only valid for some devices but 'banksel' will be adapted by the assembler so it is always correct for the target device. You can use 'banksel x' where 'x' is the bank number you desire but using a register name is easier to read and always uses the bank for the named register, even if it maps to a different address in another device.

Brian.

The problem with using banksel x is that it uses an extra byte and if you are like me I am always running out of memory, and as you change the bank often in a typical program the savings can mount up.
 

If you post a section of code, please enclose it in "code tags" so the forum doesn't remove formatting. Paste it in the message window, highlight it with your mouse then click on the # symbol in the window header. I will add the code tags to your code for you this time.

When the assembler converts your instructions into a binary image so it will run in the PIC, it normally works from the first address (normally 0000H), stores the first instruction there and moves to the next address to store the next instruction and so on. The 'ORG' directive (its an instruction to the assembler, not a PIC instruction) makes the next instruction start at a different address. It gives you the ability to jump over some addresses which sometimes you have to do. What your code:
Code:
code is here
org 0
goto start

org 04h

BTFSS PIR1,TMR1IF
RETFIE

LCALL SECONDS


org 100h

start:
does is this:
'org 0' tells the assembler the following instruction is to be stored at address zero,
'goto start' creates the instruction to jump to the place you called 'start' at address zero
'org 04h' tells the assemble the next instruction is to be placed at address 4, so you skipped over addresses 1 ,2 and 3. I'll explain why this is done later.
the next two instructions are placed starting at address 4.
The 'LCALL' isn't quite right, it should just be 'call' in this case although it will still work in most cases.
'org 100h' moves the address for the next instruction to address 100h, so you left nearly 250 addresses unused!

In that PIC there are two special addresses, the reset vector (0000h) and the interrupt vector (0004h). They are both 'hard coded' inside the PIC and can never be changed. The reset vector is where the program counter picks up the first instruction after the power is turned on, the MCLR pin goes from low to high or a watchdog timeout occurs. So that's where your program is normally located so it runs straight away. The interrupt vector is the address the PIC automatically jumps to when an interrupt occurs, that's why you will find the interrupt service routine (ISR) is always located at address 0004h. Now you can see why the 'org 04h' is important, if you didn't use it the assembler might put instructions at address 04h that were nothing to do with the interrupt and cause chaos if an interrupt happened.

Instead of 'org 04h' you could have used other instructions to fill in the gap so the first ISR instruction was still at address 04 but that would mean if you changed those instructions the gap they filled may change size and the ISR be placed wrongly. Using the 'org' makes sure the address is always correct.

The normal way we start an assembly program is to put a jump instruction to the real starting address at 0000h, then move the address to 0004h and place the ISR there. After the end of the ISR code, we place the 'real' program to be run. That explains why we use labels in jump instructions rather than a numeric address. When the assembler runs, it goes through the code twice (that's why we call them two-pass assemblers!), the first pass ignores what the instructions actually do, it just looks to see if they are one byte or two bytes long and whether there are any 'org' lines that change the addresses. From this it can work out what addresses each of the labels will be at in the final binary image. It builds a list of labels and their addresses called a 'symbol table'. Then it makes the second pass, this time converting the assembler instructons to their binary equivalents and wherever a label is found it substitutes the value from the symbol table. It make the program very flexible because if you add or remove lines of code, the assembler will re-calculate all the label addresses for you.

Don't worry about it too much for now but there is an alternative way of writing your program in 'relocatable' code where the benefits of labels really show because you can write the code in modules are use them in other programs. Thats really useful if you write library routines.

Back to your program. If you remove the 'org 100h' altogether it will move the 'start' label from address 100h to the next address after "LCALL SECONDS" and save you lots of memory space. Also make the 'LCALL' a normal 'call' and it should assemble with no errors.

It still wont work though! You have a serious error in the ISR. See if you can get it to assemble using the suggestions I gave first.

Brian.
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top