Assembler PIC programming, is this correct?

Thread Starter

visaxx

Joined Aug 14, 2013
9
Hello!! I have done some exercies / assignments but I dont have the correct answer for them. Can anyone please help me check if I have solved them correct? :) I would truly appreciate that.


Question:
implement the following pseudo code with assembly code:

Rich (BB code):
if (var1 > 9) or (var1 < 4)
(increment PortC by 5)
else
(invert PortC)
My answer:


Rich (BB code):
movlw x09
cpfslt var1
goto add_5
comf portC
movlw x04
cpfsgt var1
goto add_5
comf portC

add_5 movlw x05
addwf portC

Question:
We want to send the ASCII-character 'B' with serial communication RS323 standard. The parity is odd. Draw the pattern in the following picture

http://forumbilder.se/CF6Q7/133

My answer:
ASCII 'B' = 0100 0010
http://forumbilder.se/CF6Q7/33a
or should I swap the High and low bits before I draw the pattern?
 

MaxHeadRoom

Joined Jul 18, 2013
28,702
Question:
We want to send the ASCII-character 'B' with serial communication RS323 standard. The parity is odd. Draw the pattern in the following picture

http://forumbilder.se/CF6Q7/133

My answer:
ASCII 'B' = 0100 0010
http://forumbilder.se/CF6Q7/33a
or should I swap the High and low bits before I draw the pattern?
There are many PIC's with built in USART now that it is a piece of cake to send RS232.
PicMicro has an app note solely for USART function for each part category.
Max.
 

JohnInTX

Joined Jun 26, 2012
4,787
Presumably, this is for an 18F part (based on your use of the cpfslt instructions.)
For A) Your logic is basically correct i.e. 4 < var1 < 9 will goto add_5. You need to add 'return' (if this is a subroutine) OR bra done (if inline) instructions at the end AND after the COMF or that code will run right into add_5, making a mess.
Style pointers:
Use bra instead of goto (smaller code)
Always specify the destination register i.e. addwf PORTC,F for clarity and to remind you what you are doing.
PortC is not a valid register using the standard register definitions, PORTC is (and never turn off case-sensitivity).
Speaking of ports, when modifying outputs on an 18F use LATC, not PORTC to avoid the notorious read-modify-write problem.
When you are using decimal numbers, specify them as .4 and .9, not x04 and x09. Either way works with these values.. until you change to x10 instead of .10
Comments are your friend.
Put your labels on a separate line and follow them with a colon i.e.
Rich (BB code):
add5:
 movlw .5 ; add .5 to the port
None of the style things will impact the running of your code but you won't always be writing little things. Clean source is easier to read and find problems in.

B)Your ASCII 'B' is 0x42 which is correct. For odd parity, you'll likely send 7 bits with the MSbit as parity (ODD,7,1) There are an even number of '1's in the data byte so the MSbit has to be set to make odd parity so its
b'11000010'. When sending, the LSbit goes first so with a firmware UART you'll wind up shifting the byte to the right as you send the 8 bits, not swapping. Don't forget the start and stop bits.

As Max says, a hardware USART is simpler in the real world, just compute the parity, drop the byte onto TXREG and you are done.

You should be able to manually run through the code with pencil and paper. Pick a value for var1 and step through the code line by line until you get to the end. That would show problems like no return after the comf. Repeat with other values less than, equal to, between and greater than your test values. When the code flows on your desk, it will likely flow on the chip.

Looks like you are on the right track.
 
Last edited:

Thread Starter

visaxx

Joined Aug 14, 2013
9
Thank you, very clear explanation and useful tips! :)

I have some more if you have time and don't mind take a look at them?

Question:
Type in the the new content after the asm-code have been executed.

Rich (BB code):
Var1 equ x20
Var2 equ x21
Var3 equ x22
Status equ x03
VarA equ x28
VarB equ x29
VarC equ x2A

movfw Var1 
movwf Var3
bsf status,w
rrf Var3
rrf Var3

movlw x3B
addwf VarA,w
movwf VarB
xorfw VarC,1



The left table is the memory-content before code execution and the left table is after. Does the 'after table' look correct?



Question
which ASCII-character is being sent? Method is serial communication with RS232. There is a stop bit, start bit, and a parity.



My answer is 'b'. But I dont know if that is correct, I know that the startbit is always 0, and stop bit is always 1, then I am left with a space for 9 bits...

 

JohnInTX

