Digital Storage
Scope
Using
Cygnal - C8051F000
Mixed Signal
Processor
Part I
Sudhir
Gupta
Summary
It is the first part of a two-part article. The article discusses the mixed signal processor C8051F000 from Cygnal Inc. The article focuses on its application as a digital storage oscilloscope in combination with a PC. The C8051F000 microcontroller is used as a programmable analogue front end, while the PC acts as the storage and display unit. This part of the article covers the salient features of C8051F000 processor pertinent to a digital oscilloscope. A schematic diagram and an assembly language source file is provided.
Introduction
C8051F000 processor from Cygnal is a powerful mixed signal
processor. It contains a powerful analogue front-end, which makes it quite
suitable for a relatively fast (100 KHz) data acquisition. It does not contain an access to external
(byte or word) memory, but its on-board UART can be operated at a high baud rate
(115,200 baud) and thus it is possible to acquire data at about 5000
samples/sec and transmit to PC in a continuous fashion. PC in turn can then
store the measurements or display these in real time. The complete storage
system can then be divided into two subsystems.
·
Analogue Front-End
·
Conversion Control and Data Transfer
·
Storage and Display
C8051F000 uC contains a complete and programmable analogue
front-end.
·
8-channel analogue multiplexer
·
Programmable gain amplifier
·
12-bit A/D converter
·
Buried zener reference
The multiplexer can be programmed to act as a 4-channel
differential or as an 8-channel single ended multiplexer. The differential
configuration is useful, when interfacing to bridge circuit (as in pressure
transducer, strain gauges) as well as thermocouples. The multiplexer output is
fed to a programmable gain amplifier (PGA). The PGA can accept the differential
input and generates a single ended output. The PGA gain can be changed on the
fly and can be set to any one of these values: 0.5, 1, 2, 4, 8, and 16.
The output of PGA is fed to a 12-bit A/D converter with a
maximum conversion rate of about 100,000 samples/sec. The conversion rate can
be controlled through software. The A/D conversion cycle can be controlled
through Timer0, Timer3, External Signal, or internal software (convert on
demand). In this application Timer3 is used to control the A/D conversion
cycle, permitting the signal to be sampled at a fixed rate. The specs for
buried zener reference calls for a typical value of 30 ppm/°C, with a maximum of 200 ppm/°C. The
upper limit of reference drift is somewhat high for a decent 12-bit device and
for better results an external reference should be used.
Timer3 is always set to operate in auto-reload mode,
providing a pulse when the 16-bit counter rolls over from FFFF to 0000. Every
time the timer overflows, the 16-bit count value from Timer3 RL register is
reloaded into Timer3 and another time period begins. The reload count can range
from 0000 to nearly FFFF, making a count period ranging from 10000 clock cycles
to just 1 cycle. The clock source can be either the SYSCLK or SYSCLK/12,
extending the time period over a very wide range.
Once the A/D conversion is complete, the 16 bit data is read
from the A/D output register and transferred through serial port to the remote
PC. The serial port can be operated at a high baud rate 115,200 for a Xtal
frequency of 11.0582 MHz. At this rate approximately 10,000 bytes can be
transferred per second. Converting binary to ASCII will double the transmission
time and the net A/D conversion rate will be reduced by 50%. Thus in order to
maintain the maximum possible conversion rate, raw binary data will be
transferred to remote PC. Even though the UART exists internally, Port I/O
crossbar decoder controls the UART connection to external (& physical) pins
of microcontroller. The decoder is very easily configured through crossbar
registers.
A windows based program communicates with the DSS system. The
PC (software) completely controls the operation of digital storage scope. It
accepts the user input and sends commands to C8051F000 microcontroller to
configure the A/D converter. Once the configuration is complete, it sends the
command to start the conversion and gets ready to receive the data. This data
can be sent to a file for storage or displayed in real time. Better flexibility
is obtained, if the entire data is first stored and then retrieved for display.
The PC software accepts the following input from user:
·
Analogue Channel #
·
Single Ended or Differential Channel Configuration
·
PGA Gain
·
Sampling Frequency KHz
·
Number of Samples
·
File Name to Store Data
This information is sent to DSS system, which configures the
ADC and transfers the conversion results to PC.
Once a scan is complete, the user is presented with a choice
of displaying a stored data file, taking another scan, or simply terminating
the program.
Figure 1 shows the schematic diagram for the digital storage
scope. The system is fairly compact, yet powerful enough for the purpose of
implementing a digital storage scope. The following components form the heart
of the DSS system:
·
C8051F000 Microcontroller
·
RS-232 Interface IC
·
Voltage Regulator
The DSS software carries out the following tasks:
The crossbar (the internal digital switching system)
controls the physical connections of internal digital peripherals to external
uC pins. The crossbar needs to be configured before any digital peripherals can
be used. The initialization program selects the high-speed UART and assigns it
to P0.6 and P0.7 pins. It then configures the serial port to operate at 115,200
baud. On power up, C8051F000 uC uses its internal oscillator as the default.
The initialization program changes it to use the external 11.0592 MHz crystal.
Once this initialization is complete F000 signs on and is ready to receive the
command from remote PC
The A/D scan is initiated on receiving a scan command from
the PC. The scan command specifies operational details for every scan. The
command consists of a single line of ASCII characters. The format for scan
command is:
$ NNNN TTTT
M C G 0dh 0ah
·
$: Character indicating the start of command line
·
NNNN: Four ASCII characters representing the number of
conversions - 1 (hex value)
·
TTTT: Four ASCII characters representing the Timer Ticks
(Timer3). This dictates the sampling interval
·
M: An ASCII character representing the analogue multiplexer
input configuration (differential/single ended)
·
C: An ASCII character representing the analogue input
channel number
·
G: An ASCII character representing the PGA gain setting
·
0dh 0ah: ASCII characters for CR/LF
The number of characters read is limited to 16. The data is
transferred as hex values. There is no error checking in the command
processing, so any improper value can through the DSS system operation in
disarray. This is where the intelligence of PC software comes in. This
interface software ensures that only appropriate values are sent to the DSS
system.
;=============================================================================
; Micro
Control Journal
;
Sudhir Gupta
;
Copyright 2000
;
;=============================================================================
;
Equates
;=============================================================================
$MOD8F000
;=============================================================================
; RAM Variables
;=============================================================================
BSEG
org 0h
adc_done: DBIT
1 ; Flag to indicate adc status
err_flag: DBIT
1 ; Flag to indicate the error
condition.
DSEG at 30h
mux_config: ds 1 ; Multiplexer configuration
chan_num: ds 1 ;
User desired channel
adc_config: ds 1 ; ADC configuration PGA gain & CLK rate
adc_control: ds 1 ; ADC control data
l_time: ds 1 ; Timer reload values low byte
h_time: ds 1 ; Timer reload values high byte
l_num: ds 1 ;
Number of conversions low byte
h_num: ds 1 ; Number of conversions high byte
; Actual # of conversions is one more than this #.
adc_data_l: ds 1 ; Temporary location for ADC result
adc_data_h: ds 1
counter: ds 1 ;
variable for counter
buf_start: ds 16 ;
Start of string buffer
;
============================================================================
; Indirect address space
;
============================================================================
ISEG at 80h
STACK_TOP:
DS 1 ;
Beginning of hardware stack
;=============================================================================
; Reset And Interrupt Vector Table
;=============================================================================
CSEG
org 00h
ljmp Main ; RESET vector
org 7bh
ljmp adc_isr ; ADC0 end-of-conversion interrupt
;=============================================================================
; Main Program
;=============================================================================
org 0B3h
Main:
mov SP, #STACK_TOP ; init stack pointer to end of allocated RAM
acall initialize ;
Initialize the system
acall init_serial ; initialize serial port
Again:
acall sign_on
acall get_cmd
acall set_timer_ticks
acall set_adc ; set data acquisition system
acall start_adc_scan ; Start the scan
setb EA ; Enable GLOBAL interrupts
acall send_header ; Send a header announcement
wait_for_conversion:
jnb adc_done,wait_for_conversion
; Wait for ADC done flag to be set
mov a,adc_data_h ; Read high byte of adc conversion
acall tx_char ; transmit it
mov a,adc_data_l ; Read low byte of adc conversion
acall tx_char ; Transmit it
clr adc_done ;
Reset the flag
; Check if required number of conversions
are done
mov a,#0ffh
dec l_num
cjne a,
l_num, wait_2
dec h_num
wait_2:
cjne a,h_num,not_done
acall send_trailer ; Send completion msg
jmp again ;
This scan is done. Do another
not_done:
jmp wait_for_conversion
; Wait for another adc conversion
jmp $ ;
If reached here loop forever
;
============================================================================
; Interrupt Vectors
;
============================================================================
; ============================================================================
; adc_isr
;
============================================================================
;
; This
ISR is reached on end of adc conversion.
; The
two bytes of ADC data are stored in adc_data_h & adc_data_l
adc_isr:
clr ADCINT
; clear ADC interrupt
flag
jbc adc_done,adc_isr_2 ; If last value not read then there is
setb err_flag ;
a problem. Set an error flag
jmp adc_isr_9 ;
Exit without updating
adc_isr_2: ; OK. Last data was transmitted.
mov adc_data_l,
ADC0L ; LSB of ADC result
mov adc_data_h,
ADC0H ; MSB of ADC result
setb adc_done
adc_isr_9:
reti
;=============================================================================
; Procedures
;=============================================================================
;=============================================================================
; initialize
;=============================================================================
; It initializes
the uC peripherals
; WDT isdisabled
; XBAR is enabled with UART
; External Xtal oscillator
; Variables adc_control initialized and
adc_done bit reset.
initialize:
clr EA ; Disable interrupts
mov WDTCN,
#0deh ; disable watchdog timer
mov WDTCN,
#0adh
mov XBR0,#07h ; Enable SMBus,SPI, UART
; Tx at P0.6 &
Rx at P0.7
; Set Crossbar
mov XBR1,#00h ; Disable access to sysclk,T1,T2,INT0
& INT1
mov XBR2,
#40h ; Enable crossbar & weak
pull-ups
; Set Oscillator
mov OSCXCN, #01100101b
; Sysclk = Xtal osc (div by 1)
;
11.0592MHz Xtal
initialize_2:
mov a,
OSCXCN ; Read OSCXCN register
rl a ; Cy = XTLVLD (bit 7 of OSCXCN)
jnc initialize_2 ;
Wait till XTLVLD bit is set indicating
; that osc is
stable
mov OSCICN,
#88h ; Select external osc
mov adc_control,#00000100b
; Default ADC control data
; Do not enable
ADC
; Continuous
tracking mode
; ADC conversion
initiated by Timer3
; Right-justified
data
clr adc_done ; Reset ADC done flag
clr err_flag ; Reset error flag
ret
;
----------------------------------------------------------------------------
;=============================================================================
; init_serial
;=============================================================================
; It
initializes the serial port
; Xtal
11.0592MHz (sysclk)
; Use
Timer1
; Baud
115,200 8-N-1
init_serial:
mov SCON,#50h ;
Mode 1, 8 bit, enable RX
mov TMOD,
#20h ;
Use Timer1 in mode 2 (8 bit auto-reload)
mov TH1,#0f9h ;
Reload value for 115.2kbps @ 11.0592MHz sysclk
setb TR1 ; Start Timer1
anl CKCON,#00001111b ; Reset Timer1 control bits
orl CKCON,#00010000b ; Use sysclk as CLK.
; Do not upset other bits in CKCON reg.
orl PCON,#80h ;
Set bit (SMOD = 1)
setb TI ;
TI = 1. Force TX to be ready
ret
;
----------------------------------------------------------------------------
;=============================================================================
; sign_on
;=============================================================================
; It
sends a sign_on message to PC.
; This
routine could be optimized by using another(new) send_msg routine.
sign_on:
push acc ;
Save acc
push dph ;
Save data pointer
push dpl
mov dptr,#sign_msg ; Initialize pointer
sign_on_2:
clr a
movc a,@a+dptr ;
read char
cjne a,#00h,sign_on_3
; If not a NULL char then send it
jmp sign_on_9 ;
It is a NULL char. Msg sent exit.
sign_on_3:
acall tx_char ;
Transmit a char
inc dptr ;
Bump up the ptr
jmp sign_on_2 ;
Check and send another char
sign_on_9:
pop dpl ;
Retrieve registers
pop dph
pop acc
ret
sign_msg:
db 0dh,0ah
db ' MC Journal',0dh,0ah
db 'Digital
Storage Scope',0dh,0ah
db ' using',0dh,0ah
db 'Cygnal
C8051F00 uC',0dh,0ah
db 0dh,0ah,00h
;
----------------------------------------------------------------------------
;=============================================================================
; send_header
;=============================================================================
; It
sends a header indicating the start of data dump
; This
routine could be optimized by using another(new) send_msg routine.
send_header:
push acc ;
Save acc
push dph ;
Save data pointer
push dpl
mov dptr,#header_msg ; Initialize pointer
send_header_2:
clr a
movc a,@a+dptr ;
read char
cjne a,#00h,send_header_3
; If not a NULL char then send it
jmp send_header_9 ; It is a NULL char. Msg sent exit.
send_header_3:
acall tx_char ;
Transmit a char
inc dptr ;
Bump up the ptr
jmp send_header_2 ; Check and send another char
send_header_9:
pop dpl ;
Retrieve registers
pop dph
pop acc
ret
header_msg:
db 0dh,0ah
db 'MCJ-DSS>
'
db 0dh,0ah,00h
;
----------------------------------------------------------------------------
;=============================================================================
; send_trailer
;=============================================================================
; It
sends a trailer indicating the end of data dump
; This
routine could be optimized by using another(new) send_msg routine.
send_trailer:
push acc ;
Save acc
push dph ;
Save data pointer
push dpl
mov dptr,#trailer_msg;
Initialize pointer
send_trailer_2:
clr a
movc a,@a+dptr ;
read char
cjne a,#00h,send_trailer_3
; If not a NULL char then send it
jmp send_trailer_9 ; It is a NULL char. Msg sent exit.
send_trailer_3:
acall tx_char ;
Transmit a char
inc dptr ;
Bump up the ptr
jmp send_trailer_2 ; Check and send another char
send_trailer_9:
pop dpl ;
Retrieve registers
pop dph
pop acc
ret
trailer_msg:
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0dh,0ah,00h
;
----------------------------------------------------------------------------
;=============================================================================
; set_timer_ticks
;=============================================================================
; It
sets Timer3 to operate in 16-bit auto-reload mode.
; Timer
overflow value is supplied by PC to set the conversion rate
; Using
SYSCLK as its time base.
;
Timer3 is not yet activated.
;
Timer3 interrupt is disabled.
set_timer_ticks:
anl EIE2,
#11111110b ; Disable Timer3 interrupts
mov TMR3CN,
#02h ; stop Timer, clear
overflow flag TF3
; Set SYSCLK as timebase
mov TMR3RLH,
h_time ; init reload values
mov TMR3RLL,
l_time
mov TMR3H,
#0ffh ; Clear it to give some
time
mov TMR3L,
#80h ; before an overflow occurs
ret
;
----------------------------------------------------------------------------
;=============================================================================
; set_ADC
;=============================================================================
; It
sets the following ADC peripherals:
; Mux configuration (differential or single
ended)
; Multiplexer channel
; PGA gain
; ADC conversion clock
set_ADC:
clr ADCEN
; disable ADC
mov REF0CN,
#00000011b
; Disable temp sensor
; Activate ref and ref buffer
mov AMX0CF,
mux_config
; Set multiplexer configuration
mov AMX0SL,
chan_num
; Selectet user desired channel
mov ADC0CF,
adc_config
; Set the gain & clk rate
mov ADC0CN,
adc_control
; Default set up
; ADC not yet enabled
; Continuous tracking mode
; ADC conversion initiated by Timer3
; Right-justified data
ret
;-----------------------------------------------------------------------------
;=============================================================================
; Start_Timer
;=============================================================================
; It
simply activates Timer3 which is held off.
Start_Timer:
orl TMR3CN,
#00000100b ; Set TR3 the timer run
bit.
ret
;
----------------------------------------------------------------------------
;=============================================================================
; start_adc_scan
;=============================================================================
; It
enables the ADC and ADC interrupts.
; The data
is read through adc interrupt service routine adc_isr
start_adc_scan:
setb ADCEN ; Enable ADC
orl EIE2, #00000010b ; Enable ADC end of conversion interrupt
ret
;-----------------------------------------------------------------------------
;=============================================================================
; get_cmd
;=============================================================================
; It
reads a line of command and processes it.
; There
is no error checking in command processing.
;
Modifies register r1.
get_cmd:
acall get_line ;
Read a line
mov r1,#buf_start ; Initialize ptr
; Read number of number of conversions
acall scan_byte ;
Read high byte from buffer. acc = byte
mov h_num,a ; Store high byte
acall scan_byte ;
Read a low byte from buffer.
mov l_num,a ; Update variable
; Read Timer3 ticks
acall scan_byte ;
Read high byte from buffer. acc = byte
mov h_time,a ;
Update variable
acall scan_byte ;
Read low byte from buffer. acc = byte
mov l_time,a ;
Update variable
; Read multiplexer configuration
acall scan_hex ;
Read a hex digit from buffer. acc = digit
mov mux_config,a ; Update variable
; Read channel number to be set
acall scan_hex ;
Read a hex digit from buffer. acc = digit
mov chan_num,a ;
Update variable
; Read ADC configuration byte PGA gain
& CLK rate
acall scan_hex ;
Read a hex digit from buffer. acc = digit
mov adc_config,a ; Update variable
; Read ADC control data
; Not implemented due to concern for
improper operation.
ret
;
----------------------------------------------------------------------------
;=============================================================================
; scan_byte
;=============================================================================
; It
reads 2 ASCII characters from buffer
; converts these to hex digits
; packs these in acc and returns
;
ENTRY:
; The register r1 is pointing to buffer
; EXIT:
; The ptr r1 is incremented by two
; acc.a and acc.b are modified
scan_byte:
mov a,@r1
inc r1 ;
bump up ptr
anl a,
#7Fh ; Mask MSB
clr c
subb a,
#'A' ; char = char - 'A'
jnc scan_byte_2 ; If there is no borrow then it is >=A
add a,#7h ;
It is <A Add 7 to it
scan_byte_2:
add a,#0Ah ; Add 10d more to it.
swap a ;
Move to high nibble
mov b,a ;
store in acc b
mov a,@r1
inc r1 ;
bump up ptr
anl a,
#7Fh ; Mask MSB
clr c
subb a,#'A' ; char = char - 'A'
jnc scan_byte_3 ; If there is no borrow then it is >=A
add a,#7h ;
It is <A. Add 7 to it
scan_byte_3:
add a,#0Ah ; Add 10d more to it.
orl a,b ;
Pack into a
ret
;
----------------------------------------------------------------------------
;=============================================================================
; scan_hex
;=============================================================================
; It
reads an ASCII character from buffer
; converts it to a hex digit
; puts it in acc and returns
;
;
ENTRY:
; Register r1 is pointing to buffer
; EXIT:
; register ptr r1 is incremented by one
; acc.a is modified
scan_hex:
mov a,@r1
inc r1 ;
bump up ptr
anl a,
#7Fh ; Mask MSB
clr c
subb a,#'A' ; char = char - 'A'
jnc scan_hex_2 ;
If there is no borrow then it is >=A
add a,#7h ;
It is <A Add 7 to it
scan_hex_2:
add a,#0Ah ; Add 10d more to it.
ret
;
----------------------------------------------------------------------------
;=============================================================================
; get_line
;=============================================================================
; It
reads a line of ascii charcters.
; The
line is terminated by 0Dh char, or
; When
16 characters are received.
;
Register r1 is being modified.
; This
routine is called only by get_cmd routine.
get_line:
mov r1,#buf_start ; Initialize ptr
mov counter,#10h ; Initialize character counter
get_line_1:
acall get_char ;
Get a char
cjne a,#'$',
get_line_1
; wait till "$" char is received as the header.
get_line_2:
acall get_char ;
Get a char
cjne a,
#0Dh, get_line_3
; Is it terminating character?
jmp get_line_9 ;
Yes it is exit.
get_line_3: ; No it is not a terminating char
anl a,#7fh ; Limit to 7-bit ASCII
mov @r1,a ;
Store the character
inc r1 ;
Bump up the ptr
djnz counter,
get_line_2
get_line_9:
ret
;
----------------------------------------------------------------------------
;=============================================================================
; get_byte
;=============================================================================
; It
receives a byte (2 hex chars). There is no error checking
get_byte:
acall get_hex ; Get a hex char
swap a ;
Move digit to MS nibble
mov b,a ;
Save it in acc b
acall get_hex
orl a,b ; Pack both digits
ret
;
----------------------------------------------------------------------------
;=============================================================================
; get_hex
;=============================================================================
; It
recives a hex char. There is no error checking
; Char
is assumed to be in upper case (A-F).
get_hex:
acall get_char ;
Get a char
anl a,
#7Fh ; Mask MSB
clr c
subb a,#'A' ; char = char - 'A'
jnc get_hex_2 ;
If there is no borrow then it is >=A
add a,#7h ;
Else add 7 to to it
get_hex_2:
add a,#0Ah ; Add 10d more to it.
ret
;
----------------------------------------------------------------------------
;=============================================================================
; get_char
;=============================================================================
; It
waits for a char to be received and returns with char in acc
get_char:
jnb RI,
get_char ; Wait for char received
flag to be set
clr RI ;
Clear the flag for next time
mov a,
SBUF ; Read char
ret
;
----------------------------------------------------------------------------
;=============================================================================
; tx_char
;=============================================================================
; It
waits for transmitter to be empty and transmits the char stored in acc.
tx_char:
jnb TI,
tx_char ; Wait for buffer empty flag to be
set
clr TI ;
Clear the flag for next time
mov SBUF,
a ; Send char
ret
;
----------------------------------------------------------------------------
;=============================================================================
;=============================================================================
; End of file.
;=============================================================================
;=============================================================================
END