Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Update: Per the original author(Jerry) a set of reserved bits in reg 183 should be set to 0x12 per a note in AN619 pg 61. Also added some comments.

The Holy Grail

After much gnashing of teeth, I have a working driver for the Adafruit Silicon Labs si5351 clock generator break out board. This board has three clock outputs that can generate a 8KHz to 160MHz square wave.

A future upgrade will include code to optionally generate I(0 deg) and Q(90 deg) signals on CLK0 and CLK1 to support SDR processing by Quisk.

#! /usr/bin/perl # hipisi5351.pl - si5351 Test Driver for Perl HiPi GPIO package # # James M. Lynes Jr. - KE4MIQ # Created: August 25, 2019 # Last Modified: 2/14/2020 - Add the setfreq subroutine # 3/02/2020 - Set output levels to 8ma per ZL2CTM # 3/04/2020 - Fix typos/compile errors # 3/06/2020 - Additional comments on register definitio +ns # 6/14/2020 - Begin port to HiPi GPIO package # 6/15/2020 - Add HiPi I2C syntax changes # 6/16/2020 - Fix bus_write calling order(reg,data) # 6/17/2020 - Fix missing $ and several comments # 6/20/2020 - Modify pin assignments to separate I2C # functions from GPIO functions # onto 5pin(I2C) and 12pin(GPIO) connec +tors # 6/22/2020 - Reassign GPIO0(reserved) to GPIO5 # 8/25/2020 - 2x20 RPi connector shell now available # 8/26/2020 - Fix first compile errors/typos on RPi # 8/28/2020 - Change test freq to 25,000,000Hz # 8/30/2020 - Cycle through a few freq changes # 8/31/2020 - Per Jerry, update reg 183 with |0x12 # see note at An619 pg 61 # # Target: Raspberry PI 3B+ with Adafruit si5351 BOB # # Initial test version: Initilize si5351 and set frequency to 50 MHz # Code to be integrated into a Remote Head and VFO for # Pete Juliano's(N6QW) RADIG II home brew SDR transceiver # that uses Jim Ahlstrom's(N2ADR) Quisk SDR software. # # The Silicon Labs AN619 is required to understand register # functions used in this code...single values may be split # across 3 registers...took me awhile... # # Ported from an Arduino C driver by Jerry Gaffke, KE7ER # as modified by Dr. Ian Lee, KD8CEC # as used in the Bitx40 and uBitx Ham transceivers # by Ashhar Farhan, VU2ESE. # # To Do: Add Hans' quadrature(I&Q) output feature # # Note: Possible subtle bug in si5351_setfreq() # $si5351_clken &= ~(1 << $clknum) # Clears a bit in $si5351_clken to enable a clock # Would require: $si5351_clken |= 1 << $clknum # to set a bit before a subsequent call to disable a clo +ck # # RPI J8 - GPIO Pin Definitions si5351 BOB Pin Definitio +ns # ----------------------------- ------------------------ +-- # [RED] 3V3 (1) (2) 5V (1) CLK0 - SMA # [YEL] SDA/GPIO2 (3) (4) 5V (2) CLK1 - SMA # [BLU] SCL/GPIO3 (5) (6) GND (3) CLK2 - NC # GPIO4 (7) (8) GPIO14 (4) SCL - [BLU] # [BLK] GND (9) (10) GPIO15 (5) SDA - [YEL] # PB1/GPIO17 (11) (12) GPIO18 (6) GND - [BLK] # PB2/GPIO27 (13) (14) GND (7) VIN - [RED] # ENCA/GPIO22 (15) (16) GPIO23 # 3V3 (17) (18) GPIO24 # ENCB/GPIO10 (19) (20) GND # RED/ GPIO9 (21) (22) GPIO25 # YEL/GPIO11 (23) (24) GPIO8 # GND (25) (26) GPIO7 # *GPIO0 (27) (28) GPIO1* # GRN/ GPIO5 (29) (30) GND # GPIO6 (31) (32) GPIO12 # GPIO13 (33) (34) GND # GPIO19 (35) (36) GPIO16 # GPIO26 (37) (38) GPIO20 # GND (39) (40) GPIO21 # * GPIO0 & GPIO1 are reserved use strict; use warnings; use HiPi qw( :i2c ); use HiPi::Device::I2C; use Time::HiRes qw(sleep); # Globals my $SI5351_ADDR = 0x60; # si5351 I2C Address my $SI5351_XTALPF = 2; # si5351 crystal capacitanc +e in pf # 1:6pf 2:8pf 3:10pf my $SI5351_XTAL = 25000000; # Crystal frequency in Hz my $SI5351_MSA = 35; # VCOA is at 25MHz*35 = 875 +MHz # (If using 27MHz crystal # XTAL = 27000000, MSA += 33 # Then VCOA is at 891MH +z) my $si5351_vcoa = $SI5351_XTAL * $SI5351_MSA; # VCO frequency -> 875M +Hz my $si5351_rdiv = 0; # 0-7, extra divisor for f +< 500KHz # CLK pin sees fout/(2* +rdiv) my @si5351_drive = (3, 3, 3); # 0=2ma 1=4ma 2=6ma 3=8ma # for clocks 0, 1, 2 my $si5351_clken = 0xFF; # All clock output drivers +off mask my $calibration = 0; # Correction factor for cry +stal error # Main my $si5351 = HiPi::Device::I2C->new(); # Create an I2C object $si5351->select_address($SI5351_ADDR); # Set I2C device address si5351_init(); # Initialize si5351 registe +rs si5351_set_calibration($calibration); # Apply crystal correction for(my $f = 50000000; $f <= 50500000; $f += 1000) { si5351_setfreq(0, $f); # Tune clock 0 up by 1000Hz sleep(.01); # allows testing with DV +B-T } # dongle # End Main # # ------------------------------- Subroutines ---------------------- +-------------- # sub si5351_init { # Init PLLA Multisyn +th registers 26-33 my $msxp1 = (128 * $SI5351_MSA) - 512; # msxp2=0, msxp3=1, +not fractional my $bb2 = ($msxp1 >> 16) & 0xFF; # Split 32 bits into + bytes my $bb1 = ($msxp1 >> 8) & 0xFF; # See AN619 pgs +28-30 my $bb0 = $msxp1 & 0xFF; my @vals = (0, 1, $bb2, $bb1, $bb0, 0, 0, 0); # a + b/c, 35 + 0/1, + AN619 pg 3 $si5351->bus_write(149, 0); # Spread spectrum of +f $si5351->bus_write(3, $si5351_clken); # Disable all clock +output drivers $si5351->bus_write(183, ($SI5351_XTALPF << 6) | 0x12); # Set cryst +al load capacitance $si5351->bus_write(26, @vals); # Write to 8 PLLA ms +ynth regs 26-33 $si5351->bus_write(177, 0x20); # Reset PLLA(0x80 PL +LB) } sub si5351_set_calibration { my ($cal) = @_; $si5351_vcoa = ($SI5351_XTAL * $SI5351_MSA) + $cal * 100; # Apply + xtal cal $si5351->bus_write(3, $si5351_clken); # Force + all clocks off } sub si5351_setfreq { my($clknum, $fout) = @_; # Init frequency div +iders a + b/c my $msa = int($si5351_vcoa / $fout); # Integer part of vc +oa/fout my $msb = $si5351_vcoa % $fout; # Fractional part of + vcoa/fout my $msc = $fout; while($msc & 0xfff00000) { # Divide by 2 until +fits into 20 bits $msb = $msb >> 1; # Numerator $msc = $msc >> 1; # Denominator } # Multisynth Register Definitions - a + b/c (See SiLabs AN619) # msxp1 - 18 bit integer part of the divider (a) # msxp2 - 20 bit numerator part of the divider (b) # msxp3 - 20 bit denominator part of the divider (c) # From AN619 pg 6 # msxp1 = (128 * a) + (128 * b / c) - 512 # msxp2 = (128 * b) - (128 * b / c) # msxp3 = c my $msxp1 = (128 * $msa + 128 * $msb / $msc - 512) | ($si5351_rdiv + << 20); my $msxp2 = 128 * $msb - 128 * $msb / $msc * $msc; my $msxp3p2top = (($msc & 0x0F0000) << 4) | $msxp2; # Top two nib +bles # Code below splits a, b, & c into bytes to be copied into the r +egisters # Bytes do not lay into consecutive registers, see comments belo +w # A block of 8 registers is assigned to each clock output # i.e. CLK0 - Registers 42-49, AN619 pgs 33-36 my $bb1msc = ($msc >> 8) & 0xFF; # msx_p3[15:8 ] - + Reg 42 my $bb0msc = $msc & 0xFF; # msx_p3[ 7:0 ] - + Reg 43 my $bb2msxp1 = ($msxp1 >> 16) & 0xFF; # msx_p1[17:16] - + Reg 44 my $bb1msxp1 = ($msxp1 >> 8) & 0xFF; # msx_p1[15:8 ] - + Reg 45 my $bb0msxp1 = $msxp1 & 0xFF; # msx_p1[ 7:0 ] - + Reg 46 my $bb2msxp3p2top = ($msxp3p2top >> 16) & 0xFF; # msx_p3[19:16], # msx_p2[19:16] - + Reg 47 my $bb1msxp2 = ($msxp2 >> 8) & 0xFF; # msx_p2[15:8 ] - + Reg 48 my $bb0msxp2 = $msxp2 & 0xFF; # msx_p2[ 7:0 ] - + Reg 49 my @vals = ($bb1msc, $bb0msc, $bb2msxp1, $bb1msxp1, $bb0msxp1, $bb2msxp3p2top, $bb1msxp2, $bb0msxp2); $si5351->bus_write(42 + ($clknum * 8), @vals); $si5351->bus_write(16 + $clknum, 0x0C | $si5351_drive[$clknum]); # + PLLA->MS0->CLK# # + @ #ma $si5351_clken &= ~(1 << $clknum); # Clear bit to en +able clock # (see "bug" note + above) $si5351->bus_write(3, $si5351_clken); # Send clock enab +le }

James

There's never enough time to do it right, but always enough time to do it over...


In reply to Raspberry PI si5351 Driver with HiPi by jmlynesjr

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-03-28 16:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found