## Python Code for the Si514 Programmable Xtal Oscillator

I’ve put together a number of project using the Si5351. However, I’ve got to admit that with its surface mount package and need for an external crystal, it’s kind of a hassle to work with. For that reason, I purchased a few Si514 to see how they compare.

The Si514 is a single frequency user-programmable I2C oscillator, similar to the Si570. It covers the frequency range from 100 kHz to 170 MHz (or in some cases, 250 MHz). Some things I particularly like about it are the integrated crystal and the availability of a package whose surface mount pad spacing is compatible with stripboard. It doesn’t have as wide a frequency range as the Si570 or Si5351 but my projects typically involve frequencies below 30 MHz so that’s ok. And being able to easily order them through DigiKey is another plus.

To try it out I mounted the chip on a small piece of stripboard to serve as a carrier and connected it to an AdaFruit Trinket programmed to act as a USB-to-I2C interface. I modified the Si5351 Python code I had previously written to work with the Si514 and, after fixing a few bugs, had a working clock.

But Where’s the Software?

Like the code I previously wrote for the Si5351, the Python code for the Si514 is available in my GitHub repo. It was written to work with the previously mentioned I2C Stick software but should be easy to modify for use with another I2C interface.

To use the software, pull the files from the repo, place them in a a convenient location, and execute the command:

```\$ ./py514 -f [frequency_in_Hz]
```

If you don’t specify the frequency it will use a default of 10 MHz.

When I started working on this I was a little surprised I didn’t see more projects out there using the Si514. Considering its small size and availability, I think it’s time to start updating some of my other projects to use it.

## From Binary To BCD…And Back Again (Part 2)

In the previous post I described the algorithm for converting a binary number to binary-coded-decimal (BCD). In this post I’ll describe an algorithm for doing the reverse; converting a BCD number to binary.

The algorithm is based on repeated division and is best explained by example. Begin with a decimal number and divide by 2. The remainder becomes the high order bit in the binary number, the quotient carries forward. Repeat the process for the required number of bits. The table below shows an example for the decimal number 204.

Quotient Remainder Binary Number
204
102 0 0
51 0 00
25 1 100
12 1 1100
6 0 01100
3 0 001100
1 1 1001100
0 1 11001100

For a BCD number start at the most significant digit (MSD). Shift right to divide by two, moving the least-significant bit (LSB) into the carry. Since the remainder after dividing by 2 is the LSB, add 10 to the next significant digit if the carry is set and repeat. Limiting ourselves to number in the range from 0 – 255 (8 bits), in PIC assembly language with 8-bit registers this would look like:

```; Load the registers
movlw   d'2             ; load BCD digit 2
movwf   DEC2_REGISTER   ;
movlw   d'0             ; load BCD digit 1
movwf   DEC1_REGISTER   ;
movsw   d'4             ; load BCD digit 0
movwf   DEC0_REGISTER   ;
clrf    BIN_REGISTER    ; clear the binary register
movlw   d'8             ; load the counter
movwf   COUNTER         ;

; Perform the division
DIVIDE
DIGIT2
bcf     status, c       ; clear the carry
rrf     DEC2_REGISTER,1 ; rotate digit to the right, putting LSB into carry
btfss   status, c       ; skip if there is a remainder
goto    DIGIT1          ; jump to the next digit
movf    DEC1_REGISTER,0 ; get the next digit
movwf   DEC1_REGISTER   ; move it back

DIGIT1
bcf     status, c       ; clear the carry
rrf     DEC1_REGISTER,1 ; rotate digit to the right, putting LSB into carry
btfss   status, c       ; skip if there is a remainder
goto    DIGIT0          ; jump to the next digit
movwf   DEC0_REGISTER,0 ; get the next digit
movwf   DEC0_REGISTER   ; move it back

DIGIT0
bcf     status, c       ; clear the carry
rrf     DEC0_REGISTER,1 ; rotate the digit to the right, putting LSB into carry

; Update the counter.
BINARY
rrf     BIN_REGISTER,1  ; rotate remainder into the binary register

decfsz  COUNTER, 1      ; decrement the counter
goto    DIVIDE          ; back to the top of the loop
...
```

