adc.pwp


//-------------------------------------------------------------------------
//
// PicoWeb Project File for WebADC
//
//-------------------------------------------------------------------------
//
// application-specific preprocessor definitions
//
#define BANNER "\r\nPicoWeb WebADC\r\n"
#define EEPROM_IP         /* use file "ip" for IP address */
#define NET_CONFIG_IP     /* allow IP address reconfiguration via net */
#define ENABLE_WATCHDOG   /* use Atmel watchdog timer hardware */
#define DEBUGGER          /* include debugger firmware */

//#define CLOCK 8000000
#define CLOCK 7372000     /* processor clock rate (Hz) */
#define BAUD_RATE 19200   /* serial port baud rate */

#define ADC_BLINK_LED     /* will blink PicoWeb LED on ADC reads */

//
// application-specific HTML and image file names
//
adc.htm      // (default Web page)
adc1.htm
adc2.htm
adc3.htm
read.htm
info.htm
block.png
pwadc.png

//
// application-specific CGI routines
//
readtemp.cgi // returns temperature in deg-C
readpwr.cgi  // returns power supply voltage in mV
readhigh.cgi // returns 0-1V differential input voltage in mV
readlow.cgi  // returns 0-0.5V differential input voltage in mV
rawtemp.cgi  // returns raw 10-bit temperature reading
rawpwr.cgi   // returns raw 10-bit power supply reading
rawhigh.cgi  // returns raw 10-bit differential input reading (0-1V)
rawlow.cgi   // returns raw 10-bit differential input reading (0-.5V)

//test_printdec.cgi

//
// application-specific assmbly language files
//

//
// included application-specific pcode and/or AVR assembly language follows
//
#avr_reset
;--------------------------------------------------------------------------
; this code is executed each time microcontroller is reset
;--------------------------------------------------------------------------
;
;   Define LTC1392 connetions to PicoWeb
;
#define DDR(port) (port-1)
#define PINS(port) (port-2)   

#define MAKE_INPUT(port, bit)  cbi DDR(port),bit
#define MAKE_OUTPUT(port, bit) sbi DDR(port),bit

#define ADC_CS_BIT  0       /* ADC_CS (CS_), output, DB-25 pin 4, PB0 */
#define ADC_CS_PORT PORTB

#define ADC_CLK_BIT  1      /* ADC_CLK (clock), output, DB-25 pin 6, PB1 */
#define ADC_CLK_PORT PORTB

#define ADC_D_BIT  2        /* ADC_D (data in/out), input, DB-25 pin 8, PB2 */
#define ADC_D_PORT PORTB

#define ADC_CLK_HIGH  sbi ADC_CLK_PORT,ADC_CLK_BIT /* ADC CLK high */
#define ADC_CLK_LOW   cbi ADC_CLK_PORT,ADC_CLK_BIT /* ADC CLK low */

    sbi ADC_CS_PORT,ADC_CS_BIT               ; "ADC_CS" => high
    MAKE_OUTPUT(ADC_CS_PORT,ADC_CS_BIT)      ; make CS an output

    MAKE_OUTPUT(ADC_CLK_PORT,ADC_CLK_BIT)    ; make CLK an output
    cbi ADC_CLK_PORT,ADC_CLK_BIT             ; "ADC_CLK" => low

    MAKE_INPUT(ADC_D_PORT,ADC_D_BIT)         ; make D an input
    cbi ADC_D_PORT,ADC_D_BIT                 ; ...and turn off pull-up!!

#ifdef ADC_BLINK_LED
#define LED_ON  cbi portd,LED_BIT     /* turn LED on */
#define LED_OFF sbi portd,LED_BIT     /* turn LED off */
#define PLED_ON  pledon               /* turn LED on with pcode */
#define PLED_OFF pledoff               /* turn LED off with pcode */
    sbi     DDRD,LED_BIT      ; make LED driver pin an output
    LED_OFF                   ; turn LED off
#else /* !ADC_BLINK_LED */
#define LED_ON
#define LED_OFF
#define PLED_ON
#define PLED_OFF
#endif /* !ADC_BLINK_LED */

#avr_slow
;--------------------------------------------------------------------------
; this code is executed each trip through "slow idle" loop (~1 sec period)
;--------------------------------------------------------------------------
;

#avr_fast
;--------------------------------------------------------------------------
; this code is executed each trip through "fast idle" loop
;--------------------------------------------------------------------------
;

#avr_asm
;--------------------------------------------------------------------------
; application-specific CGI pcode and AVR assembly routines go here
;--------------------------------------------------------------------------
;
;--------------------------------------------------------------------------
;+
; pread_adc - take reading using LTC1392 chip (micropower data 
;             acquisition chip from Linear Technology Corp)
;
; pcode use:
;
;   pread_adc a,n
;
; where:
;
;  a     - address in SRAM to get 16-bit result
;  n     - LTC1392 registers to read as follows:
;
;      n   Reading           Formula
;      -   -------           -------
;      0   Temperature       Temp(°C) = (10-bit code) / 4 - 130
;      1   Supply Voltage    VCC = [(10-bit code) * 4.84/1024] + 2.42
;      2   Input (0-1 V)     Diff Volts = 1.0V * (10-bit code) / 1024
;      3   Input (0-.5 V)    Diff Volts = 0.5V * (10-bit code) / 1024
;-
;--------------------------------------------------------------------------
#define ADC_TWAKEUP 10      /* normal Twakeup (usecs) */
#define ADC_TWAKEUPT 80     /* temperature Twakeup (usecs) */
#define ADC_THALF  2        /* length of normal 1/2 clock cycle (usecs) */
#define ADC_THALFT 2        /* length of temp 1/2 clock cycle (usecs) */

