PIC Assembly Program Help

Thread Starter

ozirock

Joined Jan 13, 2011
47
Hi,

I'm trying to interpret a signal from an RC receiver using a PIC16f627. I'm going on the receiver sending a pulse every 20ms and the length of that pulse determining the position of the joystick on the controller. I'd just like to know if I have the timing of my pin testing correct, my code is:

Rich (BB code):
;**** All Input Pulse Test ****
Pulse_Check    	MOVLW   D'10'
                MOVWF   T_10
		MOVLW   D'10'
                MOVWF   Ind_T                
		MOVLW   D'210'
                MOVWF   IN1_S
                MOVLW   D'210'
                MOVWF   IN2_S
                MOVLW   D'210'
                MOVWF   IN3_S
INC_T2          MOVLW   D'200'
                MOVWF   T_200                
IN1_Check	call 	Servo_Delay
		BTFSC	PORTA,IN1
		DECFSZ  IN1_S,1		
		goto	IN2_Check		
IN2_Check	BTFSC	PORTA,IN2
		DECFSZ  IN2_S,1		
		goto	IN3_Check
IN3_Check	BTFSC	PORTA,IN3
		DECFSZ  IN3_S,1		
		goto	INC_T		
INC_T		DECFSZ  T_200,1
		goto	IN1_Check
		DECFSZ	Ind_T,1
		goto	INC_T3
INC_T3		DECFSZ	T_10,1
		goto	INC_T2
And servo delay is given by the code:

Rich (BB code):
Servo_Delay
			;6 cycles
	goto	$+1
	goto	$+1
	goto	$+1

			;4 cycles (including call)
	return
I think my problem is that the code makes my sample time more than 20ms but I'm not sure can anyone tell me if it is or not?

If you want to see the full code you can get the .asm files here. The code above is from RC Cobra LED C. The other file bridge_oisin is an attempt I made to redo someone else's code but it didn't work properly either I'm not sure why.

Thanks in advance for any help :)
 

Markd77

Joined Sep 7, 2009
2,806
I wouldn't try to count the 20ms.
What I would do is wait for the pulse to start, then run through your checks, decreasing the IN1_S, etc (a more meaningful name would help people reading it).
I'd also be decreasing another count which started from 211 and when that reached zero I would copy all the IN1_S, etc to another set of variables (so that the real value is always available).
At that point it doesen't matter how long it takes until the next pulse starts, you can do something else or just wait for the pulse.
Hope this makes sense.

I'm also assuming that all the pulses start at the same time, but I could be wrong, if not it gets a little more complicated.

A lot of your decfsz statements and the goto after them could just be replaced with decf because the goto is just to the next line.
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
Sorry about the mistakes I'm fairly new to programming in assembly, in theory the pulses should all start at 20ms intervals but I'm working on a labview program to try read the signal so I can know that for certain but I'm having issue's with that too.

I've redone the code like you suggested I think, I removed the DECFSZ's where you suggested and changed the BTFSC to BTFSS as I think this means that I'm only measuring from the start of the pulse, is that correct?

Here's my code now:

Rich (BB code):
;**** All Input Pulse Test ****
Pulse_Check    	MOVLW   D'210'
                MOVWF   INPUT1_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT2_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT3_PULSE_LENGTH
	        MOVLW   D'200'
                MOVWF   T_200                
IN1_Check	call 	Servo_Delay
		BTFSS	PORTA,IN1
		goto	Pulse_Check
		DECF	INPUT1_PULSE_LENGTH,1				
IN2_Check	BTFSC	PORTA,IN2
		DECF	INPUT2_PULSE_LENGTH,1		
IN3_Check	BTFSC	PORTA,IN3
		DECF	INPUT3_PULSE_LENGTH,1				
INC_T		DECFSZ  T_200,1
		goto	IN1_Check
		
;**** Move Input Values to Permanent Location ****
		MOVLW   INPUT1_PULSE_LENGTH
                MOVWF   IN1_S
		MOVLW   INPUT2_PULSE_LENGTH
                MOVWF   IN2_S
		MOVLW   INPUT3_PULSE_LENGTH
                MOVWF   IN3_S
Thanks you very much for your help.
 

jpanhalt

Joined Jan 18, 2008
11,087
Sorry about the mistakes I'm fairly new to programming in assembly, in theory the pulses should all start at 20ms intervals but I'm working on a labview program to try read the signal so I can know that for certain but I'm having issue's with that too.
They do start roughly at 20 mS intervals, but not at the same time for each channel, at least up to 9 channel receivers. I think you will need an edge detector in your program for each channel.