Note that as written, this will not work with 4-bit BCD numbers because adding 10 to a digit could potentially move it outside the range of 0 – 15. However, like the Binary to BCD algorithm, there is an optimization to be performed that will allow it work with 4-digit BCD.

Instead of checking the carry flag and adding 10 before shifting the next digit, perform all the shifts up front. As mentioned previously, the remainder of division by 2 is simply the register’s least-significant bit (LSB), which, after the shift, becomes the MSB of the next digit’s register. After performing the shifts, check each register’s MSB. If it’s set there was a carry. Clear the MSB and add 5 (because we’ve already done the division via the shift, it’s 5 instead of 10). The code becomes:

```; Load the registers
movlw   d'2             ; load decimal digit 2
movwf   DEC2_REGISTER   ;
movlw   d'0             ; load decimal digit 1
movwf   DEC1_REGISTER   ;
movsw   d'4             ; load decimal digit 0
movwf   DEC2_REGISTER   ;
clrf    BIN_REGISTER    ; clear the binary register
movlw   d'8             ; load the counter
movwf   COUNTER         ;

; Perform the division
; Shift the registers
DIVIDE
bcf     status,c        ; clear the carry
rrf     DEC2_REGISTER,1 ; rotate digit to the right
rrf     DEC1_REGISTER,1 ; rotate digit to the right
rrf     DEC0_REGISTER,1 ; rotate digit to the right
rrf     BIN_REGISTER,1  ; rotate binary number to the right

; Clear the MSB and add 5 if MSB is set.
; You don't have to check digit 2 because it will always have
; been zero shifted into its MSB.
DIGIT1
movwf   DEC1_REGISTER,0 ; move digit to the acc
btfss   DEC1_REGISTER,7 ; skip if MSB is set
goto    DIGIT0          ; jump to the next digit
andlw   h'7f            ; clear the MSB
movwf   DEC1_REGISTER   ; move the digit back
DIGIT0
movwf   DEC0_REGISTER,0 ; move digit to the acc
btfss   DEC0_REGISTER,7 ; skip if MSB is set
goto    BINARY
andlw   h'7f            ; clear the MSB
movwf   DEC0_REGISTER   ; move the digit back

; Update the counter.
BINARY
decfsz  COUNTER, 1      ; decrement the counter
goto    DIVIDE          ; back to the top of the loop
...
```

As written this code is for 8 bit registers. However, the algorithm is compatible with 4-bit BCD since the value in any of the 4-bit registers will always be in the range from 0 – 15.

If you think about it, this algorithm nicely complements the binary-to-BCD algorithm. For binary-to-BCD you subtract 5 before setting a register’s MSB. For this case, you clear the MSB and add 5. Makes it easy to remember too.

## From Binary To BCD…And Back Again (Part 1)

Binary coded decimal (BCD) is a way of representing numbers in a computer as decimal rather than binary digits. Most commonly 4 bits are used to represent each digit since 4 bits can contain the numbers 0-9 and it allows two digits to be packed into a single byte.

For those who still enjoy assembly language programming the conversion from binary to BCD is not an uncommon operation. The algorithm is well known and is usually referred to as Add 3 – Shift.

While I’ve seen the algorithm described in many places, I haven’t yet run across an explanation of how it’s derived. Turns out it’s not very difficult.

The Basic Algorithm

The best description I’ve run across for this algorithm is “you calculate the value of the binary number but do the calculations using decimal values.”

You may recall that a binary number can be represented as a polynomial in the form:

$\Large y = {p}_{1}{x}^{n} + {p}_{2}{x}^{n-1} + ... + {p}_{n}{x} + {p}_{n-1}$

