Having decided I can’t be bothered with digital sensors with oddball serial interfaces like the DHT22 it was time to suck it up when I needed a number of sensors. Cost adds up with lots of sensors – though that Honeywell product more than paid for itself a few times over in much better hatch rates (fertile eggs are about £2 a pop by post, that’s how much of a loss you eat for every failure to hatch!) not every sensor application affects the bottom line like that. Sometimes low cost trumps accuracy, reliability and serviceability. Enter the AM2302, apparently a.k.a. DHT22, produced by the fine Aosong corporation. Their website looks like line noise on my browser, but apparently they are based in Guangzhou, which is China’s third largest city, a conurbation of nearly thirteen million souls.
The sensors are cheap, nasty and have poor accuracy, but the price is right, it’s the cheapest way to get a humidity and temperature sensor. Five for £17.70 or a unit price of £3.54 from a Chinese supplier on ebay, Buyincoins ISTR. They have a non-standard one-wire interface. That requires you to be able to tell a 30μS high duration from a 68μS high duration. No problem, eh, even with a PIC running on the internal 4MHz oscillator so each clock cycle is 1μS?
Except it doesn’t work – it acts up after about 20s in the video. It sort of works some times, tantalising short runs of OK in amongst loads of timeout errors. I fiddle with the power supply a little as the AM2302 is claimed to be finicky on the need for 5V. No luck. Tracing the library code I find it barfs around
The 16F628 pic doesn’t actually have a A/D converter. It has a couple of comparators and a software controllable voltage reference which does pretty well for most applications. I’m looking at using it for a low-battery cutoff device to protect 12V lead-acid batteries.The two comparators would work for that – one for the low-water mark, and one set a little bit above for a level the charge needs to reach before the load is reapplied, with some time delay probably too.
However, I came across Microchip’s Application note AN700 Make a Delta-Sigma Converter Using a Microcontroller’s Analog Comparator Module which shows how to use the comparator as an A/D converter. There are plenty of PICs that already have an A/D converter – the 16F88 has several analogue channels, but the 16F628 is cheaper. A lot of applications need just one analogue value sampled, and my application also wants an A/D that includes 12V. If I used a chip with an onboard A/D I’m going to need two resistors anyway to pad 12V down to the 5V range, and AN700 fig 7 shows how to take advantage of the external parts to shift the A/D range up to about 12V.
Trouble is, I want to use JAL, to make life easier when I write the rest of the code, and the assembler mode of JAL didn’t work. I swiped this from here probably derived from here but it didn’t work for me. I took a look at the code and suspected the assembler part, because what was happening was as soon as I called the DeltaSigA2D() the whole program seemed to sit down and start again from the top, resulting in endless printing of “STARTED” and little else.
Thinking about what JAL is probably doing with assembler, I suspected some of the clever space-saving constructs like the bit in eat5cycles that goes
goto +1
which was supposed to be
goto $+1
could be asking for trouble unless I could really assume that next instruction is the next one in the program space. In the end if you’re going to use something like JAL you aren’t trying to eke out every last drop of code space, so I figured I would nut the cleverness and use stacked nops. And it worked.
-- JAL 2.4i
-- Just the A/D 15 June 2013
-- modded by Richard
--
-- adc_test_16f628_v03
-- after DeltaSig.asm v1.00 from Dan Butler, Microchip Technology Inc. 02 December 1998
-- after http://den-nekonoko.blog.so-net.ne.jp/2010-11-25 and AN700
-- continually spews 10-bit value on TXD at 9600 baud, 5V=1024 0V=0
-- AN700 Fig 2
include 16f628_inc_all
-- We'll use internal oscillator. It work @ 4MHz
pragma target clock 4_000_000
const word _fuses = 0x3fff ; default value
pragma target osc INTRC_NOCLKOUT
pragma target WDT off
pragma target MCLRE off
pragma target PWRTE on
pragma target BODEN on
pragma target LVP off
-- HS, -BOD, ,-LVP, -WDT, -CP = 0x3F22
; pragma target fuses 0x3D02
--enable_digital_io()
; const bit enhanced_flash_eeprom = false
include pic_general
include delay
var word result
var byte result_l
var byte result_h
var byte counter1
var byte counter2
const byte booted[] = "STARTED"
pin_a0_direction = input
pin_a1_direction = input
pin_a2_direction = input ; comparator in / vref out
pin_a3_direction = output ; comp1 out
pin_a4_direction = output ; comp2 out (OD)
pin_b2_direction=output ; RS232 TX
const bit usart_hw_serial = TRUE
const serial_hw_baudrate = 9600
include serial_hardware
serial_hw_init()
include print
include format
Procedure InitDeltaSigA2D() is
VRCON=0b11101100 -- 0xEC
CMCON=0b00000110 -- 0x06 two common ref comps with outputs
end procedure
;
; Delta Sigma A2D
; The below contains a lot of nops and goto next instruction. These
; are necessary to ensure each pass through the Elop1 takes the same
; amount of , no the path through the code.
;
Procedure DeltaSigA2D() is
assembler
LOCAL elop1,comphigh,complow,eat2cycles,endelop1,eat5cycles,exit1
--DeltaSigA2D
clrf counter1
clrf counter2
clrf result_l
clrf result_h
movlw 0x03 ; set up for 2 analog comparators with common reference
movwf cmcon
elop1:
btfsc c1out ; is comparator high or low?
-- btfsc cmcon,c1out ; is comparator high or low?
goto complow ; go the low route
comphigh:
nop ; necessary to timing even
bcf porta,3 ; porta.3 = 0
incfsz result_l,f ; bump counter
goto eat2cycles ;
incf result_h,f ;
goto endelop1 ;
complow:
bsf porta,3 ; comparator is low
nop ; necessary to keep timing even
goto eat2cycles ; same here
eat2cycles:
goto endelop1 ; eat 2 more cycles
endelop1:
incfsz counter1,f ; count this lap through the elop1.
goto eat5cycles ;
incf counter2,f ;
movf counter2,w ;
andlw 0x04 ; are we done? (we're done when bit2 of
-- btfsc status,_z ; the high order byte overflows to 1).
btfsc Z --status,z ; the high order byte overflows to 1).
goto elop1 ;
goto exit1
eat5cycles:
nop -- this really seems to hate the goto $+1, keeps resetting. I am not so short of code space, case to be made to turn everything into nops
nop
--goto +1 ; more wasted time to keep the elop1s even
nop ;
goto elop1 ;
exit1:
movlw 0x06 ; set up for 2 analog comparators with common reference
movwf cmcon
end assembler
end procedure
function rescale(WORD IN value) RETURN WORD is
-- 1024 corresponds to 5V
-- multiply by 50000/1024 (~49)
var word temp =value*49
temp=temp/100 -- now lose excess precision
return temp
end function
InitDeltaSigA2D()
print_string(serial_hw_data,booted)
forever loop
DeltaSigA2D()
result=word(result_h)*256 + word(result_l) -- you must explicity make result_h a word as described on p11 of JAL manual
result=rescale(result)
format_word_dec(serial_hw_data, result, 3, 2)
serial_hw_data = " "
serial_hw_data = 13 -- CR
serial_hw_data = 10 -- LF
delay_1ms(500) -- this is purely to allow the terminal display to catch up and not overload buffer of the slow device
end loop
There’s a case to be made for using a voltage reference and setting it to be some convenient power of 2 like 2.05 V to save all the messy integer calculation in rescale
This is a video of the display on the serial port compared with a multimeter on the input, connected to a 5k pot between 0V and +5V. I should have moved the control a bit slower to allow the 0.5s sampling to catch up at times, but it shows it is reasonably accurate.