PIC-Compass

Another attempt to make a useful electronic compass with the Pewatron-sensor.

(last update 28/12/01)

prototype

The idea this time is to overcome the limit of using two RCX input-ports AND the nasty calibrating operations. This is really challenging, because you need to compute at least two directional informations to get the correct compass-azimuth. So you must add some intelligent electronics which may operate parts of the work done by the RCX in our previous projects.

Again we are working with the PIC 16F84. This is not the best choice at all from the technical point of view, for we actually would need :

The micro-controller has to read the amplified sensor-voltages, convert them to digital infomation, compute the azimuth-computations and put out a voltage to be transferred to the RCX.

Nevertheless we decided to use the PIC 16F84 for DIDACTICAL reasons. Some of our students learn how to program this chip. So, here a marvelous exercise to push themselves and the micro-controller to their limits. We also decided to use the well-known LM 324, the 339 and the analog switch 4066. The students have to deal with the limits of these chips too. After this project every participant will definitely know the value of a rail-to-rail amplifier !


1. The circuit

Click here

Click here

(Don't forget the single wire !)

To overcome the complicated calibration and standardization of our former projects, this device is equipped with a monitored stabilized power supply. It needs 9V DC and provides 5V, 6.3V and 9V.

Let's have a look at one channel :

The slightly damped Pewatron-signal is read, then amplified and sent to a comparator. Note that the LM 324 amplifier is powered by 6.3V, not being a rail-to-rail op-amp.

By enabling the correct analog switch, the PIC leads the voltage from the resistor-net to the comparator. This is an inverted application of our D/A converter. The PIC counts from 0 to 255 and will generate a growing voltage from 0 to 5V. Note that the RA4 is an open-drain port when configured as an output. So, it must be pulled-up by R34.

The output of the 339-comparator is pulled-up to 5V ! This is not an error of design since the 339 is powered by 9V, but a legal application of this chip's open-collector output.

If the voltage from the resistor-net reaches the sensor-voltage, the comparator output switches from logical LOW to HIGH, telling the PIC to stop counting at PORTB, which is the resistor-net driver. Now the PIC is able to interprete the voltage as an 8bit value.

To avoid oscillations at the switching voltage, a 10M resistor is added providing a certain hysteresis.

It is important to well adjust both channels to work between 1.5 and 4.5V. The adjusting is done through the 4 potentiometers. This provides a common midth-voltage of 3V (e.a. 256*3/5 = 153). Thanks to the stabilized power-supply and this one-time adjusting, this compass must not be calibrated later.

The PIC returns the azimuth in a 2 resolution as a voltage within the range of 0 to 5V. We use the pulse width modulator (PWM) explained at the D/A converter page. As the 16F84 has no on-board PWM, we have to simulate one through an astute time-interrupting process. Neither a 0V, nor a 5V output will be possible, because the interrupt service routine needs some time. To gain speed, the PIC is clocked at 8 MHz.


2. The PIC-program

2.1. The A/D conversion

The scenario is the following :

 ; JUMP TO MAIN PROGRAM
     GOTO   START
     ;
     ; HERE THE READINGS OF THE CHANNELS
LOOP_1
     ;  WAIT 0.01MS
     MOVLW  12;*
     MOVWF  AUX1_H;*
DELAYLAB21;*
     MOVLW  52;*
     MOVWF  AUX1_L;*
DELAYLAB11;*
     DECFSZ AUX1_L, F;*
     GOTO   DELAYLAB11;*
     DECFSZ AUX1_H, F;*
     GOTO   DELAYLAB21;*
    
     ; SKIP IF COMPARATOR 1 IS HIGH
     BTFSS PORTA,0
     GOTO   COUNT_1
     ; NOW WE HAVE THE FIRST VALUE TO BE STORED
     MOVF   PORTB, W
     MOVWF  X
     GOTO   CONTINUE_1
     ;
LOOP_2
     ;  WAIT 0.01MS
     MOVLW  12;*
     MOVWF  AUX1_H
DELAYLAB22;*
     MOVLW  52;*
     MOVWF  AUX1_L;*
DELAYLAB12;*
     DECFSZ AUX1_L, F;*
     GOTO   DELAYLAB12;*
     DECFSZ AUX1_H, F;*
     GOTO   DELAYLAB22;*
     
     ; SKIP IF COMPARATOR 2 IS HIGH
     BTFSS PORTA,2;*
     GOTO   COUNT_2
     ; NOW WE HAVE THE SECOND VALUE TO BE STORED
     MOVF   PORTB, W
     MOVWF  Y
     GOTO   CONTINUE_2
     ;
     ;
START
     BCF    STATUS, RP0
     ;
     ; CLEAR PORT B
     MOVLW  B'00000000'
     MOVWF  PORTB
     ;
     ;  ENABLE CHANNEL_1
     BSF    PORTA, 1
     ;
COUNT_1
     BCF    STATUS, RP0
     INCFSZ  PORTB,F
     GOTO   LOOP_1
     ;
CONTINUE_1
     BCF    STATUS, RP0
     ;  DISABLE CHANNEL_1
     BCF    PORTA, 1
     ; CLEAR PORTB
     MOVLW B'00000000'
     MOVWF PORTB
     ;  WAIT 0.01MS
     MOVLW  12;*
     MOVWF  AUX1_H
DELAYLAB23;*
     MOVLW  52;*
     MOVWF  AUX1_L;*
DELAYLAB13;*
     DECFSZ AUX1_L, F;*
     GOTO   DELAYLAB13;*
     DECFSZ AUX1_H, F;*
     GOTO   DELAYLAB23;*
     ; ENABLE CHANNEL_2
     BSF    PORTA, 4
     ;
COUNT_2
     BCF    STATUS, RP0
     INCFSZ PORTB,F;*
     GOTO   LOOP_2
     ;
CONTINUE_2
     BCF    STATUS, RP0
     ;  DISABLE CHANNEL_2
     BCF    PORTA, 4
     ;
;NOW OPERATE THE QUADRANT AND CASE ANALYSES, THE DIVISION X/Y
;AND THE ATAN-FUNCTION, FINALLY COMPUTE THE ANGLE
     GOTO START

Note that the delays-functions were made with CH Flash Basic.


2.2. The quadrant and the case discussion

Have a look at our former compass projects for better understanding.

This program-part has been created with CH-Flash-Basic. The software interpretes the BASIC-code and returns PIC-assembler.

PIC 16F84
FREQ 8
OSCILLATOR CRYSTAL
BYTE quadrant,case,angle,x,y,midth,dx,dy,temp
WORD phi,q,temp2
Start :
     'Quadrant
     IF x>midth THEN
        dx=x-midth
        IF y>midth THEN
           dy=y-midth
           quadrant=1
        ELSE
           dy=midth-y
           quadrant=2
        END IF
     ELSE
        dx=midth-x
        IF y>midth THEN
           dy=y-midth
           quadrant=4
        ELSE
           dy=midth-y
           quadrant=3
        END IF
     END IF
     '
     '
     'Case  
     IF dx>dy THEN
        case=1
        temp=dx
        dx=dy
        dy=temp
     ELSE
        case=2
     END IF

 


2.2. The multiplying by 100

For speed and memory-economy reasons we wrote this useful multiplication of a byte by 100. The result is stored in the variable q (word-length).

;
MUL_AUX
    	CLRF	AUX1_H
	MOVF	DX,W
	MOVWF   AUX1_L
ROT
	BCF	STATUS,C
	RLF	AUX1_L,F
	RLF	AUX1_H,F
	DECFSZ	AUX
	GOTO	ROT
	MOVF	AUX1_L,W
	ADDWF   Q_L,F
	MOVF	STATUS,W
	ANDLW   B'00000001'
	ADDWF   Q_H,F
	MOVF	AUX1_H,W
	ADDWF   Q_H,F
	RETLW	0
;
;
;
;HERE THE MAIN PROGRAM BODY .....
;SOMEWHERE :
;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
; HERE THE ANGLE COMPUTATIONS
; Q=100*X
; COUNT=Q / Y
; PHI = ATAN(COUNT)
	CLRF	Q_L
	CLRF	Q_H
	BCF	STATUS,C
; MUL_100
	MOVLW	6
	MOVWF	AUX
	CALL	MUL_AUX
	
	MOVLW	5
	MOVWF	AUX
	CALL	MUL_AUX
	
	MOVLW	2
	MOVWF	AUX
	CALL	MUL_AUX
; AND SO ON AND SO ON ...

The algorithm is simple :

100 = 64 + 32 + 4 = 26 + 25 + 22

example:

132 * 100 = 132 * (26 + 25 + 22) = 132 * 26 + 132 * 25 + 132 * 22 

according to the distributive rule.

Multiplications by 2 are very easy to be done in Assembler, since you only have to rotate left the byte by one :

132 = 0x84 = B'1000 0100'

132 * 2 = B'1 0000 1000' = 0x108


2.3. The division (word by byte)

;
;
DIV16_8
	CLRF	COUNT
	MOVF	DY,W
LOOP_DIV	
	SUBWF	Q_L,F
	BTFSC	STATUS,C
	GOTO	INCREMENT
NEGATIVE
	MOVF	Q_H
	BTFSC	STATUS,Z ;SKIP IF NOT ZERO
	RETURN
	DECFSZ	Q_H
	GOTO	INCREMENT
INCREMENT
	INCF 	COUNT
	GOTO	LOOP_DIV
;
;
;HERE THE MAIN PROGRAM BODY .....
;SOMEWHERE :
 
; DIVIDE 	
	CALL	DIV16_8
; AND SO ON AND SO ON ...

This is the shortest way we could do the division, not the quickest. But the algorithm has the advantage that the PIC does not get lost in a loop, if division by zero or some undesirable overflow-error, which never should happen, but since Murphy's laws we are aware! The alogirithm is again very simple :


2.4. The atan-function

  ; TO CALL Y=ATAN(X):
     ; MOVF X,W
     ; CALL ATAN
     ; MOVWF Y
     ; NOW THE ATAN TABLE
ATAN
     ADDWF PCL;*
     RETLW 0;*1
     RETLW 1;*2
     RETLW 1;*3
     RETLW 2;*4
     RETLW 2;*5
     RETLW 3;*6
     RETLW 4;*7
     RETLW 4;*8
     RETLW 5;*9
     RETLW 5;*10
     RETLW 6;*11
     RETLW 6;*12
     RETLW 7;*13
     RETLW 7;*14
     RETLW 8;*15
     RETLW 9;*16
     RETLW 9;*17
     RETLW 0XA;*18
     RETLW 0XA;*19
     RETLW 0XB;*20
     RETLW 0XB;*21
     RETLW 0XC;*22
; AND SO ON AND SO ON ...

This look-up table-function is extremely quick. It uses the particular feature which makes it possible to increment the program-counter proportionally to the function-operator. The program jumps to the given line, then returns from the call with the litteral value placed in the accumulator.


2.5. The azimuth

     ; 
;HERE THE MAIN PROGRAM BODY .....
;SOMEWHERE : CH-Flash-BASIC code
     '
     phi_H=0
     IF quadrant=4 THEN
        IF case=1 THEN
           phi=90-phi
        END IF
     END IF
     '
     IF quadrant=3 THEN
        IF case=1 THEN
           phi=90+phi
        ELSE
           phi=180-phi
        END IF
     END IF
     '
     IF quadrant=2 THEN
        IF case=1 THEN
           phi=270-phi
        ELSE
           phi=180+phi
        END IF
     END IF
     '
     IF quadrant=1    THEN
        IF case=1 THEN
           phi=270+phi
        ELSE
           phi=360-phi
        END IF
     END IF
     '

continue :

;
;
; HERE THE CONVERSION TO FIT INTO THE PWM-CONDITIONS:
; PHI=PHI/2+56
; SO WE GET VALUES FROM 56 TO 180+56=236
;THIS MAKES SURE THE VOLTAGE WILL BE GREATER
;THAN THE 1,3V-LOSS AT THE OUTPUT-DIODES OF MIKE'S BUFFER
	BCF  STATUS,C
	RRF  PHI_H ;DIVISION BY TWO
	RRF  PHI_L
	MOVLW  0X32 ; DEC 56
	ADDWF  PHI_L,W
	MOVWF  ANGLE 
	 
;
    
     GOTO   START
     ;
     ;
     ;
  
     END

So we have a 2 resolution for the compass.


2.6. The Pulse Width Modulator (PWM)

The algorithm sets the output pin HIGH during the time :

ton = angle div 256

and LOW during :

toff = (256 - angle) div 256

This is done by enabling timer-interrupts and permanently changing the TMR0-value to the complement of ton resp. toff. The complement is needed, for the TMR0 is incremented, not decremented. The interrupt is generated when there is a timer overflow. At 8 MHz the prescale is set to 1:2. So one cycle has the duration of 256 microseconds, thus a frequency of about 4kHz, which corresponds to the RC-values of our integrator.

     ORG 0X0004 ;interrupt entry
     ; SAVE CONTEXT (PIC REGISTERS)
     MOVWF   W_TEMP
     SWAPF   STATUS, W
     MOVWF   STATUS_TEMP
     ; Interrupt Service Routine
     BTFSC PORTA,3	;skip if PWM is low 
     GOTO   OFF_
     ; ON
     COMF ANGLE,W	;create complement
     NOP		;synchronization
     BSF PORTA,3	;set PWM high
     GOTO   POP
OFF_
     MOVF ANGLE,W	;set to angle-value
     BCF PORTA,3	;clear PWM
     GOTO   POP		;needed to synchronize
     ;
POP
     BCF    STATUS, RP0 ;make sure we have bank0
     MOVWF  TMR0		;set the timer
     BCF    INTCON, T0IF ;clear the interrupt-flag
     ; RESTORE CONTEXT (PIC REGISTERS)
     SWAPF   STATUS_TEMP, W
     MOVWF   STATUS
     SWAPF   W_TEMP, F
     SWAPF   W_TEMP, W
     RETFIE
     ; HEADER END

Note that the program has to operate some important configuration-instructions before.


5. Tests and error

This Robolab test-device uses our new recycled angle-sensor with an 8 resolution. The test-base has a 56:8 ratio, so one turn of the base produces 315 impulses which is largely sufficient.

The error-correction should be done in the RCX, for it depends on the individual part-caracteristics and the use-conditions. Have a look at our former projects for better understanding.


6. Download

Download all the files. Note that the circuit and the PCBoard have been made with Eagle 4.01.


RetourMain Page