John
 

Markd77

Joined Sep 7, 2009
2,806
Getting closer I think.
I'll reply again tomorrow because I don't have much time now.

This bit:
MOVLW INPUT1_PULSE_LENGTH
MOVWF IN1_S

should be

MOVF INPUT1_PULSE_LENGTH, W
MOVWF IN1_S

because otherwise the memory location of INPUT1_PULSE_LENGTH (good name by the way) instead of it's value gets moved to the W register.
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
I think you will need an edge detector in your program for each channel.
I completely missed that thank you for pointing it out, I've changed it a bit so that I'm pretty sure that now it measure's each pulse individually then saves the values elsewhere for use later in the program, here's my code:

Rich (BB code):
;**** All Input Pulse Test ****
Pulse_Check    	MOVLW   D'210'
                MOVWF   INPUT1_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT2_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT3_PULSE_LENGTH 
                
IN1_Check	call 	Servo_Delay
		BTFSS	PORTA,IN1
		goto	IN1_Check
IN1_Check_2	DECF	INPUT1_PULSE_LENGTH,1
		call 	Servo_Delay
		BTFSS	PORTA,IN1
		goto	IN1_Check_2
		
IN2_Check	call 	Servo_Delay
		BTFSS	PORTA,IN2
		goto	IN2_Check		
IN2_Check_2	DECF	INPUT2_PULSE_LENGTH,1
		call 	Servo_Delay
		BTFSS	PORTA,IN2
		goto	IN2_Check_2
		
IN3_Check	call 	Servo_Delay
		BTFSS	PORTA,IN3			
		goto	IN3_Check
IN3_Check_2	DECF	INPUT3_PULSE_LENGTH,1
		call 	Servo_Delay
		BTFSS	PORTA,IN3
		goto	IN3_Check_2		
		
;**** Move Input Values to Permanent Location ****
		MOVLW   INPUT1_PULSE_LENGTH, W
                MOVWF   IN1_S
		MOVLW   INPUT2_PULSE_LENGTH, W
                MOVWF   IN2_S
		MOVLW   INPUT3_PULSE_LENGTH, W
                MOVWF   IN3_S

Do you want this to work on every receiver or just the one you have?
I just want it to work with my receiver really.

Have you got an oscilloscope or a PICKIT2 (which can be used as a logic analyser)?
I have a Velleman K8055 which I'm trying to use to get a visual representation of the signal is that the kind of thing you mean? I'm having a bit of trouble with that but a friend of mine is going to try help me with it on Monday.

What else does the PIC need to do apart from receive the pulses?
There are 3 controls on the remote, a switch and two joysticks, depending on the position of the switch the joysticks either control a motor and a servo or they turn on and off LED's. Or at least thats the plan :)
 

Markd77

Joined Sep 7, 2009
2,806
Looks like it's on the right track. Hopefully the vellman can show exactly what is happening so we will know the right method to use and see what signal the switch gives (it could be just a 1 or 2 ms pulse).

I think these changes should make it work better:

Rich (BB code):
IN3_Check    ;not required call     Servo_Delay
        BTFSS    PORTA,IN3            
        goto    IN3_Check
IN3_Check_2    DECF    INPUT3_PULSE_LENGTH,F ;(just looks better)
        call     Servo_Delay
        BTFSC    PORTA,IN3
        goto    IN3_Check_2        
        
;**** Move Input Values to Permanent Location ****
        MOVF   INPUT1_PULSE_LENGTH, W
                MOVWF   IN1_S
 

jpanhalt

Joined Jan 18, 2008
11,087
I have a Velleman K8055 which I'm trying to use to get a visual representation of the signal is that the kind of thing you mean? I'm having a bit of trouble with that but a friend of mine is going to try help me with it on Monday.
If it is a standard RC receiver, on the servo out pins, center is V+, one is ground, and the other is signal. Generally, it does not hurt if you accidentally connect signal to ground, but it won't work. Connecting V+ to ground will burn out a servo very quickly. The pulses you are trying to see are 1 to 2 mS wide (I suspect you knew that). Set your scope to 1 mS per division, connect the ground to ground pin and probe to the signal pin. 5 to 10 V full scale should be about right to get started. If you have an old servo connector with a lead, it makes the connections easier. You may need to turn the transmitter on to see the signal.

