| |
udpdata.pwp
// PicoWeb Project File
#define BANNER "\r\nPicoWeb UDP data acquisition tester.\r\n"
#define EEPROM_IP /* use file "ip" for IP address */
#define ENABLE_WATCHDOG
#undef DEBUGGER /* no debugger */
#define SERIAL_BAUD_DIVISOR 51 /* 9600 @ 8Mhz */
#define USER_int0_isr int0_isr /* plug INT0 interrupt */
#define USER_int1_isr int0_isr /* plug INT1 interrupt */
#undef DUMMY_DATA /* define for dummy tringle wave data (no H/W) */
//#define SHOW_UDP_REQUESTS
#define UDP_CHECKSUM_DONE
#define UDP_CMD_PORT 99
#define Data_PORT PORTB /* D0-7, input, PB0-7 */
#define Data_PINS PINB
#define Data_DDR DDRB
#define RdClk_BIT 4 /* RdClk, output, DB-25 pin 12, PD4 (same as LED2) */
#define RdClk_PORT PORTD
#define RdClk_PINS PIND
#define RdClk_DDR DDRD
#define Start_BIT 2 /* Start, input, DB-25 pin 7, PD2 */
#define Start_PORT PORTD
#define Start_PINS PIND
#define Start_DDR DDRD
#define Done_BIT 3 /* Done, output, DB-25 pin 9, PD3 */
#define Done_PORT PORTD
#define Done_PINS PIND
#define Done_DDR DDRD
// HTML and images
udpdata.htm // base page
// CGI routines
sertest.cgi // iu00
// Assembly language
//------------------ included AVR assembly language follows ---------------
#avr_reset
clr r17
sts data_pkt_count,r17 ; reset packet counter
sts udp_byte_count,r17 ; clear UDP counter
sts udp_byte_count+1,r17 ;
clr r17
out Data_DDR,r17 ; make PB0-7 input
out Data_PORT,r17 ; ...and turn off pull-ups
sbi RdClk_DDR,RdClk_BIT ; make output
sbi Done_DDR,Done_BIT ; make output
cbi Start_DDR,Start_BIT ; make input
cbi Start_PORT,Start_BIT ; ...and turn off pull-up!!
cbi RdClk_PORT,RdClk_BIT ; "RdClk" => low
cbi Done_PORT,Done_BIT ; "Done" => low
#avr_slow
#avr_fast
rcall udp_loop_processing
in r17,MCUCR ; edge sensitive interrupts
ori r17,0x0f
out MCUCR,r17
clr r17 ; disable INT0 and INT1
out GIMSK,r17
#avr_asm
/*--------------------------------------------------------------------------
Data Acquisition Interface
The data acquisition interface to the PicoWeb UDP Server uses
17 pins on the unit's 25-pin male D-subminiature (DB-25P)
connector. When data is ready to be transferred into the
PicoWeb UDP server for retransmission to a remote host
computer over the Ethernet, the attached data acquisition
device activates the "Start" signal. The PicoWeb server
first waits until it receives a "start data acquisition"
UDP packet from the controlling host computer. If both
conditions are met, the PicoWeb UDP server begins taking
data, one 8-bit byte at a time over the data bus (D0-D7).
These data bytes are transferred to the PicoWeb's outgoing UDP
packet buffer. The signal "RdClk" is used to clock read data
from the external device. It is assumed that the external
device can supply data faster than the PicoWeb firmware can
strobe the read data clock (i.e., some sort of hardware FIFO
buffer in the external device's data path is assumed).
The amount of data clocked from the external device for
each "start data acquisition" UDP packet received is
user-programmable, set by a data word in the "start data
acquisition" UDP packet. Acquired data automatically will
be broken up into a maximum of 1024-byte data packets and
sent in sequence to the remote host computer. Besides the
acquired data, the UDP data packet sent to the remote host also
contains header information with a data packet sequence number.
Lost UDP packets are not automatically retransmitted by the
PicoWeb server.
Once the requested number of bytes of data has been clocked
from the external device, any remaining buffered UDP packet
data is sent to the remote host, and the PicoWeb server
signals completion to the external device by activating the
"Done" signal. The external device acknowledges this signal
my de-asserting the original "Start" signal. The PicoWeb UDP
server then waits for the assertion of a new "Start" signal and
the arrival of a new "start data acquisition" UDP packet before
beginning the data acquisition/UDP transfer sequence again.
--------------------------------------------------------------------------*/
#include "tcpip.inc"
#include "mempkt.asm"
.dseg
client_ip: .byte 4
client_port: .byte 2
udp_byte_count: .byte 2
udp_pkt_seq: .byte 2 ; packet sequence # 0...N
packet_header: .byte UDP_DATA ; ether/ip/udp headers
data_pkt_count: .byte 2 ; incremented by 1 for every packet sent
.cseg
#define UDP_PKT_LEN 1024
#define MEM_ETH_DEST packet_header+0
#define MEM_ETH_SRC MEM_ETH_DEST+6
#define MEM_ETH_PT MEM_ETH_SRC+6
#define MEM_IPH MEM_ETH_PT+2
#define MEM_IP_HVTOS MEM_IPH+0
#define MEM_IP_LEN MEM_IPH+2
#define MEM_IP_ID MEM_IPH+4
#define MEM_IP_FFO MEM_IPH+6
#define MEM_IP_TTLP MEM_IPH+8
#define MEM_IP_CHKSUM MEM_IPH+10
#define MEM_IP_SRCADDR MEM_IPH+12
#define MEM_IP_DSTADDR MEM_IPH+16
#define MEM_IPD MEM_IPH+20
#define MEM_UDP_HEADER MEM_IPD+0
#define MEM_UDP_SP MEM_UDP_HEADER+0
#define MEM_UDP_DP MEM_UDP_HEADER+2
#define MEM_UDP_LEN MEM_UDP_HEADER+4
#define MEM_UDP_CHKSUM MEM_UDP_HEADER+6
#define MEM_UDP_DATA MEM_UDP_HEADER+8
;+
; **-udp-udp receive handler.
;
; return with ret.
;-
#define UDP_FUNC UDP_DATA
import_udp:
rcall pcode
pr2s buf,UDP_DP,2 ; get destination port
pcmpwi buf,BSWAP(UDP_CMD_PORT) ; check if it matches
pjumpne udp_bail
pr2s udp_byte_count,UDP_DATA,2 ; byte count is first two bytes of data
pmovwi udp_pkt_seq,0 ; zero sequence number
pincw data_pkt_count
#if defined(SHOW_UDP_REQUESTS)
ppushn putcok,1
ppushn putc_b,1
pmovbi putcok,1
pmovbi putc_b,0
pprintr "UDP src",IP_SRCADDR,4
pprintr ":",UDP_DP,2
pprintv " len ",[udp_byte_count]
pcrlf
ppopn putc_b,1
ppopn putcok,1
#endif
pcall set_client ; remember who to send data to!
udp_bail:
pend
ret
;+
; **-udp_loop_processing-fast loop hook.
;
; this routine is called every time through the "fast loop". it generates the
; next packet if there is more data to send.
;
; note:
; this routine is called via "rcall", and must return with "ret".
;-
udp_loop_processing:
#ifndef DUMMY_DATA
in r17,Start_PINS ; "Start" signal active?
bst r17,Start_BIT ; move "Start" bit to T-reg
brts started ; check T-reg
ret ; not set...return
started: ; set...send (more) UDP data
#endif
rcall pcode
pandwi udp_byte_count,0xffff ; check remaining byte count
pjumpeq udp_bail
#if defined(SHOW_UDP_REQUESTS)
pprintv "pkt=",[udp_byte_count]
#endif
pmovwi MEM_UDP_LEN,UDP_PKT_LEN+2 ; set length (+2 for sequence #)
pf2x UDP_DATA,movedata,UDP_PKT_LEN+2 ; move the data
pcall mem_udp_send_client ; send it
psubwi udp_byte_count,UDP_PKT_LEN ; minus bytes just moved (excluding seq #)
pjumplo udp_stop
pjumpeq udp_stop
pjump udp_bail ; more to send
udp_stop:
pmovwi udp_byte_count,0 ; zero out count
#ifndef DUMMY_DATA
psignal_done ; signal that we are done
#endif
pjump udp_bail
;+
; **-set_client-remember requestor as client for future autonomous sends.
;
; inputs:
; current receive packet contains client info
;
; outputs:
; appropriate info copied to memory packet buffer.
;
; caveats:
; called via pcall/pret
;-
set_client:
pr2s client_ip,IP_SRCADDR,4 ; remember client's IP
pr2s client_port,UDP_SP,2 ; and port number
pr2s MEM_ETH_DEST,ETH_SRC,6 ; and client ether
pmemcpy MEM_IP_SRCADDR,my_ip,4 ; source ip == me
pmovwi MEM_UDP_SP,BSWAP(UDP_CMD_PORT) ; and port
pmemcpy MEM_ETH_SRC,my_ether,6 ; and ether
pmovwi MEM_ETH_PT,0x0008 ; set PT=IP
pmovwi MEM_IP_HVTOS,0x0045 ; 4500
pmovwi MEM_IP_TTLP,0x1101 ; UDP proto and TTL=1
pmovwi MEM_IP_FFO,0 ; zero fragment stuff
pret
#ifdef DUMMY_DATA
;+
; **-movedata-move dummy data to packet.
;
; inputs:
; none
;
; outputs:
; data moved
;
; notes:
; demonstrates how to use pf2x with initial setup processing vs.
; "during the loop" processing.
;-
movedata:
movwi z,movedata_next
clrw x ; reset the checksum
ldsw y,udp_pkt_seq ; get packet sequence #
rcall moveword ; "move" it
incw y ; advance sequence #
stsw udp_pkt_seq,y ; store it back
lds yl,data_pkt_count ; get start of sequence
mov yh,yl
inc yh
ret
movedata_next: ; called to supply next 2 bytes of UDP data
rcall moveword ; set dummy data
inc yl ; bump dummy data counters in yh,yl
inc yl ; (this makes a triangle wave)
inc yh
inc yh
ret
moveword:
mov i0,yl ; supply next two bytes of dummy data
mov i1,yh ; i0/i1 = next word of data
f2x_checksum y,r10 ; accumulate checksum - r10 destroyed
ret
#else /* !DUMMY_DATA */
;+
; **-movedata-move UT data from FIFO to packet.
;
; inputs:
; none
;
; outputs:
; data moved from FIFO to outgoing UDP packet
;-
movedata:
movwi z,movedata_next
clrw x ; reset the checksum
ldsw y,udp_pkt_seq ; get packet sequence #
rcall moveword ; "move" it
incw y ; advance sequence #
stsw udp_pkt_seq,y ; store it back
lds yl,data_pkt_count ; get start of sequence
mov yh,yl
inc yh
ret
movedata_next: ; called to supply next 2 bytes of UDP data
in yl,Data_PINS ; read LSB from FIFO
rcall strobe_RdClk
in yh,Data_PINS ; read MSB from FIFO
rcall strobe_RdClk
mov i1,yh ; i0/i1 = next word of data
mov i0,yl
f2x_checksum y,r10 ; accumulate checksum - r10 destroyed
ret
strobe_RdClk:
sbi RdClk_PORT,RdClk_BIT ; "RdClk" => high
nop ; delay (2 nops + sbi => 4 8MHz clocks)
nop
cbi RdClk_PORT,RdClk_BIT ; "RdClk" => low
ret
moveword:
mov i0,yl ; supply next two bytes of dummy data
mov i1,yh ; i0/i1 = next word of data
f2x_checksum y,r10 ; accumulate checksum - r10 destroyed
ret
#endif /* !DUMMY_DATA */
;+
; **-mem_udp_send-send a UDP datagram from ram.
;
; inputs:
; transmit buffer:
; MEM_UDP_DATA = user data to be sent
; MEM_UDP_LEN = native byte order length of data to send
;
; checksum algorithm:
;
; ip_chksum = 0 ;
; ip_chksum = ~checksum(IPH,IPHLEN)
; udp_chksum = 0
; ph.src = ip.src
; ph.dst = ip.dst
; ph.mbzprot = 0|(ip.prot<<8)
; ph.len = udp_len (UDP data only, not header)
; ph_chksum = checksum(UDP_HDR,UDP_LEN+UDP_HEADER_LEN)
; udp_chksum = ~checksum(PH_HDR,PH_HEADER_LEN)
;-
#define PH_HEADER rcv_hdr+en_rbuf_nhdr
#define PH_SRCADDR PH_HEADER+0
#define PH_DSTADDR PH_HEADER+4
#define PH_MBZPROT PH_HEADER+8
#define PH_LEN PH_HEADER+10
#define PH_CHKSUM PH_HEADER+12
#define PH_HEADER_LEN 14
#define MOVW(x,y) pmovwi x,[y]
#define UDP_CHECKSUM_DONE
mem_udp_send_client:
pmemcpy MEM_IP_DSTADDR,client_ip,4 ; destination = client
pmovwi MEM_UDP_DP,[client_port] ; and client port
mem_udp_send:
#if defined(UDP_MEM_SEND_DEBUG)
pprintv " MVCHK=",[chkacc]
pclrw chkacc
pmem_chksum MEM_UDP_DATA,[MEM_UDP_LEN]
pprintv " UDPCHK=",[chkacc]
#endif
;
; compute UDP length and store in UDP_LEN field and PH_LEN field.
;
MOVW(buf,MEM_UDP_LEN) ; get length (native order)
paddwi buf,UDP_HEADER_LEN ; total length
MOVW(MEM_UDP_LEN,swap buf) ; set UDP length
;
; checksum UDP data, and store checksum (uncomplemented) in pseudoheader.
;
pclrw MEM_UDP_CHKSUM ; zero UDP checksum
#if defined(UDP_CHECKSUM_DONE)
pmem_chksum MEM_UDP_HEADER,UDP_HEADER_LEN
#else
pclrw chkacc ; reset accumulator
pmem_chksum MEM_UDP_HEADER,[buf] ; compute UDP checksum
#endif
MOVW(PH_CHKSUM,chkacc) ; store checksum in pseudoheader
paddwi buf,IP_HEADER_LEN
MOVW(MEM_IP_LEN,swap buf) ; set IP length
;
; the IP header should be ready, zero its checksum, checksum it, and
; store complemented checksum back in header.
;
pclrw chkacc
pclrw MEM_IP_CHKSUM ; zero IP header checksum
pmem_chksum MEM_IPH,IP_HEADER_LEN ; checksum IP header
pxorwi chkacc,-1 ; complement the ip checksum
#if defined(LOG_MEM_CHKSUM)
pprintv "IPCK ",[chkacc]
#endif
MOVW(MEM_IP_CHKSUM,chkacc) ; store in IP header
;
; finally, checksum the pseudoheader and store complemented checksum
; in UDP header.
;
pmemcpy PH_SRCADDR,MEM_IP_SRCADDR,8 ; source/dest IP addrs
pmovwi PH_MBZPROT,0x1100 ; UDP proto in PH
MOVW(PH_LEN,MEM_UDP_LEN) ; store net order length in ph
pclrw chkacc
pmem_chksum PH_HEADER,PH_HEADER_LEN ; checksum pseudoheader
pxorwi chkacc,-1 ; complement
MOVW(MEM_UDP_CHKSUM,chkacc) ; store in UDP header checksum
#if defined(LOG_MEM_CHKSUM)
pprintv "PHCK ",[chkacc]
#endif
;
; compute total packet size and send.
;
MOVW(buf,swap MEM_IP_LEN) ; ip length total
paddwi buf,6+6+2 ; compute length with ether header
#if defined(UDP_CHECKSUM_DONE)
;
; if the checksum has already been computed, it also means the packet
; data is in the frame buffer, so just load the headers.
;
ps2x 0,MEM_ETH_DEST,UDP_DATA ; ether, ip, udp headers
#else
;
; load everything.
;
ps2x 0,MEM_ETH_DEST,[buf] ; copy packet to board
#endif
xmit_frame [buf] ; out she goes
#if defined(LOG_MEM_CHKSUM)
pprintv "PKLEN ",[buf]
#endif
pret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; serial device I/O ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.cseg
#define putchar_serial pmovbi putc_b,0
#define putchar_net pmovbi putc_b,1
#define serial_binary pser_mode 1
#define serial_normal pser_mode 0
#define SER_TMO 2000 ; 1500
;+
; **-sertest-serial I/O testor.
;-
#define SD_ADDR buf
#define SD_LEN buf+2
#define SD_CHAR buf+4
#define SD_TEMP buf+6
#define SD_LAST buf+8
#define SD_TMO SD_CHAR
sertest:
ppushn putcok,1 ; save putchar enable state
pmovbi putcok,0xff ; putchar is now OK
pprint "HTTP/1.0 200\nContent-type: text/html\n\n"
putchar_serial ; switch putchar to serial port
serial_binary ; put serial port in binary mode
pmovwi SD_ADDR,[data_addr] ; get data address
paddwi SD_ADDR,10 ; GET /iuNN?xxxxxxxxxxx
; 01234567890
pmovwi SD_LEN,[data_len]
psubwi SD_LEN,10 ; bias length for GET /...? part
;
; note there is not a lot of checking on the format of the URL. the first
; occurrence of '=' is interpreted as the start of the stuff to transmit
; to the serial port, and that continues until either '&' or ' ' is
; encountered.
;
pmovwi SD_LAST,0 ; clear last character (both bytes!)
find_equal_next:
pr2s SD_CHAR,[SD_ADDR],1 ; scan for '=' in Ethernet packet
pincw SD_ADDR
pdecw SD_LEN
pcmpbi SD_CHAR,'='
pjumpeq serdev_next
pcmpwi SD_LEN,0
pjumplo serial_done
pjumpne find_equal_next
pjump serial_done
;
; send stuff after "=" to serial port with %XX expansion until ' '.
;
serdev_next:
pwdr ; reset hardware watchdog timer
pr2s SD_CHAR,[SD_ADDR],1 ; get next byte
pcmpbi SD_CHAR,' ' ; the end?
pjumpeq serial_done ; yes...we found a ' '
pcmpbi SD_CHAR,'+' ; map '+' -> ' '
pjumpne not_plus
pmovbi SD_CHAR,' '
pjump output_next_char
not_plus:
pcmpbi SD_CHAR,'%' ; check for HTTP quoting char
pjumpne output_next_char
;
; get next two chars from request.
;
pincw SD_ADDR ; skip '%'
pr2s SD_CHAR,[SD_ADDR],2 ; get XX of %XX
paddwi SD_ADDR,1 ; skip past it
psubwi SD_LEN,2 ; discount in length
;
; hex conversion of two chars in SD_CHAR, SD_CHAR+1.
;
pmovb SD_TEMP,SD_CHAR ; get first char
pandwi SD_TEMP,0xff
psubwi SD_TEMP,'0' ; convert to binary (maybe)
pcmpwi SD_TEMP,10 ; see if valid hex digit
pjumplo do_upper_digit ; yep - do upper one
pandwi SD_TEMP,0xf ; 1-6
paddwi SD_TEMP,9 ; turn 1-6 into 10-15
do_upper_digit:
pmovb SD_CHAR,SD_CHAR+1 ; get second digit
pandwi SD_CHAR,0xff ; isolate it
psubwi SD_CHAR,'0' ; convert to binary (maybe)
pcmpwi SD_CHAR,10 ; see if valid hex digit
pjumplo merge_digits ; yep - merge the two
pandwi SD_CHAR,0xf
paddwi SD_CHAR,9
merge_digits:
pshnw SD_TEMP,4 ; shift top bits into place
paddwi SD_CHAR,[SD_TEMP] ; SD_CHAR now has char
output_next_char:
pandwi SD_CHAR,0xff ; isolate it
pcmpwi SD_CHAR,0x5c ; back-slash?
pjumpeq skip_output ; yes...don't output
pcmpwi SD_LAST,0x5c ; last character a back-slash?
pjumpne output_char_now ; no...proceed as normal
pcmpwi SD_CHAR,'r' ; got a \r?
pjumpne not_CR ; no...skip ahead
pmovbi SD_CHAR,0x0d ; yes...change it to CR
not_CR:
pcmpwi SD_CHAR,'n' ; got \n?
pjumpne not_LF ; no...
pmovbi SD_CHAR,0x0a ; yes...change to LF
not_LF:
output_char_now:
pputcb SD_CHAR
skip_output:
pmovbi SD_LAST,[SD_CHAR] ; save last character
pincw SD_ADDR ; adjust pointer
pdecw SD_LEN ; see if we're done
pjumplo serial_done
pjumpne serdev_next
serial_done:
pmovwi SD_TMO,SER_TMO ; select the timeout period
putchar_net ; switch putchar back to 'net
pcall serdev_copynet ; copy serial port input to net
ppopn putcok,1 ; restore putchar enable state
pret
;
; loop reading chars from the serial buffer and writing to 'net until
; LF encountered (or timeout)
;
; SD_TMO (word) = timeout loop count.
;
.cseg
serdev_nextchar:
pser_getc SD_CHAR ; get a char into buf
pjumpeq serdev_empty ; no more chars!
pputcb SD_CHAR ; put char to the net
pcmpbi SD_CHAR,0x0a ; check for LF
pjumpeq serdev_copydone
serdev_copynet:
pmovwi SD_TEMP,[SD_TMO] ; reset timeout
pwdr ; reset hardware watchdog timer too
pjump serdev_nextchar ; go get another one
serdev_empty:
psubwi SD_TEMP,1 ; decrement timeout
pjumpne serdev_nextchar ; not yet timed out
serdev_copydone:
pret
#ifndef DUMMY_DATA
;
; Assert "Done", wait for "Start" to go low, then release "Done"
;
psignal_done: pcode_routine 0
sbi Done_PORT,Done_BIT ; "Done" => high
psignal_done_loop:
in r17,Start_PINS ; "Start" signal active?
bst r17,Start_BIT ; move "Start" bit to T-reg
brts psignal_done_loop ; loop back if still set
cbi Done_PORT,Done_BIT ; not set..."Done" => low
ret
#endif
;
; INT0/1 interrupt service routine
;
.cseg
int0_isr:
push r17 ; save working register
in r17,SREG ; save flags
push r17 ;
clr r17 ; disable INT0 and INT1
out GIMSK,r17 ;
in r17,MCUCR ; make edge sensitive interrupts
ori r17,0x0f ;
out MCUCR,r17 ;
pop r17 ; restore flags
out SREG,r17 ;
pop r17 ; restore working register
reti ; return from interrupt
Back
|
|
|