adc.pwp


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

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

#define CLOCK 8000000
///#define CLOCK 7372000
#define BAUD_RATE 19200

//
// application-specific HTML and image file names
//
adc.htm      // ii00  (default Web page)
adc1.htm     // ii01
adc2.htm     // ii02
adc3.htm     // ii03
read.htm     // ii04
info.htm     // ii05
block.png    // ii06
pwadc.png    // ii07

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

//
// 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's 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

#ifdef TEST_DECCONV
test_printdec:
    pprint "\n";
    pmovwi buf,0
    pcall printdec
    pprint "\n";
    pmovwi buf,-1
    pcall printdec
    pprint "\n";
    pmovwi buf,-123
    pcall printdec
    pprint "\n";
    pmovwi buf,-10005
    pcall printdec
    pprint "\n";
    pmovwi buf,10005
    pcall printdec
    pprint "\n";
    pret
#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)
    pcall printdec                ; output reading in decimal
    PLED_OFF                      ; turn off PicoWeb's 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
    ppushwi [buf]
    pcall printdec                     ; output reading in decimal
    ppopw buf
    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

;--------------------------------------------------------------------------
;+
; **-printdec-output signed 16-bit integer in decimal (pcode)
;
; inputs:
;   buf = word to convert
;
; outputs:
;   result printed (with pputc)
;
; working SRAM:
;   buf - buf+14 (save/restores on stack)
;
;--------------------------------------------------------------------------
#define DIG_BUF buf+10      /* uses 5 bytes for ASCII digit storage */
#define DIG_SGN buf+8
#define DIG_CNT buf+6
#define DIG_CH  DIG_CNT
#define DIG_PTR buf+4
#define DEC_REM buf+2
#define DIG_LST DEC_REM
#define DEC_VAL buf

.cseg
printdec:
    ppushn buf,15                       ; stack everything we use

    pmovwi DIG_SGN,' '                  ; set default sign
    pbitwi DEC_VAL,0x8000               ; check the sign
    pjumpeq printdec0                   ; positive - start conversion
    pnegw DEC_VAL                       ; negate it
    pmovwi DIG_SGN,'-'                  ; change sign to negative

printdec0:
    pmovwi DIG_PTR,DIG_BUF              ; get input word
    pmovwi DIG_CNT,5                    ; we will do 5 bytes
printdec1:
    pdiv DEC_VAL,[DEC_VAL],10           ; get next one
    paddwi DEC_REM,'0'                  ; convert digit for display
    pmovbi [DIG_PTR],[byte DEC_REM]     ; save digit
    pincw DIG_PTR                       ; bump past digit just stored
    pdecw DIG_CNT                       ; decrement count
    pjumpne printdec1                   ; more to do if non-zero

    pclrw DIG_CH                        ; setup to output digits
    pmovwi DIG_LST,' '                  ; make "last character" a space
printdec2:
    pdecw DIG_PTR                       ; point to next digit
    pmovb DIG_CH,[byte DIG_PTR]         ; get next digit
    pcmpwi DIG_CH,'0'                   ; is it '0'?
    pjumpne printdec4                       ; no - may need sign char
    pcmpwi DIG_LST,' '                  ; last char ' '?
    pjumpne printdec3                       ; no - continue as normal
    pcmpwi DIG_PTR,DIG_BUF              ; this is the very last digit?
    pjumpeq printdec4                       ; yes - check on sign
    pmovwi DIG_CH,' '                   ; output a ' ' instead

printdec3:
    pputcb DIG_CH                       ; output the current digit
    pmovb DIG_LST,DIG_CH                ; save last digit output
    pcmpwi DIG_PTR,DIG_BUF              ; check if just did last one
    pjumpne printdec2                      ; no - do the next digit

    ppopn buf,15                        ; restore everything from stack
    pret

printdec4:
    pcmpwi DIG_SGN,0                    ; have we output sign char?
    pjumpeq printdec3                       ; no - continue as normal
    pputcb DIG_SGN                      ; output sign
    pclrw DIG_SGN                       ; zap sign character
    pjump printdec3                     ; continue as normal

;--------------------------------------------------------------------------
.cseg
#include "muldiv.asm"

Back