There are 3 controls on the remote, a switch and two joysticks, depending on the position of the switch the joysticks either control a motor and a servo or they turn on and off LED's. Or at least thats the plan :)
That is a very odd set-up. As I understand it, the receiver is 4 channel, but the transmitter can only control two channels at a time. I would be sure to check what happens to the two channels that are not being controlled. The more likely explanation is that I don't understand the configuration you are talking about.

John
 

Markd77

Joined Sep 7, 2009
2,806
How does the motor need controlling? Does it need forward and reverse and does it need speed control? Also will you use a motor controller chip or do it with mosfets / bipolar transistors?
Full control with just the PIC could get more complicated without a motor chip, otherwise it's pretty easy.
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
Markd77

I've made the changes as you suggested hopefully this looks right now

Rich (BB code):
;**** All Input Pulse Test ****
Pulse_Check    	MOVLW   D'210'
                MOVWF   INPUT1_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT2_PULSE_LENGTH
                MOVLW   D'210'
                MOVWF   INPUT3_PULSE_LENGTH 
                
IN1_Check	BTFSS	PORTA,IN1
		goto	IN1_Check
IN1_Check_2	DECF	INPUT1_PULSE_LENGTH,F
		call 	Servo_Delay
		BTFSC	PORTA,IN1
		goto	IN1_Check_2
		
IN2_Check	BTFSS	PORTA,IN2
		goto	IN2_Check		
IN2_Check_2	DECF	INPUT2_PULSE_LENGTH,F
		call 	Servo_Delay
		BTFSC	PORTA,IN2
		goto	IN2_Check_2
		
IN3_Check	BTFSS	PORTA,IN3			
		goto	IN3_Check
IN3_Check_2	DECF	INPUT3_PULSE_LENGTH,F
		call 	Servo_Delay
		BTFSC	PORTA,IN3
		goto	IN3_Check_2		
		
;**** Move Input Values to Permanent Location ****
		MOVF   INPUT1_PULSE_LENGTH, W
                MOVWF   IN1_S
		MOVF   INPUT2_PULSE_LENGTH, W
                MOVWF   IN2_S
		MOVF   INPUT3_PULSE_LENGTH, W
                MOVWF   IN3_S
Yes the motor should be able to go forward and reverse hopefully with speed control using PWM, would you think I'd need a second pic for this? I'm wondering if the timing would just be too complicated with the pulse check running along side the motor PWM. The motor is controlled using a motor driver chip and logic inverter so it takes two pins to control. I've tested this circuit and all seem's to work fine.

jpanhalt

I've got some help from a guy at Velleman and unfortunately it just can't sample fast enough to see the signal, 10ms intervals is as good as it can do.

That is a very odd set-up.
Sorry I explained it badly, it's out of an old RC plane, I think your right about the 4 channels as the receiver has four ports, 1 channel is controlled by the switch and used to turn on the motor when it was in the plane, another 2 channels are controlled by the joysticks and they used to control the tail fins of the plane, I've no idea what the final channel is for I don't think it's used at all.
 

Markd77

Joined Sep 7, 2009
2,806
With the motor driver chip it is pretty easy. Have a look at the PWM section in the datasheet.
I'm pretty sure this code should set it up nearly correct. It's for the 16F628 but I think it should work OK.

Rich (BB code):
    BSF STATUS, RP0         ; Select Bank 1 
    movlw 0xFF 
    movwf PR2                ;PWM setting 
    MOVLW B'11000011'          
    MOVWF TRISA             ; Set RA<1:0 and 7:6> as input all others out 
    MOVLW B'11110111'          
    MOVWF TRISB             ; Set all input except RB3 PWM out 
 
    BCF STATUS, RP0         ;bank 0 
    movlw B'00000100'         
    movwf T2CON                ;timer 2 on, no prescaler or postscaler 
    movlw B'00001111' 
    movwf CCP1CON 
    movlw 0x04 
    movwf CCPR1L
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
I had no idea I could do that, embarrassingly I taught I had to do it with delay loops. Does this only work with specific pins on the chip or will any pin I set as an output be able to utilise this PWM?

I tried to put your code into my reset/initialize routine here:

Rich (BB code):
RESET		MOVLW	B'00000111'	;Disable Comparator module's
		MOVWF	CMCON
		;
		BSF	STATUS,RP0	;Switch to register bank 1
					;Disable pull-ups
					;INT on rising edge
					;TMR0 to CLKOUT
					;TMR0 Incr low2high trans.
					;Prescaler assign to Timer0
					;Prescaler rate is 1:256
		MOVLW	B'11010111'	;Set PIC options (See datasheet).
		MOVWF	OPTION_REG	;Write the OPTION register.
					;
		CLRF	INTCON		;Disable interrupts
		
		movlw 	0xFF 
    		movwf 	PR2             ;PWM setting
		
		MOVLW	B'00000000'
		MOVWF	TRISB		;all RB ports are outputs
					
		MOVLW	B'00011100'	;RA3:RA5 ports are inputs, all others are outputs
		MOVWF	TRISA
		
		BCF	STATUS,RP0	;Switch Back to reg. Bank 0
		movlw 	B'00000100'         
		movwf 	T2CON           ;timer 2 on, no prescaler or postscaler 
		movlw 	B'00001111' 
		movwf 	CCP1CON 
		CLRF 	CCPR1L
    		CLRF	PORTB
Does this code give 4 possible speeds, is that what the following lines do?

Rich (BB code):
movlw 0x04 
    movwf CCPR1L
I've been looking a this tutorial http://www.winpicprog.co.uk/pic_tutorial8.htm and would I be right to say you would control the motor with the following code:

Rich (BB code):
	MOVLW	d'64'
	CALL	Speed		;both half speed forwards


Speed:				;use value in W to set speed (0-127)
    	MOVWF	temp
	BTFSC	temp, 7		;if more than 128 set speed in reverse
	CALL	Reverse		;so '1' is very slow forward
	BTFSS	temp, 7		;and '129' is very slow reverse
	CALL	Forward
	ANDLW	0x7F
    	MOVWF   CCPR1L
	RETURN

Reverse:
	BSF	PORTB, Motor_On	;set pins for reverse
	BCF	PORTB, Direction
	RETURN

ForwardL:
	BSF	PORTB, Motor_On	;set pins for forward
	BSF	PORTB, Direction
	RETURN
I'm not sure where the switching to reverse at 127/128 came into the program though would it be with the following code?

Rich (BB code):
    	MOVLW    126		;set highest PWM value
    	BANKSEL  PR2		;over this (127) is permanently on
    	MOVWF    PR2
    	BANKSEL  TMR2
Thanks for all your help, hope my questions aren't too stupid :)
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
Sorry ignore this part as I figure out that it was setting the motor moving not setting a range of speeds.

Does this code give 4 possible speeds, is that what the following lines do?

Code:
movlw 0x04
movwf CCPR1L
 

Markd77

Joined Sep 7, 2009
2,806
It's only available on one pin (PORTB,3) on the 16F627.
The testing for reverse is the btfsc temp,7 line (and the other one). In binary the MSB is 1 for any number over 127 (127 is 01111111, 128 is 10000000).
It's probably best to use their values for the PWM registers instead of mine.
Maybe a good thing to do at this point is to test if the pulse decoding part works. You could light up 3 LEDs based on testing if the pulse lengths are bigger than some mid value. Then change the value and see if it works as expected.
 

jpanhalt

Joined Jan 18, 2008
11,087
The usual controls would be motor on/off, motor speed ("throttle"), elevator, rudder, and ailerons. With 4 channels, the on/off and motor speed are on the same channel. You have to reduce the throttle to zero, then advance to start the motor. Sometimes, you actually have to restart the receiver and/or transmitter with the throttle in zero for it to even recognize the throttle control.

John
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
@ Markd77

I've concentrated on the pulse decoding part like you suggested and found that the switch works well when set around 100 and the joysticks appear to be centered around 110. The switch is working perfectly with the following code:

Rich (BB code):
;**** Switch Pulse Test ****
IN1_Test	movf    S_160,W		;**** X => 160 ****
    		subwf   IN1_S,W
    		btfsc   STATUS,C
    		goto    LC1
    		movf    S_160,W		;**** X < 160 ****
		subwf   IN1_S,W
		btfss   STATUS,C
    		goto    IN2_Test    		
    		goto	Pulse_Check
However the code for testing the joystick didn't work properly, it doesn't appear to be registering the idle position i.e. it is going straight from the forward to the reverse. I think there may be an issue with my logic but I can't see an issue, here is the code for this section:

Rich (BB code):
;**** First Light Control ****
LC1		movf    IN2_S,W		;**** X =< 140 ****
		subwf   S_140,W
		btfsc   STATUS,C
    		goto    Dip_C

