Reset on setfreq k31 command 14M2

crowland

Member
I had an interesting problem with a program running on a 14M2.
It has the usual structure:

init:
' initialise code
sertxd ("hello", cr,lf)

main:
' main code
sertxd ("main", cr, lf)
setfreq k31
' delay handling
nap 3
goto main

I'm trying to minimise the power taken so I have a setfreq K31 command to minimise the power. What I am seeing is that the processor seems to keep resetting. The "hello" line is being sent all the time.
If I remove the setfreq command, or change it to setfreq K250 then it is fine.

It's fine in the simulator. I've only tried the one processor.
Could this be a problem with the processor or the circuit? It's not one I've seen before.

Chris
 

AllyCat

Senior Member
Hi Chris,

It's probably the PIC(axe)'s "Watchdog Timer" whose clock does NOT change when the instruction clock is altered (by setfreq). Presumably it's the NAP command* in your code that is causing the reset; in my case it was PULSIN, as discussed in this old thread.

Note that the raw PIC actually has two "31 kHz" oscillators, one is the Watchdog oscillator and the other is divided down from the normal 4 MHz clock. Also, the frequency tolerance of the watchdog clock is (now) nothing like as bad as the PICaxe manual implies.

*EDITS: Or is it the SERTXD? You're not setting the clock back to 4 MHz before looping back to main:

Also, you can disable or modify the Watchdog time(r) using a POKESFR command.

Cheers, Alan.
 
Last edited:

techElder

Well-known member
Regardless what is happening with the RESET problem, you certainly don't want to operate the AWAKE time of your program at 31KHz!
 

crowland

Member
I forgot about the setfreq M4 command immediately after the main: label.

This is the full code, I commented out the compass and rf calls and it made no difference until I changed k31 to k250.
Code:
; *******************************
; ***** Sample Header File  *****
; *******************************
;    Filename: rfCompass.bas 		
;    Date: 			
;    File Version: 	
;    Written by: Chris Rowland		
;    Function:		
;    Last Revision:
;    Target PICAXE:	14M2
; *******************************
; This uses a HMC4883L compass module to read the compass heading
; and transmit it using a 433MHz transmitter.
; The heading in degrees, a compass valid flag and the power supply
; voltage in 1/10V are transmitted.

; The program reduces the checking rate and stops transmitting
; the position if the heading has not changed, eventually only
; checking the position every minute.

; This should mean that the power requirement, from 2 AA batteries,
; should be low enough that there's no need for a switch and it
; should run continuously.

