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
which was supposed to be
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 -- http://www.megalithia.com/2013/06/16f628-pic-ad-using-jal/ -- 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
and the hex file
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.