.cseg
pread_adc:    pcode_routine   2
    LED_ON                             ; turn on PicoWeb LED
    ADC_CLK_LOW                        ; drive CLK low
    MAKE_OUTPUT(ADC_D_PORT,ADC_D_BIT)  ; make D an output
    cbi ADC_D_PORT,ADC_D_BIT           ; and drive low
    cbi ADC_CS_PORT,ADC_CS_BIT         ; "ADC_CS" => low (active)

    ldi r16l,ADC_TWAKEUP          ; default delay for "wakeup" period (after CS)
    ldi r17,ADC_THALF         ; get half-period clock (usec)
    tst r12l                  ; r10 == 0?
    brne pread_adc0           ; nope...skip ahead
    ldi r17,ADC_THALFT        ; yes (temp)...use different period
    ldi r16l,ADC_TWAKEUPT     ; use extra long delay for "wakeup" (after CS)
pread_adc0:

    ADC_CLK_LOW
    rcall pusecdly            ; r16l still had delay (in usec)
    ADC_CLK_HIGH              ; toggle clock signal
    mov r16l,r17              ; delay for 1/2 normal clock period
    rcall pusecdly
    ;
    ; send command nibble
    ;
    mov r20l,r12l             ; r12 has device register
    lsl r20l                  ; shift up to make room for START/MSBF bits
    ldi r16l,((1<<3)|(1<<0))  ; add in START=1 and MSBF=1
    eor r20l,r16l
    ldi r18,4                 ; write 4 bits
pread_adc_loop1:
    sbrc r20,3                ; skip if bit 3 not set
    sbi ADC_D_PORT,ADC_D_BIT  ; D bit = 1
    sbrs r20,3                ; skip if bit 3 set
    cbi ADC_D_PORT,ADC_D_BIT  ; D bit = 0

    rcall adc_toggle_clk      ; toggle clock

    lsl r20l                  ; shift bits

    dec r18                   ; loop again?
    brne pread_adc_loop1         ; yes
    ;
    ; switchover data bus
    ;
    MAKE_INPUT(ADC_D_PORT,ADC_D_BIT)  ; make D an input
    cbi ADC_D_PORT,ADC_D_BIT          ; ...and turn off pull-up!!

    rcall adc_toggle_clk      ; one more clock toggle before read-back

    ;
    ; read back bits from device
    ;
    movwi r20,0              ; accumlate 16-bit value here
    ldi r18,10               ; read 10 bits
pread_adc_loop2:

    rcall adc_toggle_clk

    clc                      ; shift 16-bit value left
    rol r20l
    rol r20h
    in r22,PINS(ADC_D_PORT)  ; read I/O port with D bit
    sbrc r22,ADC_D_BIT       ; skip if D-bit not set
    inc r20l                 ; add 1 to value

    dec r18                  ; loop again?
    brne pread_adc_loop2        ; yes

    sbi ADC_CS_PORT,ADC_CS_BIT ; "ADC_CS" => high (inactive)

    movw    x,r10            ; return 16-bit value
    st      x+,r20l
    st      x+,r20h
    ret
;
;  delay for number of usec in r16l (0-128)
;
pusecdly:                    ; assume 8 MHz clock
    lsl r16l                 ; double trip count (0.5 usec/trip)
pusecdly1:
    dec r16l                 ; +1
    nop                      ; +1
    brne pusecdly1           ; +2
                             ;----
    ret                      ;  4 clocks * 2 total (1 usec/loop)
;
;  cycle ADC CLK (low->high) with proper delays
;
;         _        ______
;   CLK    \______/
;
adc_toggle_clk:              ; toggle CLK
    ADC_CLK_LOW              ; clock => low
    mov r16l,r17             ; delay 2 usec
    rcall pusecdly           ; delay
    ADC_CLK_HIGH             ; clock => high
    mov r16l,r17             ; delay 2 usec
    rcall pusecdly           ; delay
    ret

#ifdef ADC_BLINK_LED
pledon:    pcode_routine   0
    LED_ON
    ret

pledoff:   pcode_routine   0
    LED_OFF
    ret
#endif /* ADC_BLINK_LED */