Joined Jun 26, 2012
4,787
Rich (BB code):
Var1 equ x20
Var2 equ x21
Var3 equ x22
Status equ x03
VarA equ x28
VarB equ x29
VarC equ x2A

 movfw Var1    ; 1ah->W
 movwf Var3    ; 1ah->Var3  B'00011010'
 bsf status,w    ; set CARRY - use bsf STATUS,C not w. Yes w evaluates to 0 but what's THAT mean here?
 rrf Var3    ; rotate Var3 right, carry->MSbit, carry out=0
 rrf Var3    ; rotate again, 0->MSbit.  Var3 = B'01000110' 46h OK!    

 movlw x3B    ; W=3bh
 addwf VarA,w    ; W=3bh+occh = (1)07h
 movwf VarB    ; VarB = 07h  OK!
 xorfw VarC,1    ; xorwf VarC,F  07h^0aah = 0adh ->VarC OK!
I get 46h (ASCII 'F') for the serial. On the diagram left to right:
1 - MARK condition, TX line is IDLE while MARK is true.
0 - the start bit
0110 - 6hex (backwards but it happens to be the same either way)
0010 - 4hex (read it backwards as 0100)
0 - 9th data bit, odd parity
(1) stop bit

The UART settings for this would be 8,O,1 8 data bits + 1 parity bit + 1 stop bit (+1 start bit) = 11 bit times total. Note that traditional ASCII is 7 bit data so parity (EVEN,ODD or NONE) would occupy the 8th bit (MSbit) for 10bittimes/character.

Remember between start and stop, the parity is added at the MSbit end -AND- the data goes out LSbit first (right after the start).

EDIT: if you read it the other way (start bit on the right), you get 62h which is ASCII 'b' but if the arrow is increasing time and the left-most 1 is the IDLE (the traditional way to draw it), its 46h.
Also, the way it's drawn is correct from a LOGICAL viewpoint (MARK==1, SPACE==0) but if you measure the voltages on RS232 lines themselves, you'll find that the MARK is a (-)V and SPACE is (+)V. Something like a MAX232 is used to convert the RS232 to logic levels.

BTW: you are mixing two different PICs in this thread. The first had to be an 18F because of cpfslt etc. This one has to be a midrange because of rrf (not rrncf or rrcf as in the 18F).
Have fun!
 
Last edited:

Thread Starter

visaxx

Joined Aug 14, 2013
9
Just as before, thank you for very clear explanation and help! :)
These are some mixed assignments/exercises from last year class and this year's class. Last year they used a 16F PIC and this year they are using a 18F, so that is why the instructions are mixed :)

And I have some more exercises!
Question:
How long will it take for the following subrutin take if we use a 12 MHz crystal frequency on a F18 PIC?

Rich (BB code):
hugo:

movlw .12
movwf n
goto test
clrf n2

test:
rrf n,W
movwf PORTC
nop
return
My answer:
One instruction cycle will take 0.3333 ns, since (12Mhz/4)=3 --> invert 3 = (1/3) = 0.3333

And we have 9 instructions cycles, so 9*0.3333 = 2.999 ns.


Question:
Write the following pseudo code with assembly code:

if PORTC bit 2 == 1
(increment var1 by 1)
else
(increment var1 by 2)



My answer:

Rich (BB code):
btfss PORTC,2 ;test if bit 2 is set
goto add_2 ;skip if set
incf var1,f ;add 1 to var1
end

add_2:
movlw .2 ; w = 2
addwf var1,f ; var1 = var1 + w
end

Then there is a question that I dont really understand:
What will the complete machine code instruction be and how long is the execution time expressed in cycles?

Rich (BB code):
MOVLW 0x3A
I understand that machine code is written in terms of 0 and 1. But how can I translate 'MOVLW' to binary form? Does it have something to do with the 'encoding' http://technology.niagarac.on.ca/staff/mboldin/18F_Instruction_Set/MOVLW.html
 

Thread Starter

visaxx

Joined Aug 14, 2013
9
Thank you! Then I know it is 3 ns :)

But what do you mean with add 1? Do you mean in question 2? I am supposed to add 1 or 2 depending on if PORTC,2 is set or not..
 

Markd77

Joined Sep 7, 2009
2,806
Yes, question 2.
The code becomes:
Rich (BB code):
incf var1,f ;add 1 to var1
btfsc PORTC,2 ;test if bit 2 is set
incf var1,f ;add 1 to var1
Same result, fewer instructions.
 

Thread Starter

visaxx

Joined Aug 14, 2013
9
Oh I see, that is a smart solution!
But shouldn't it be btfss? Because if PORTC,2 is set we only want it to increase by 1.
 

Thread Starter

visaxx

Joined Aug 14, 2013
9
:)

Here is another instruction cycle question, still with a 12 MHz frequency.

Rich (BB code):
movlw .20
movwf n

again: 
decf n,f
bnz again
nop
nop
rlncf n,w
movwf PORTC
nop
return
W have 68 instruction cycles, right? So my answer will be that it will take 22.66 ns which is about 23 ns. Or should I round down to 22 ns?
 

JohnInTX

