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 :

• two 8 bit A/D-channels
• one 10 bit D/A converter (only possible with the 16F84 : 8 bits)
• high operation speed

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

(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 :

• reset the counter (=PortB)
• enable channel 1
• increment counter until comparator 1 is HIGH or counter-overflow
• disable channel 1
• reset the counter
• enable channel 2
• increment counter until comparator 2 is HIGH or counter-overflow
• disable channel 2
 ``` ; 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 :

• substract the divisor as long as the result is positive and count the substractions

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.