In PIC assembly language, for an 8 bit value and a binary accumulator, the code to calculate this value woud look like:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    ACC_REGISTER    ; clear the result accumulator
movlw   d'8             ; load the counter
movwf   COUNTER         ;

; Rotate bits
ROTATE
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     ACC_REGISTER, 1 ; rotate carry into accumulator low bit

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

For a decimal accumulator, the code is similar but you need a separate register for each digit. You shift the individual digits to the left to multiply by 2, then check for carrys. If a digit it greater than 10, subtract 10 and add 1 to the next higher digit.

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'8             ; load the counter

; Rotate bits
ROTATE
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

DIGIT0
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfss   status, c       ; skip if >= 10
goto    DIGIT1          ; jump to next digit
incf    DEC1_REGISTER   ; increment next digit
movwf   DEC0_REGISTER   ; move value back to the register

DIGIT1
movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfss   status, c       ; skip if >= 10
goto    DIGIT2          ; jump to the next digit
incf    DEC2_REGISTER   ; increment next digit
movwf   DEC1_REGISTER   ; move value back to the register

DIGIT2
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

You could stop here but there are some optimizations that can be done. For example, we know that any accumulator holding a digit of 5 or greater will have a carry once it’s multiplied by 2. To take advantage of this, check the value of each digit. If it’s 5 or greater set that register’s most significant bit (MSB). When you perform the shift, that flag will rotate into the carry and the carry flag from the previous digit will rotate into the least significant bit (LSB). This same trick can be used to shift the MSB of the original binary value into the LSB of the lowest decimal digit. Making this optimization, the code becomes:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'8             ; load the counter

; Set the flag bits
ROTATE
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfsc   status, c       ; skip if < 5
bsf     DEC0_REGISTER, 7; set the MSB

movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfsc   status, c       ; skip if < 5
bsf     DEC1_REGISTER, 7; set the MSB

; Rotate the bits
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

; Check for digits > 10
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfsc   status, c       ; skip if < 10
movwf   DEC0_REGISTER   ; move value back to the register

movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfsc   status, c       ; skip if < 10
movwf   DEC1_REGISTER   ; move value back to the register

; Update the counter
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

The next optimization is to combine the digit adjustment with setting the flag. We already said any register with a value of 5 or greater will have a carry once it’s multiplied by 2. It will also have 10 subtracted after the shift. So we’ll combine the two and, rather than subtract 10 after the shift, we’ll subtract 5 before the shift. The code becomes:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'7             ; load the counter

; Set the flag bits
ROTATE
DIGIT0
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfss   status, c       ; skip if >= 5
goto    DIGIT1          ; jump to next digit
iorlw   d'128           ; set the MSB
movwf   DEC0_REGISTER   ; move value back to register

DIGIT1
movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfss   status, c       ; skip if >= 5
goto    SHIFT_BITS      ; jump to next digit
iorlw   d'128           ; set the MSB
movwf   DEC1_REGISTER   ; move value back to register

; Shift the bits
SHIFT_BITS
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

; Update the counter
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

So after all that, where does Add 3 – Shift come from? Each time through the loop, if a register’s value is greather than 5, 5 is subtracted and the MSB set. Setting the MSB is the same as adding 128 so we’re really adding 123.

This is for an 8 bit register. But as I mentioned at the beginning, BCD is normally done using 4 bit registers. In that case, setting the MSB is the same as adding 8. So add 8, subtract 5 (in other words, add 3) and shift.

rtl-sdr.com

A blog about RTL-SDR (RTL2832) and cheap software defined radio

DuWayne's Place

Computers, Electronics, and Amateur Radio from KC3XM

QRP HomeBuilder - QRPHB -

Computers, Electronics, and Amateur Radio from KC3XM

Open Emitter

Computers, Electronics, and Amateur Radio from KC3XM

Ripples in the Ether

Emanations from Amateur Radio Station NT7S

m0xpd's 'Shack Nasties'

Computers, Electronics, and Amateur Radio from KC3XM