#picaxe 14M2
; define this to enable sending of data through the serial port.
;#define TEST
; pin definitions
{
; transmitter pins
Symbol txEnable = c.1
Symbol txData   = c.0

; shutter pin.  pulled high when the shutter is open
symbol shutter = c.4
symbol shutterPin = pinC.4
symbol shutterMask = %0001110000100110

; the HMC4883L uses sda and scl, these are pins b.3.and b.4
}
; registers
{
; Temporary registers

; W0 to W3 / B0 to B7 are only used in
; a subroutine

; compass vectors as words
symbol X = w4
symbol Y = w5
symbol Z = w6
; byte equivalents
symbol XL = b8
symbol XH = b9
symbol YL = b10
symbol YH = b11
symbol ZL = b12
symbol ZH = b13
; 1 if the compass data is valid, 0 if not
; (could be a bit)
symbol dataValid = b26
; count the number of times since the heading changed
symbol changeCount = w7

; compass heading in degrees
symbol heading = w8
symbol lastHeading = s_w1

symbol headingTolerance = w9
}
; Init
{
init:
	low txEnable
	low txData
	low b.5
	low b.1
	low b.2
	low c.2
	low c.4
	
	pause 60
	call initCompass
	pause 20
	#ifdef TEST
	sertxd ("hello",cr,lf)
	sertxd ("X, Y, Z, hdg",cr, lf)
	#endif
	
	; enable weak pullups on all unconnected pins
	;pullup shutterMask
	disablebod
	
	headingTolerance = 1
;==========================================================
}
; Main code loop
{
main:
	setfreq M4
	gosub readCompass
	#ifdef TEST
	sertxd(",", #changeCount, cr,lf)
	gosub txXYZ
	#endif
	if dataValid = 1 then
		#ifdef TEST
		sertxd (",",#heading)
		#endif
		inc changeCount
		; has the heading changed?
		W0 = heading - lastheading
		; Handle the wrap round 360 and remove the sign
		select case  w0
			case 181 to 360
				W0 = 360 - W0
			case $ff4c to $ffff	; -1 to -180
				W0 = -w0
			case $fe98 to $ff4b	; -181 to -360
				W0 = W0 + 360
		end select
		if W0 > headingTolerance then
			; the heading has changed, send the position
			gosub TransmitData
			lastHeading = heading
			changeCount = 0
			headingTolerance = 1	
		else if changeCount < 5 then	; send a burst of positions
			gosub TransmitData
		endif
	else
		#ifdef TEST
		sertxd (",?", cr, lf)
		#endif
	endif
		
	setfreq k250      ; I get resets to init: if this is set to k31, setting to k250
	nap 2
	if changeCount < 2400 then
		w0 = changeCount % 10
		if w0 = 0 then gosub TransmitData
		;goto main
	; reduce the frequency to minimise the power consumption
	else if changeCount < 9600 then
		; after 5 minutes increase the tolerance but
		; continue to check frequently
		headingTolerance = 2
	else
		; after 20 mins we only check every minute to see if
		; the heading has changed. When the heading changes
		; we revert to the faster checks.  This should hopefully
		; keep the power consumption low enough when the system
		; is inactive that we don't need to have a battery switch.
		headingTolerance = 5
		sleep 26
		; wraps after about 42 days
	endif
	goto main	
}	

;==========================================================
; send data using the rf output	
{
; 8 bytes of data are sent:
; hdddxbvv
; hddd is the heading in degrees ascii, bvv is the battery
; x is d if the heading is valid, x if it isn't
; voltage in 1/10 V as ascii
; e,g  "h123db29" is a valid heading of 123 deg and a voltage of 2.9 V

symbol ADCON0 = $1f

TransmitData:
	if dataValid = 0 then
		return		; don't send anything if the compass valus is invalid
	endif
	; enable the ADC
	peek ADCON0, b2
	b2 = b2 or 1		; set bit 0 to enable the ADC
	poke ADCON0, b2
	; get the supply voltage by reading the 1.024V
	; adc calibration voltage using the supply voltage
	; as the DAC reference.
	calibadc w0
	; disable the ADC to save power
	peek ADCON0, b2
	b2 = b2 and $FE		; clear bit 0 to disable the ADC
	poke ADCON0, b2
	; send XYZ and raw ADC setting.
	; there is a possibility that the 'X' header byte could
	; occur in the data. This will only cause a problem if
	; the data is already unsynchronised so it isn't thought
	; to be a problem.
	high txEnable
	;setfreq M4
	rfout txData,("X",XL,XH,YL,YH,ZL,ZH,b0)
	;setfreq M1
	low txEnable
	#ifdef TEST
	sertxd("*")
	#endif
	return;
}	
;==================================================

; compass control
{
; The compass module returns X,Y amd Z field strengths as 12 bit
; signed integers. The PicAxe works in unsigned arithmetic so in it's
; world negative numbers have the sign bit set and so are large.
; This code takes account of this and also does some scaling to avoid
; 16 bit multiplies and divides from overflowing.

; compass module I2C address
symbol HMC5883_I2C = $3c

initCompass:
	hi2csetup i2cmaster, HMC5883_I2C, i2cslow, i2cbyte
	pause 100
	#ifdef TEST
	;sertxd ("hi2cin",cr,lf)
	;hi2cin $0A, (b0,b1,b2)
	;sertxd ("id:",b0,b1,b2,cr,lf)
	#endif
	; 15 samples/sec, normal bias, gain 1, sleep mode
	; gain 1 seems OK as long as the sensor is flat
	hi2cout 0, ($10, %00100000, $03)
	return
	
;---------------------------------------------------------
; read the raw compass values into X, Y and Z words
; as signed integers in the range -2048 to 2047
; set dataValid to 0 if any of the compass values
; are invalid, to 1 if they are all valid
	
readCompass:
	hi2cout 2,(%00000001)			; single conversion
	pause 7							; wait for conversion
	hi2cin 3,(XH,XL, ZH,ZL, YH,YL)	; read compass
	if X=$F000 or Y=$F000 or Z = $F000 then
		dataValid = 0
		return
	else
		dataValid = 1
		goto calcHeading
	endif
	return
}

; Calculate compass heading
{
; calculates the heading from the raw X and Y
; words.  These must be in the range -2048 to +2047
; Uses W0, W1
; returns heading
; no offset correction
; scale correction from the calib
calcHeading:
	; get absolute values of X and Y
	W0 = Y
	call calcAbs
	W1 = W0
	W0 = X
	call calcAbs
	; divide by the largest to avoid infinities
	if W0 < W1 then
		; angle is 0 to 45
		call calcAngle
	else
		; angle is 90 to 45
		swap w0, w1
		call calcAngle
		b2 = 90 - b2
	endif
	; work out what quadrant it's in
	; unsigned so -Ve values are large
	if X <= 2048 then
		if Y <= 2048 then
			; X+, Y+, 0 to 90
			heading = b2
		else
			; X+ Y-, 180 to 90
			heading = 180 - b2
		endif
	else
		if Y <= 2048 then
			; X-, Y+, 360 to 270
			heading = 360 - b2
		else
			; X-. Y-, 180 to 270
			heading = b2 + 180
		endif
	endif
	; reverse the heading for negative Zs
	if Z > $8000 then
		heading = 360 - heading
	endif
	return
}
;======================================================
; utility subroutines
{
; convert W0 to an absolute value
calcAbs:
	if W0 >= $8000 then
		w0 = -w0
	endif
	return

;===================================================

; W0 and W1 contain the x and y values
; divide W0 by W1 to get the tangent and look it up
; in the table.
; W0 and W1 must be less than 2048 and
; W1 greater than W0
; returns angle in B2 in the range 0 to 45	
calcAngle:
	; scale W0 and W1 to get W0 100 times w1
	; but avoid overflows
	select case W0
	case 0 to 655
		W0 = W0 * 100
	case 656 to 1310
		W0 = W0 * 50
		W1 = W1/2
	else
		W0 = W0 * 25
		W1 = W1 / 4
	end select
	if W1 = 0 then
		b2 = 0
		return
	endif
	W0 = W0 / W1
	; this gets us the tangent in the range 0 to 100
	; corresponding to 0 to 45 degrees
	; look up the angle using the low byte of
	; the tangent
	read b0, b2
	return

; table of angles corresponding to tangents
; address is the tangent * 100, data is the
;  angle in the range 0 to 45 degrees
EEPROM 0, ( 0, 1, 1, 2, 2, 3, 3, 4, 5, 5)
EEPROM 10,( 6, 6, 7, 7, 8, 9, 9,10,10,11)
EEPROM 20,(11,12,12,13,13,14,15,15,16,16)
EEPROM 30,(17,17,18,18,19,19,20,20,21,21)
EEPROM 40,(22,22,23,23,24,24,25,25,26,26)
EEPROM 50,(27,27,27,28,28,29,29,30,30,31)
EEPROM 60,(31,31,32,32,33,33,33,34,34,35)
EEPROM 70,(35,35,36,36,36,37,37,38,38,38)
EEPROM 80,(39,39,39,40,40,40,41,41,41,42)
EEPROM 90,(42,42,43,43,43,44,44,44,44,45)
EEPROM 100,(45)
}
; test function
{
#ifdef TEST
txsignedW:
	if w0 < $8000 then
		sertxd (#w0)
	else
		w0 = -w0
		sertxd ("-",#w0)
	endif
	return
	
txXYZ:
	w0 = X : call txsignedw
	sertxd(",")
	w0 = Y : call txsignedw
	sertxd(",")
	w0 = Z : call txsignedw
	return
#endif
}
I've had another module running for years so it looks as if it could be something to do with this processor or the layout.

Chris
 
Top