I've bought the C8051F34X Development Kit (which is available at Digikey for small money) because I want to play a bit with USB devices and testing the 8051 instruction set and architecture. The Blinky sample worked without problems with the IDE. Compiling larger programs is difficult, because the Keil compiler is an evaluation version, only, which limits the maximum program size. But there is SDCC support in the IDE. I tried it, but couldn't figure out how to define aliases for registers, so I installed a later version of the ASxxxx assembler, which was used by SDCC. This version doesn't work with the IDE, because the command line arguments are different and the required output file name and format is different (looks like the Silabs IDE can't use Intel Hex for the toolchain generated files). The editor in the IDE is not very good anyway, so I switched to my favorite editor Ultraedit and used a small build.cmd script for compiling the file:
C:\Programme\ASXV4PXX\ASXMAK\VC6\EXE\AS8051.EXE -slox blinky.asm C:\Programme\ASXV4PXX\ASXMAK\VC6\EXE\ASLINK.EXE -i blinky blinky xcopy /y C:\SiLabs\MCU\Examples\C8051F34x\Blinky\blinky.ihx "c:\Dokumente und Einstellungen\Frank\blinky.hex"
The IDE has a nice option "Download object file" in the Debug menu, where you can specify an Intel Hex file, which will be flashed to the device. This setup works, so finally I have mastered the usual development tool quirks.
The demo program samples the ADC, which is connected to the pin on the development board with a potentiometer. It sends the average of 16 values, sampled every millisecond, every 16 ms, with the P2.0 state (a button on the devboard) in bit 7. Received bytes from the serial port are written to P2 (only for P2.2 and P2.3, the other bits are overwritten with 1 to avoid problems with the open drain GPIOs). This is the source:
.include "c8051f340.inc"
GREEN_LED1 == P2.2 ; green LED1
GREEN_LED2 == P2.3 ; green LED2
.define TEMP "R2" ; temporary register
.define ADC_COUNT "R3" ; current number of accumulated ADC samples
.define ADC_SUM_L "R4" ; accumulated samples value, low byte
.define ADC_SUM_H "R5" ; accumulated samples value, high byte
.area CODE (ABS)
.org 0
reset: ljmp start ; reset vector
.org 0x02B ; interrupt vector for timer2
ajmp timer2
.org 0x100
start: anl PCA0MD, #0xff-0x40 ; clear watchdog enable bit
mov OSCICN, #0x83 ; set oscillator to full speed internal clock, 12 MHz
; setup 4x clock multiplier
mov CLKMUL, #0x00
mov CLKMUL, #0x80 ; enable clock multiplier
clr a ; wait a bit for initialization
djnz acc, .
orl CLKMUL, #0xc0 ; initialize clock multiplier
cinit: mov a, CLKMUL
jnb ACC.5, cinit ; wait for stabilization
mov CLKSEL, #3 ; select full 48 MHz for system clock and USB clock
; setup ports
orl P2MDOUT, #0x0c ; make LEDs pin output push-pull
orl P2MDIN, #0x0c ; make LEDs pin input mode digital
setb GREEN_LED1 ; turn on LED1
anl P1MDIN, #0xff-0x02 ; set P1.1 as an analog input
; setup ADC
mov ADC0CN, #0x00 ; ADC0 disabled, normal tracking
mov REF0CN, #0x08 ; use VDD as AD reference
mov AMX0P, #0x04 ; ADC0 positive input = P2.5
mov AMX0N, #0x1F ; ADC0 negative input = GND (single ended mode)
mov ADC0CF, #0xfc ; set SAR clock to minimum and left-justify results
setb AD0EN ; enable ADC0
mov ADC_COUNT, #0x10 ; initialize counter for ADC accumulation
; setup timer2 for an interrupt every millisecond
mov TMR2CN, #0x00 ; stop timer2; clear TF2; use SYSCLK/12 as timebase for T2XCLK
mov TMR2RLH, #0xf0 ; init reload values
mov TMR2RLL, #0x60
mov TMR2H, #0xff ; set to reload immediately
mov TMR2L, #0xff
setb ET2 ; enable timer2 interrupts
setb TR2 ; start timer2
; setup UART0
orl P0MDOUT, #0x10 ; enable UTX as push-pull output
mov XBR0, #0x01 ; enable UART on P0.4(TX) and P0.5(RX)
mov SCON0, #0x10 ; 8 bit, ignore stop bit, rx enabled, 9th bits are zeros, clear RI0 and TI0 bits
mov TH1, #0x30
mov TL1, #0x30
orl CKCON, #8 ; T1M = 1 (timer1 UART baud rate generator uses system clock)
mov TMOD, #0x20 ; timer 1 in 8-bit autoreload
setb TR1 ; start timer1
setb TI0 ; Indicate TX0 ready
mov XBR1, #0x40 ; enable Crossbar
setb EA ; enable global interrupts
ajmp . ; wait forever
; timer2 interrupt handler
timer2: clr TF2H ; clear timer2 interrupt flag
lcall sample
dec ADC_COUNT
mov a, ADC_COUNT
jnz testA ; skip ADC output, until 16 values accumulated
mov ADC_COUNT, #0x10 ; reinitialize counter for ADC accumulation
lcall avg ; get average ADC value in accu
anl a, #0xfe ; discard lsb
rr a
jb P2.0, notSet ; test button
orl a, #0x80 ; set msb, if button is pressed
notSet: lcall emit ; transfer to RS232
testA: lcall avail ; test if RS232 data is available
jnb ACC.0, iend
mov a, SBUF0 ; get byte
orl a, #0xf3 ; set 1 for all input pins
mov P2, a ; save in port2
iend: reti ; return from interrupt
; sends a byte to RS232 and waits until transfered
emit: mov SBUF0, a ; send byte
emit2: mov a, SCON0
jnb ACC.1, emit2 ; wait until transfered
clr SCON0+1 ; clear transmit bit
ret
; tests, if a byte is available from RS232
; return: accu=1, if available, 0 otherwise
avail: mov a, SCON0
anl a, #1
clr SCON0+0
ret
; sample one ADC value and accumulate it in r5/r6
sample: setb AD0BUSY ; start AD conversion
waitAD: mov a, AD0BUSY ; wait until AD conversion is finished
jb ACC.0, waitAD
mov a, ADC0H
add a, ADC_SUM_L
mov ADC_SUM_L, a
jnc s2
inc ADC_SUM_H
s2: ret
; get the average of 16 accumulated ADC values from ADC_SUM and reinitialize it
; return: accu=average
avg: mov TEMP, #4
shift: clr C ; clear carry
mov a, ADC_SUM_H ; shift ADC_COUNT_L/ADC_COUNT_H one bit right
rrc a
mov ADC_SUM_H, a
mov a, ADC_SUM_L
rrc a
mov ADC_SUM_L, a
djnz TEMP, shift ; shift four times = divide by 16
mov a, ADC_SUM_L ; return result in accu
mov ADC_SUM_L, #0 ; reinitialize ADC_SUM
mov ADC_SUM_H, #0
ret
I like the instruction set, the bit-set and bit-clear operations are very handy. And many instructions of the C8051F34X chips are executed in one ow two clock cycles, which means you have up to 48 MIPS speed (but with some Silabs parts the system clock is limited to 25 MIPS). No external clock is required, the internal 12 MHz clock can be divided and multiplied up to 48 MHz, if the accuracy from 11.82 MHz to 12.18 MHz is sufficient.
Some time ago there was a full version of LabView in a German computer magazin DVD for personal use. I've seen some expensive PCI cards for integration with LabView, with ADC, GPIOs etc., but for many tasks the multiplexed ADCs and many GPIOs of one Silabs chip, wired with RS232 to the PC, is all you need to control and measure some external hardware, so this was a good test for me to try out LabView, because the graphical, schematic-like programming environment looks very user friendly and easier for electronic engineers than writing some text programs. And after reading some tutorials, finally I managed to create my first Virtual Instrument. Once you have learned the basics, it is very easy to create your own instruments. To do all the minor format conversions, numerical constants etc. with symbols needs getting used to, but the same is true for writing programs with a text editor. I don't know how it scales for bigger projects (but you can encapsulate functionalities inside user defined symbols, which should help), but this small project looks very nice and clear, and it is fun to work with.
This is the GUI of the virtual instrument:
The pointer of the round instrument is updated very fast when tuning the potentiometer and there is nearly no CPU usage.
The flow diagram for this instrument:
You can download the instrument. Maybe I'll buy the commercial version and use it more often instead of writing text programs. For virtual instruments it is more natural to just draw it instead of writing lots of text for implementing it.