;**** 140 < X < 160 ****
    		movf    IN2_S,W		;**** 140<X<160 ****
    		subwf   S_140,W
    		btfsc   STATUS,C
    		goto    $+5
    		movf    S_160,W
    		subwf   IN2_S,W
    		btfss   STATUS,C
    		goto    LC1_Idle

;**** X => 160 ****    		
    		movf    S_160,W		;**** X => 160 ****
		subwf   IN2_S,W
		btfsc   STATUS,C
    		goto    FB_C
@ jpanhalt

Ah you see this one didn't have a throttle control, when you flicked the switch the motor came on full. The left joystick controlled the elevator and the right joystick controlled the tail. It's a jamara XT-3, the pictures are very small on google but here it is, hopefully you can make out the controls http://img.archiwumallegro.pl/?1140000400
 

Markd77

Joined Sep 7, 2009
2,806
I think you just have the btfsc and btfss the wrong way round.
It catches nearly everyone out (including me).
If you add two numbers and there is an overflow then the carry bit gets set, however if you subtract two numbers and there is an overflow the carry bit is clear, it's set if there is no overflow.
 

Thread Starter

ozirock

Joined Jan 13, 2011
47
I've tried them both ways now and I'm still only getting the two extreme's. I've made it as simple as I can I think, after the test the pulse lengths are moved to the new names:

Rich (BB code):
;**** Move Input Values to Permanent Location ****
		MOVF   INPUT1_PULSE_LENGTH, W	;**** Switch ****
                MOVWF   IN1_S
		MOVF   INPUT2_PULSE_LENGTH, W	;**** Joystick 1 ****
                MOVWF   IN2_S
		MOVF   INPUT3_PULSE_LENGTH, W	;**** Joystick 2 ****
                MOVWF   IN3_S
These are then tested to see what lights should be on for example here is Joystick 1:

Rich (BB code):
;**** First Light Control ****
LC1		movf    IN2_S,W		;**** X =< 140 ****
		subwf   S_140,W
		btfsc   STATUS,C
    		goto    Dip_C

;**** 140 < X < 160 ****
    		movf    IN2_S,W		;**** 140<X<160 ****
    		subwf   S_140,W
    		btfss   STATUS,C
    		goto    $+5
    		movf    S_160,W
    		subwf   IN2_S,W
    		btfsc   STATUS,C
    		goto    LC1_Idle

;**** X => 160 ****    		
    		movf    S_160,W		;**** X => 160 ****
		subwf   IN2_S,W
		btfsc   STATUS,C
    		goto    FB_C
Then this turns on the LED's as follows:

Rich (BB code):
;**** Full Beam Control ****
FB_C		movlw   B'00000011'  	;Turn on Full Beams and Dips
		movwf   PORTB
		goto	LC2                


;**** LC1 Idle ****
LC1_Idle        movlw   B'00000000'  	;Turn all off 
		movwf   PORTB
		goto	LC2


;**** Dips Control ****
Dip_C		movlw   B'00000010'  	;Turn on Dips
		movwf   PORTB
    		goto	LC2
And then it goes back to do the same for the second joystick.

I can't see where it's going wrong, any suggestions?
 

Markd77

Joined Sep 7, 2009
2,806
I've made a couple of changes and commented a couple of bits out, because there is no need for two checks.
If the second section, if you write to PORTB in the same way, it will overwrite the changes you made in the first section (if you see what I mean).
Just for testing it might be worth just looking at one stick at a time.
Have you tried the simulator in MPLAB yet? It's very useful for this sort of thing, you can make changes to the registers and step through the code to see where it goes and what changes.
Rich (BB code):
;**** First Light Control ****
LC1        movf    IN2_S,W        ;**** X =< 140 ****
        subwf   S_140,W
        btfsc   STATUS,C
            goto    Dip_C

;**** 140 < X < 160 ****
;            movf    IN2_S,W        ;**** 140<X<160 ****
;            subwf   S_140,W
;            btfss   STATUS,C
;            goto    $+5
            movf    IN2_S,W
            subwf   S_160,W
            btfsc   STATUS,C
            goto    LC1_Idle

;**** X => 160 ****            
;            movf    S_160,W        ;**** X => 160 ****
;        subwf   IN2_S,W
;        btfsc   STATUS,C
            goto    FB_C
 
Top