Joined Jun 26, 2012
4,787
Actually at 12MHz Tcyc is 333.33ns. 12/4=3MHz. → .333us. typing on phone.but wanted to point that out. so if it is 68 Tcyc your time is about 22.644 us. Looks like 68 to me too.
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,287
Want to do it slightly faster?
Subtract 4 then see if the answer is greater than 5.
Not entirely correct. In 8 bit math, the result of the subtraction from the values 0 through 3 will result in the values 255 through 252, which are all considerably greater than 5.

Still need two tests. In this case, the first test should be of the carry flag after the subtraction.
 

Markd77

Joined Sep 7, 2009
2,806
Good point, there is a way of doing it with just one test, but there were a few ways of interpreting what I wrote in post 5, and the most of them would give the wrong answer.
This way isn't much of a speed improvement, and it destroys var1, but I think it works.
Rich (BB code):
movlw 4
subwf var1, F
movlw 6
subwf var1, F
btfsc STATUS, C
goto com_portC
movlw 5
addwf PORTC, F
goto done
com_portC
comf PORTC
done
<ed> There is probably a way to knock a few cycles off using pic18 instructions </ed>
 
Last edited:

Thread Starter

visaxx

Joined Aug 14, 2013
9
Hello

I have a question about the first assignment:
Rich (BB code):
if (var1 > 9) or (var1 < 4)
(increment PortC by 5)
else
(invert PortC)

Rich (BB code):
movlw .9
cpfslt var1
bra add_5
comf POTRTC
movlw .4
cpfsgt var1
bra add_5
comf PORTC

add_5:
movlw .5
addwf PORTC
RETURN
If var1 is greater than .9, then it will call add_5, and after the subroutin it will return to compf PORTC, and run the complement, even though we don't want it to..


So maybe this will work better?:

Rich (BB code):
movlw .9
cpfsgt var1
compf PORTC
movlw .5
addwf PORTC,f
movlw .4
cpfslt var1
compf PORTC
movlw .5
addwf PORTC,f
 

Markd77

Joined Sep 7, 2009
2,806
As far as I know, bra is like goto, and doesn't affect the call stack, so the RETURN at the end will return to the point immediately after the most recent CALL operation.
It's the things that JohnInTX mentioned in post 4 that you have to worry about.
 

MMcLaren

Joined Feb 14, 2010
861
I think someone already suggested something like this for checking if a number is inside or outside a range of numbers;

Rich (BB code):
;
;       if (var1 > 9) or (var1 < 4)
;
        movf    var1,W          ; 
        addlw   -(9+1)          ; test for [4..9] range
        addlw   (9-4)+1         ; C=1 when 4 ≤ var1 ≤ 9
;
Here's an excerpt from a program where the method is used to decode a 12-bit Sony SIRC signal;

Rich (BB code):
;
;  collect sirc data, test for valid ~600-us high level followed
;  by a low level ~600-us '0' or a ~1200-us '1' data bit
;
newbit
        clrf    TMR0            ; clear TMR0, start measurement   |B0
loophi
        btfsc   PORTA,IR        ; pin lo yet? yes, skip, else     |B0
        goto    loophi          ; loop (while timing hi pulse)    |B0

        movf    TMR0,W          ; pulse hi time (64-us ticks)     |B0
        addlw   -(800/64+1)     ; test for ~600 ± 200 usecs       |B0
        addlw   (800-400)/64+1  ; C=1 (400..800 usec range)?      |B0
        skpc                    ; yes, skip (in range), else      |B0
        goto    irloop          ; branch (abort & start over)     |B0

        clrf    TMR0            ; clear TMR0, start measurement   |B0 
looplo
        btfss   PORTA,IR        ; pin hi yet? yes, skip, else     |B0
        goto    looplo          ; loop (while timing lo pulse)    |B0

        movf    TMR0,W          ; pulse lo time (64-us ticks)     |B0
        addlw   -(1500/64+1)    ; test ~600..~1200 ± 300 usecs    |B0
        addlw   (1500-300)/64+1 ; C=1 (300..1500 usec range)?     |B0
        skpc                    ; yes, skip (in range), else      |B0
        goto    irloop          ; branch (abort & start over)     |B0

        addlw   -(900-300)/64   ; pulse <= ~900 usecs (a '0')?    |B0
        skpnc                   ; yes, skip, else                 |B0
        bsf     iradr,5         ; add a '1' bit to SIRC vars      |B0
        clrc                    ;                                 |B0
        rrf     iradr,F         ; shift entire 12 bits right      |B0
        rrf     ircmd,F         ; and test the bit counter        |B0
        btfss   ircmd,0         ; all 12 bits? yes, skip, else    |B0
        goto    newbit          ; branch (collect next data bit)  |B0

        rrf     ircmd,F         ; fix 'ircmd' bit position        |B0
Hope the example helps. Good luck with your projects.

Cheerful regards, Mike
 
Last edited:
Top