;--------------------------------------------------------------------------
;
; Debug routines
;
;--------------------------------------------------------------------------
.eseg
#ifdef SHOW_ADC_HEX_WITH_URL_PARAM
#   define SHOW_ADC_HEX pcall adc_show_hex
adc_show_hex:
    ppushwi [buf+2]                 ; save temp
    pmovwi buf+2,[data_addr]        ; get data address (skip over "GET ")
                                    ; 0123456789
    paddwi buf+2,10                 ; GET /iuNN?xxxxxxxxxxx
    pr2s buf+2,[buf+2],1            ; get next byte after ?
    pandwi buf+2,0xff
    pcmpwi buf+2,'X'
    pjumpne adc_no_show_hex
    pprint "(0x"                    ; print value in hex
    phexbi [buf+1]
    phexbi [buf]
    pprint ") "
adc_no_show_hex:
    ppopw buf+2
    pret
#else
#   define SHOW_ADC_HEX
#endif

;--------------------------------------------------------------------------
;+
; **-readXXX-read ADC and return converted reading as ASCII text
;-
;--------------------------------------------------------------------------
.cseg

readtemp:
    pread_adc buf,0               ; read LTC1392 ADC register 0 (temp)
    pread_adc buf,0               ; do second time for stable reading!!!
    pcall convert_adc_temp
adc_printdec:
    SHOW_ADC_HEX                  ; output reading in hex (if debug)
    pprintswi [buf]               ; output reading in decimal
    PLED_OFF                      ; turn off PicoWeb LED
    pret

readpwr:
    pread_adc buf,1               ; read LTC1392 ADC register 1 (4.5-6.0 VDC)
    pcall convert_adc_pwr
    pjump adc_printdec            ; output reading in decimal

readhigh:
    pread_adc buf,2               ; read LTC1392 ADC register 2 (0-1 VDC)
    pcall convert_adc_volts
    pjump adc_printdec            ; output reading in decimal

readlow:
    pread_adc buf,3               ; read LTC1392 ADC register 3 (0-0.5 VDC)
    pcall convert_adc_volts
    pshnw buf,-1                  ; divide by 2 before output (0-0.5V range)
    pjump adc_printdec            ; output reading in decimal

;--------------------------------------------------------------------------
;+
; **-rawXXX-read ADC and return raw reading as ASCII text
;-
;--------------------------------------------------------------------------

rawtemp:
#ifdef ADC_OSCOPE_LOOP
    pread_adc buf,0               ; read LTC1392 ADC
    pjump readtemp
#endif
    pread_adc buf,0               ; read LTC1392 ADC register 0 (temp)
    pread_adc buf,0               ; do second time for stable reading!!!
    pjump adc_printdec            ; output reading in decimal

rawpwr:
#ifdef ADC_OSCOPE_LOOP
    pread_adc buf,1               ; read LTC1392 ADC
    pjump readpwr
#endif
    pread_adc buf,1               ; read LTC1392 ADC register 1 (4.5-6.0 VDC)
    pjump adc_printdec            ; output reading in decimal

rawhigh:
    pread_adc buf,2               ; read LTC1392 ADC register 2 (0-1 VDC)
    pjump adc_printdec            ; output reading in decimal

rawlow:
    pread_adc buf,3               ; read LTC1392 ADC register 3 (0-0.5 VDC)
    pjump adc_printdec            ; output reading in decimal

;--------------------------------------------------------------------------
;
; Conversion routines
;
;--------------------------------------------------------------------------
;
;  Temperature (°C) = (10-bit code) / 4 - 130
;
convert_adc_temp:
    pdiv buf,[buf],4                   ; deg-C = val/4 - 130
    psubwi buf,130
    pret
;
;  Measured VCC = [(10-bit code) * 4.84/1024] + 2.42
;
convert_adc_pwr:
    pmul buf,[buf],47                  ; V = (val * 47) / 10 + 2420
    pdiv buf,[buf],10                  ; !!! approximate (0.6% error) !!!
    paddwi buf,2420
    pret
;
;  Differential Voltage = 1.0V * (10-bit code) / 1024;  (0-1.0 V range)
;                         0.5V * (10-bit code) / 1024;  (0-0.5 V range)
;
convert_adc_volts:
#ifdef DEBUG_CONVERT
    pprintswi [buf]                    ; output reading in decimal
    pprint "->"
#endif
    pmovwi buf+2,[buf]                 ; delta = ((val * 24) + 500) / 1000
    pmul buf+2,[buf+2],24
    paddwi buf+2,500
    pdiv buf+2,[buf+2],1000
    psubwi buf,[buf+2]                 ; val = val - 0.024 * val;
    pret

#ifdef TEST_DECCONV
.eseg
test_printdec:
  ;;pserial_debug
    pprint "\n"
    pmovwi buf,0
    pprint " 0:"
    pprintswi [buf]
    pprint "\n"
    pprint " -1:"
    pmovwi buf,-1
    pprintswi [buf]
    pprint "\n"
    pprint " -123:"
    pmovwi buf,-123
    pprintswi [buf]
    pprint "\n"
    pprint " -10005:"
    pmovwi buf,-10005
    pprintswi [buf]
    pprint "\n"
    pprint " 10005:"
    pmovwi buf,10005
    pprintswi [buf]
    pprint "\n"

    pprint " -1:"
    pprintuwi -1
    pprint "\n"
    pret

#include "pprintswi.asm"
#endif

Back