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

 

Analogue Front-End

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.

 

Conversion Control and Data Transfer

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.

 

Storage and Display

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.

 

 


 


Text Box: Figure 1 Schematic diagram of C8051F00 uC in a digital storage scope

DSS System

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:

 

Initialization

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

 

Scan

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.

 

Assembly Language Source

;=============================================================================

; 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