├── .DS_Store ├── README.md ├── _config.yml ├── extras ├── .DS_Store ├── SI5351_Arduinol.sch └── images │ ├── photo_02.png │ ├── photo_03.png │ ├── photo_04.png │ ├── photo_05.png │ └── schematic_eagle.png ├── license.txt └── source ├── README.md ├── si5351_signal_generator ├── Rotary.cpp ├── Rotary.h └── si5351_signal_generator.ino └── si5351_signal_generator2 ├── Rotary.cpp ├── Rotary.h ├── si5351_signal_generator2.ino ├── si5351wire.cpp └── si5351wire.h /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Multipurpose signal generator with SI5351](https://pu2clr.github.io/SI5351/) 2 | 3 | It is a multipurpose signal generator controlled by Arduino. This project uses the SI5351 from Silicon Labs. 4 | The [Arduino sketch](https://github.com/pu2clr/SI5351/tree/master/source) is configured to control the SI5351 with three channels from 32.768KHz to 160MHz and steps from 1Hz to 1MHz. 5 | 6 | This project can be freely distributed using the [MIT Free Software model](https://pu2clr.github.io/SI5351/#mit-license). 7 | 8 | [Copyright (c) 2020 Ricardo Lima Caratti](https://pu2clr.github.io/AKC695X/#mit-license). 9 | 10 | Contact: __pu2clr@gmail.com__. 11 | 12 | Be a member of Facebook group [DSP receivers for hobbyists](https://www.facebook.com/groups/2655942598067211) 13 | 14 | 15 | ## Content 16 | 17 | 1. [Preface](https://pu2clr.github.io/SI5351/#preface) 18 | 2. [Schematic](https://pu2clr.github.io/SI5351/#schematic) 19 | 3. [Photos and Interface](https://pu2clr.github.io/SI5351/#photos-and-interface) 20 | 4. [Source Code](source) 21 | 5. [References](https://pu2clr.github.io/SI5351/#references) 22 | 23 | ## Preface 24 | 25 | The Si5351 is an I2C configurable clock generator that is very appropriate for many electronic projects. It is also a great device for replacing crystal oscillators. This project uses a version of SI5351 that has three signal outputs that can vary from 8 KHz to 160 MHz. These outputs, can give you three distinct frequencies running at the same time. A great feature of the Si5351A is the possibility of using it with a microcontroller or platform like Arduino, PIC family and others. This small project is about a multipurpose signal generator controlled by Arduino or similar. For more information about SI5351A see [Si5351A/B/C-B I2C-PROGRAMMABLE ANY-FREQUENCY CMOS CLOCKGENERATOR + VCXO](https://www.silabs.com/documents/public/data-sheets/Si5351-B.pdf). 26 | 27 | Despite the square wave form generated by the Si5351 device, it is very appropriated for many transmitters and receivers applications. This project has been very useful for me during Arduino Library development for the DSP receivers SI473X, SI4844, AKC695X, KT0915 and otehrs. With this signal generator, I could test the external oscillator feature of these devices. Also, I used it to build a small shortwave transmitter to test the HF reception during the bad propagation and bad reception conditions due to my current location. 28 | 29 | ### See also: 30 | 31 | 1. [PU2CLR Si4735 Library for Arduino](https://pu2clr.github.io/SI4735/). This library was built based on “Si47XX PROGRAMMING GUIDE; AN332” and it has support to FM, AM and SSB modes (LW, MW and SW). It also can be used on all members of the SI47XX family respecting, of course, the features available for each IC version; 32 | 2. [PU2CLR SI4844 Arduino Library](https://github.com/pu2clr/SI4844). This is an Arduino library for the SI4844, BROADCAST ANALOG TUNING DIGITAL DISPLAY AM/FM/SW RADIO RECEIVER, IC from Silicon Labs. It is available on Arduino IDE. This library is intended to provide an easier interface for controlling the SI4844. 33 | 3. [PU2CLR AKC695X Arduino Library](https://pu2clr.github.io/AKC695X/). The AKC695X is a family of IC DSP receiver from AKC technology. The AKC6955 and AKC6959sx support AM and FM modes. On AM mode the AKC6955 and AKC6959sx work on LW, MW and SW. On FM mode they work from 64MHz to 222MHz. 34 | 4. [PU2CLR KT0915 Arduino Library](https://pu2clr.github.io/KT0915/). The KT0915 is a full band AM (LW, MW and SW) and FM DSP receiver that can provide you a easy way to build a high quality radio with low cost. 35 | 5. [PU2CLR RDA5807 Arduino Library](https://pu2clr.github.io/RDA5807/). The RDA5807 is a FM DSP integrated circuit receiver (50 to 115MHz) with low noise amplifier support. This device requires very few external components if compared with other similar devices. It also supports RDS/RBDS functionalities, direct auto gain control (AGC) and real time adaptive noise cancellation function. 36 | 6. [PU2CLR SI470X Arduino Library](https://pu2clr.github.io/SI470X/). It is a Silicon Labs device family that integrates the complete functionalities for FM receivers, including RDS (Si4703). 37 | 7. [Small Shortwave transmitter](https://github.com/pu2clr/Small-Shortwave-Transmitter). This project is about a shortwave trasmitter from 3 MHz to 30 MHz. It uses the SI5351 oscillator from Silicon Labs controlled by Arduino. Also, you can use it with a crystal oscillator. In this case, you will not need the SI5351 devive and Arduino. 38 | 8. [Simple BFO with si5351 controlled by Arduino](https://github.com/pu2clr/BFO). This small project is about a BFO that you can control the output of the Si5351A by using the Arduino Micro (Atmega32u4). This BFO ocilates from 452KHz to 458KHz. However, you can change this range for other between 8Kz to 160MHz. 39 | 9. [VFO and BFO using Arduino with Si5351 controlled by SmartPhone](https://github.com/pu2clr/VFO_SI5351_ARDUINO_SMARTPHONE). This project implements a VFO and BFO using an Arduino with Si5351 signal generator. You can controll the VFO using an encoder and also a SmartPhone via a mobile application developed here for this purpose. 40 | 41 | 42 | #### More Arduino Projects by author 43 | 44 | * [Multipurpose signal generator with SI5351](https://pu2clr.github.io/SI5351/). It is a multipurpose signal generator controlled by Arduino. This project uses the SI5351 from Silicon Labs. The Arduino sketch is configured to control the SI5351 with three channels from 32.768KHz to 160MHz and steps from 1Hz to 1MHz. 45 | * [Shortwave Arduino Transmiter](https://pu2clr.github.io/Small-Shortwave-Transmitter/). This project is about a shortwave transmitter from 3 MHz to 30 MHz. It uses the SI5351 oscillator from Silicon Labs controlled by Arduino. Also, you can use it with a crystal oscillator. In this case, you will not need the SI5351 device and Arduino. 46 | * [Android and iOS Bluetooth Remote Control for PU2CLR Arduino Library DSP receivers](https://pu2clr.github.io/bluetooth_remote_control/). This project is an extension of the Arduino library projects for: [SI4735](https://pu2clr.github.io/SI4735/); [AKC6959](https://pu2clr.github.io/AKC695X/) and [KT0915](https://pu2clr.github.io/KT0915/). It is a simple example that shows a way to use your smartphone as a remote control via Bluetooth. In order to follow the steps presented here, I am assuming that you have some knowledge in development for mobile devices. Also, you will need to be familiar with the Javascript programming language. The development environment used by this project is the [Apache Cordova](https://cordova.apache.org/docs/en/latest/guide/overview/index.html). Cordova is a open-source mobile development framework that allows you to develop cross-platform applications. That means you can code once and deploy the application in many system, including iOS and Android. 47 | Cordova provides an easy way to develop for iOS and Android. 48 | * [Band Pass Filter controlled by Arduino](https://pu2clr.github.io/auto_bpf_arduino/). It is a HF band pass filter controlled by Arduino. It is designed for HF receivers. With this project, you can use a set of up to four HF bandpass filters that can be selected by Arduino. To do that you will need just two digital Arduino pins. 49 | 50 | 51 | There is a __Facebook__ group called [__Si47XX for Radio Experimenters__](https://www.facebook.com/groups/532613604253401/) where the purpose is exchanging experiences with projects based on Silicon Labs SI47XX IC family. You will be welcome to the group [Si47XX for Radio Experimenters](https://www.facebook.com/groups/532613604253401/). You can also be a member of __group.io__ [SI47XX for hobbyists](https://groups.io/g/si47xx) 52 | 53 | 54 | ## Arduino Source Code 55 | 56 | The Arduino sketch can be found on [source folder - https://github.com/pu2clr/SI5351/tree/master/source](https://github.com/pu2clr/SI5351/tree/master/source). The software is configured to control the SI5351 from 32.768KHz to 160MHz and steps from 1Hz to 1MHz. 57 | 58 | For SI5351 control, the Arduino sketch uses the [Etherkit/si5351 Arduino library from Jason Milldrum](https://github.com/etherkit/Si5351Arduino); 59 | 60 | For encoder control, the Arduino sketch uses the Rotary Encoder Class implementation from Ben Buxton (the source code is included together with this sketch) 61 | 62 | [See source folder for more detail](https://github.com/pu2clr/SI5351/tree/master/source). 63 | 64 | 65 | ## Schematic 66 | 67 | ![Schematic](extras/images/schematic_eagle.png) 68 | 69 | 70 | ## Photos and Interface 71 | 72 | The user interface of this generator consists of three push buttons and an encoder. One of that push buttons comes with the encoder device. 73 | 74 | Pressing the encoder push button, you can select the current control (Step, Favorite frequencies or output clock). By pressing de left or right push buttons, you can select the parameter you want for each kind of control. The display will show you the current status of the signal generator. 75 | 76 | 77 | The photos below show the multipurpose signal generator controlled by Arduino. This project uses an Arduino Nano. However, you can use any Arduino board. In this case, see the pinout of your Arduino. 78 | 79 | 80 | Current output clock is CLK0; the current Step is 10Hz; and current frequency is 8000.010 KHz 81 | 82 | ![Photo 01](extras/images/photo_02.png) 83 | 84 | 85 | The current control is output clock (select the output you want to setup by pressing the left or right push buttons); current clock is CLK2; current frequency is 32.768 KHz; and current step is 10 Hz. 86 | 87 | ![Photo 02](extras/images/photo_03.png) 88 | 89 | 90 | The current frequency on output CLK2 is 28000 KHz (28MHz); the current control is Step (select the step you want by pressing left or right push buttons); in this case, the current step is 50 KHz. 91 | 92 | ![Photo 03](extras/images/photo_04.png) 93 | 94 | 95 | The current control is Favorite (select the favorite frequency you want for the current channel by pressing the left or right push buttons); the current step is 1 KHz; and current frequency is 16MHz. 96 | 97 | ![Photo 04](extras/images/photo_05.png) 98 | 99 | 100 | 101 | ## MIT License 102 | 103 | Copyright (c) 2019 Ricardo Lima Caratti 104 | 105 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 106 | 107 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 108 | 109 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 110 | 111 | 112 | 113 | ## References 114 | 115 | * [Si5351A/B/C-B I2C-PROGRAMMABLE ANY-FREQUENCY CMOS CLOCKGENERATOR + VCXO](https://www.silabs.com/documents/public/data-sheets/Si5351-B.pdf). 116 | * [Etherkit/si5351 Arduino library from Jason Milldrum](https://github.com/etherkit/Si5351Arduino) -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /extras/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/.DS_Store -------------------------------------------------------------------------------- /extras/SI5351_Arduinol.sch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | <b>DIY Modules for Arduino, Raspberry Pi, CubieBoard etc.</b> 149 | <br><br> 150 | The library contains a list of symbols and footprints for popular, cheap and easy-to-use electronic modules.<br> 151 | The modules are intend to work with microprocessor-based platforms such as <a href="http://arduino.cc">Arduino</a>, <a href="http://raspberrypi.org/">Raspberry Pi</a>, <a href="http://cubieboard.org/">CubieBoard</a>, <a href="http://beagleboard.org/">BeagleBone</a> and many others. There are many manufacturers of the modules in the world. Almost all of them can be bought on <a href="ebay.com">ebay.com</a>.<br> 152 | <br> 153 | By using this library, you can design a PCB for devices created with usage of modules. Even if you do not need to create PCB design, you can also use the library to quickly document your work by drawing schematics of devices built by you.<br> 154 | <br> 155 | The latest version, examples, photos and much more can be found at: <b><a href="http://diymodules.org/eagle">diymodules.org/eagle</a></b><br><br> 156 | Comments, suggestions and bug reports please send to: <b><a href="mailto:eagle@diymodules.org">eagle@diymodules.org</b></a><br><br> 157 | <i>Version: 1.8.0 (2017-Jul-02)</i><br> 158 | <i>Created by: Miroslaw Brudnowski</i><br><br> 159 | <i>Released under the Creative Commons Attribution 4.0 International License: <a href="http://creativecommons.org/licenses/by/4.0">http://creativecommons.org/licenses/by/4.0</a></i> 160 | <br><br> 161 | <center> 162 | <a href="http://diymodules.org/eagle"><img src="http://www.diymodules.org/img/diymodules-lbr-image.php?v=1.8.0" alt="DIYmodules.org"></a> 163 | </center> 164 | 165 | 166 | <b>Arduino Pro Mini</b><br> 167 | Footprint with angle programming connector 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | >NAME 430 | >VALUE 431 | 432 | 433 | <b>128x64 Dot Matrix OLED Module</b><br /> 434 | Variant with I2C interface 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | >NAME 472 | >VALUE 473 | 474 | 475 | 476 | 477 | GND 478 | VCC 479 | SCL 480 | SDA 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | <b>Arduino Pro Mini</b> without programming pins 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | >NAME 520 | >VALUE 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | <b>128x64 Dot Matrix OLED Module</b><br /> 529 | Variant with I2C interface 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | >NAME 546 | >VALUE 547 | 548 | 549 | 550 | 128x64 551 | OLED 552 | 553 | 554 | 555 | 556 | <b>Arduino Pro Mini</b> (and compatible devices) with angle programming connector 557 | <p>More details available here:<br> 558 | <a href="https://www.arduino.cc/en/Main/ArduinoBoardProMini">https://www.arduino.cc/en/Main/ArduinoBoardProMini</a></p> 559 | <p><b><a href="http://www.ebay.com/sch/arduino+pro+mini">Click here to find device on ebay.com</a></b></p> 560 | 561 | <p><img alt="photo" src="http://www.diymodules.org/img/device-photo.php?name=ARDUINO-PRO-MINI-"></p> 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | <b>128x64 Dot Matrix OLED Module</b> based on <b>SSD1306</b> chip<br /> 607 | Variant with <b>I2C interface</b> 608 | <p>More details available here:<br /> 609 | <a href="http://www.instructables.com/id/Monochrome-096-i2c-OLED-display-with-arduino-SSD13/">http://www.instructables.com/id/Monochrome-096-i2c-OLED-display-with-arduino-SSD13/</a></p> 610 | <p><b>SSD1306</b> datasheet:<br> 611 | <a href="https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf">https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf</a></p> 612 | <p><b><a href="http://www.ebay.com/sch/oled+display+128x64+iic">Click here to find device on ebay.com</a></b><br /> 613 | <b>Note:</b> There are two variants: I2C and SPI. Search for the proper version.</p> 614 | <p><img alt="photo" src="http://www.diymodules.org/img/device-photo.php?name=DISPLAY-OLED-128X64-I2C"></p> 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | >NAME 658 | >VALUE 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | >NAME 691 | >VALUE 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | Programmable PLL Clock Generator Single 0.0025MHz to 200MHz 20-Pin QFN Bulk 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | <b>Supply Symbols</b><p> 763 | GND, VCC, 0V, +5V, -5V, etc.<p> 764 | Please keep in mind, that these devices are necessary for the 765 | automatic wiring of the supply signals.<p> 766 | The pin name defined in the symbol is identical to the net which is to be wired automatically.<p> 767 | In this library the device names are the same as the pin names of the symbols, therefore the correct signal names appear next to the supply symbols in the schematic.<p> 768 | <author>Created by librarian@cadsoft.de</author> 769 | 770 | 771 | 772 | 773 | 774 | >VALUE 775 | 776 | 777 | 778 | 779 | 780 | <b>SUPPLY SYMBOL</b> 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | <b>Supply Symbols</b><p> 796 | GND, VCC, 0V, +5V, -5V, etc.<p> 797 | Please keep in mind, that these devices are necessary for the 798 | automatic wiring of the supply signals.<p> 799 | The pin name defined in the symbol is identical to the net which is to be wired automatically.<p> 800 | In this library the device names are the same as the pin names of the symbols, therefore the correct signal names appear next to the supply symbols in the schematic.<p> 801 | <author>Created by librarian@cadsoft.de</author> 802 | 803 | 804 | 805 | 806 | 807 | >VALUE 808 | 809 | 810 | 811 | 812 | 813 | <b>SUPPLY SYMBOL</b> 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | <h3>SparkFun Switches, Buttons, Encoders</h3> 829 | In this library you'll find switches, buttons, joysticks, and anything that moves to create or disrupt an electrical connection. 830 | <br> 831 | <br> 832 | We've spent an enormous amount of time creating and checking these footprints and parts, but it is <b> the end user's responsibility</b> to ensure correctness and suitablity for a given componet or application. 833 | <br> 834 | <br>If you enjoy using this library, please buy one of our products at <a href=" www.sparkfun.com">SparkFun.com</a>. 835 | <br> 836 | <br> 837 | <b>Licensing:</b> Creative Commons ShareAlike 4.0 International - https://creativecommons.org/licenses/by-sa/4.0/ 838 | <br> 839 | <br> 840 | You are welcome to use this library for commercial purposes. For attribution, we ask that when you begin to sell your device using our footprint, you email us with a link to the product being sold. We want bragging rights that we helped (in a very small part) to create your 8th world wonder. We would like the opportunity to feature your device on our homepage. 841 | 842 | 843 | <h3>Rotary Encoder - 16mm Top Width</h3> 844 | <p>Encoders rotate similarly to potentiometers, but they’re different from potentiometers in that an encoder has full rotation without limits. They output gray code so that you can tell how much and in which direction the encoder has been turned.</p> 845 | <p><a href="http://www.top-up.com.tw/front/bin/ptdetail.phtml?Part=EC16PXHFB-B">Datasheet</a> (EC16PHBF-B-20F-24-24C-16Y)</p> 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | Mounting Surface 887 | *3mm diameter x 2mm tab at 6 o'clock (from front) 888 | >Name 889 | >Value 890 | 891 | 892 | 893 | 894 | Rotary Encoder - 16mm Top Width 895 | Encoders rotate similarly to potentiometers, but they’re different from potentiometers in that an encoder has full rotation without limits. They output gray code so that you can tell how much and in which direction the encoder has been turned. 896 | Datasheet (EC16PHBF-B-20F-24-24C-16Y) 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | <h3>Rotary Encoder</h3> 905 | <p>Encoders rotate similarly to potentiometers, but they’re different from potentiometers in that an encoder has full rotation without limits. They output gray code so that you can tell how much and in which direction the encoder has been turned.</p> 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | >NAME 923 | >VALUE 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | <h3>Rotary Encoder</h3> 933 | <p>Encoders rotate similarly to potentiometers, but they’re different from potentiometers in that an encoder has full rotation without limits. They output gray code so that you can tell how much and in which direction the encoder has been turned.</p> 934 | <h4>SparkFun Products</h4> 935 | <ul> 936 | <li>Used on the <a href="https://www.sparkfun.com/products/11043">https://www.sparkfun.com/products/11043</a> (COM-11043)</li> 937 | </ul> 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | >NAME 978 | >VALUE 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | >NAME 1012 | >VALUE 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | >NAME 1040 | >VALUE 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | >NAME 1068 | >VALUE 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | <p><b>Buttons</b></p> 1076 | <b>SOFTTOUCHSMD_SJ</b> - Soft touch temporary push button (Super Junk Akihabara) 1077 | <p>Compatible with SKPMANE010 [Mouser: 688-SKPMAN]</p> 1078 | <p>C&K_KSS = Digikey: KSS221GLFS</p> 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | Encoder 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | Since Version 8.2, EAGLE supports online libraries. The ids 1326 | of those online libraries will not be understood (or retained) 1327 | with this version. 1328 | 1329 | 1330 | Since Version 8.3, EAGLE supports URNs for individual library 1331 | assets (packages, symbols, and devices). The URNs of those assets 1332 | will not be understood (or retained) with this version. 1333 | 1334 | 1335 | Since Version 8.3, EAGLE supports the association of 3D packages 1336 | with devices in libraries, schematics, and board files. Those 3D 1337 | packages will not be understood (or retained) with this version. 1338 | 1339 | 1340 | 1341 | -------------------------------------------------------------------------------- /extras/images/photo_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/images/photo_02.png -------------------------------------------------------------------------------- /extras/images/photo_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/images/photo_03.png -------------------------------------------------------------------------------- /extras/images/photo_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/images/photo_04.png -------------------------------------------------------------------------------- /extras/images/photo_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/images/photo_05.png -------------------------------------------------------------------------------- /extras/images/schematic_eagle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pu2clr/SI5351/a6a7145a30a802f06bfa76025700da0861be85a3/extras/images/schematic_eagle.png -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ricardo Lima Caratti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /source/README.md: -------------------------------------------------------------------------------- 1 | # Source Code 2 | 3 | Arduino Sketch to control the SI5351 device. 4 | 5 | The user interface of this generator consists of three push buttons and an encoder. One of that push buttons comes with the encoder device. 6 | 7 | Pressing the encoder pushbutton, you can select the current control (Step, Favorite frequencies or output clock). By pressing de left or right push buttons, you can select the parameter you want for each kind of control. The display will show you the current status of the signal generator. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /source/si5351_signal_generator/Rotary.cpp: -------------------------------------------------------------------------------- 1 | /* Rotary encoder handler for arduino. v1.1 2 | 3 | Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. 4 | Contact: bb@cactii.net 5 | 6 | A typical mechanical rotary encoder emits a two bit gray code 7 | on 3 output pins. Every step in the output (often accompanied 8 | by a physical 'click') generates a specific sequence of output 9 | codes on the pins. 10 | 11 | There are 3 pins used for the rotary encoding - one common and 12 | two 'bit' pins. 13 | 14 | The following is the typical sequence of code on the output when 15 | moving from one step to the next: 16 | 17 | Position Bit1 Bit2 18 | ---------------------- 19 | Step1 0 0 20 | 1/4 1 0 21 | 1/2 1 1 22 | 3/4 0 1 23 | Step2 0 0 24 | 25 | From this table, we can see that when moving from one 'click' to 26 | the next, there are 4 changes in the output code. 27 | 28 | - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. 29 | - Then both bits are high, halfway through the step. 30 | - Then Bit1 goes low, but Bit2 stays high. 31 | - Finally at the end of the step, both bits return to 0. 32 | 33 | Detecting the direction is easy - the table simply goes in the other 34 | direction (read up instead of down). 35 | 36 | To decode this, we use a simple state machine. Every time the output 37 | code changes, it follows state, until finally a full steps worth of 38 | code is received (in the correct order). At the final 0-0, it returns 39 | a value indicating a step in one direction or the other. 40 | 41 | It's also possible to use 'half-step' mode. This just emits an event 42 | at both the 0-0 and 1-1 positions. This might be useful for some 43 | encoders where you want to detect all positions. 44 | 45 | If an invalid state happens (for example we go from '0-1' straight 46 | to '1-0'), the state machine resets to the start until 0-0 and the 47 | next valid codes occur. 48 | 49 | The biggest advantage of using a state machine over other algorithms 50 | is that this has inherent debounce built in. Other algorithms emit spurious 51 | output with switch bounce, but this one will simply flip between 52 | sub-states until the bounce settles, then continue along the state 53 | machine. 54 | A side effect of debounce is that fast rotations can cause steps to 55 | be skipped. By not requiring debounce, fast rotations can be accurately 56 | measured. 57 | Another advantage is the ability to properly handle bad state, such 58 | as due to EMI, etc. 59 | It is also a lot simpler than others - a static state table and less 60 | than 10 lines of logic. */ 61 | 62 | #include "Arduino.h" 63 | #include "Rotary.h" 64 | 65 | /* The below state table has, for each state (row), the new state 66 | to set based on the next encoder output. From left to right in, 67 | the table, the encoder outputs are 00, 01, 10, 11, and the value 68 | in that position is the new state to set. */ 69 | 70 | #define R_START 0x0 71 | #ifdef HALF_STEP 72 | // Use the half-step state table (emits a code at 00 and 11) 73 | #define R_CCW_BEGIN 0x1 74 | #define R_CW_BEGIN 0x2 75 | #define R_START_M 0x3 76 | #define R_CW_BEGIN_M 0x4 77 | #define R_CCW_BEGIN_M 0x5 78 | const unsigned char ttable[6][4] = { 79 | // R_START (00) 80 | {R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 81 | // R_CCW_BEGIN 82 | {R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, 83 | // R_CW_BEGIN 84 | {R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, 85 | // R_START_M (11) 86 | {R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, 87 | // R_CW_BEGIN_M 88 | {R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, 89 | // R_CCW_BEGIN_M 90 | {R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, 91 | }; 92 | #else 93 | // Use the full-step state table (emits a code at 00 only) 94 | #define R_CW_FINAL 0x1 95 | #define R_CW_BEGIN 0x2 96 | #define R_CW_NEXT 0x3 97 | #define R_CCW_BEGIN 0x4 98 | #define R_CCW_FINAL 0x5 99 | #define R_CCW_NEXT 0x6 100 | 101 | const unsigned char ttable[7][4] = { 102 | // R_START 103 | {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 104 | // R_CW_FINAL 105 | {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, 106 | // R_CW_BEGIN 107 | {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, 108 | // R_CW_NEXT 109 | {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, 110 | // R_CCW_BEGIN 111 | {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, 112 | // R_CCW_FINAL 113 | {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, 114 | // R_CCW_NEXT 115 | {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, 116 | }; 117 | #endif 118 | 119 | // Constructor. Each arg is the pin number for each encoder contact 120 | Rotary::Rotary(char _pin1, char _pin2) { 121 | // Assign variables 122 | pin1 = _pin1; 123 | pin2 = _pin2; 124 | // Set pins to input. 125 | pinMode(pin1, INPUT); 126 | pinMode(pin2, INPUT); 127 | #ifdef ENABLE_PULLUPS 128 | digitalWrite(pin1, HIGH); 129 | digitalWrite(pin2, HIGH); 130 | #endif 131 | // Initialise state 132 | state = R_START; 133 | } 134 | 135 | unsigned char Rotary::process() { 136 | // Grab state of input pins 137 | unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1); 138 | // Determine new state from the pins and state table 139 | state = ttable[state & 0xf][pinstate]; 140 | // Return emit bits, ie the generated event 141 | return state & 0x30; 142 | } 143 | 144 | -------------------------------------------------------------------------------- /source/si5351_signal_generator/Rotary.h: -------------------------------------------------------------------------------- 1 | // Rotary encoder library for Arduino. 2 | #include "Arduino.h" 3 | 4 | #ifndef rotary_h 5 | #define rotary_h 6 | 7 | // Enable this to emit codes twice per step 8 | // #define HALF_STEP 9 | 10 | #define ENABLE_PULLUPS // Enable weak pullups 11 | 12 | // Values returned by 'process' 13 | #define DIR_NONE 0x0 // No complete step yet 14 | #define DIR_CW 0x10 // Clockwise step 15 | #define DIR_CCW 0x20 // Anti-clockwise step 16 | 17 | class Rotary 18 | { 19 | public: 20 | Rotary(char, char); 21 | // Process pin(s) 22 | unsigned char process(); 23 | private: 24 | unsigned char state; 25 | unsigned char pin1; 26 | unsigned char pin2; 27 | }; 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /source/si5351_signal_generator/si5351_signal_generator.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | It is a multipurpose signal generator controlled by Arduino. This project uses the SI5351 from Silicon Labs. 4 | This sketch is configured to control the SI5351 from 32.768KHz to 160MHz and steps from 1Hz to 1MHz. 5 | 6 | With this sketch you can control the three clock outputs. 7 | 8 | For SI5351 control, this sketch uses the the Etherkit/si5351 Arduino library from Jason Milldrum (https://github.com/etherkit/Si5351Arduino); 9 | For encoder control, this sketch uses the Rotary Encoder Class implementation from Ben Buxton (the source code is included together with this sketch) 10 | 11 | Arduino Pro Mini 3.3V (8MHz) and SI5351 breakout wire up 12 | 13 | | Device name | Device Pin / Description | Arduino Pin | 14 | | ---------------- | -------------------- | ------------ | 15 | | OLED | | | 16 | | | SDA | A4 | 17 | | | SCL | A5 | 18 | | SI5351 | | | 19 | | | SDIO (pin 18) | A4 | 20 | | | SCLK (pin 17) | A5 | 21 | | Buttons | | | 22 | | | SWITCH_CMD | 4 | 23 | | | BUTTON_UP | 6 | 24 | | | BUTTON_DOWN | 5 | 25 | | Encoder | | | 26 | | | A | 3 | 27 | | | B | 2 | 28 | 29 | See https://github.com/etherkit/Si5351Arduino and know how to calibrate your Si5351 30 | Author: Ricardo Lima Caratti 2020 31 | 32 | */ 33 | 34 | #include 35 | #include 36 | #include "Rotary.h" 37 | #include "SSD1306Ascii.h" 38 | #include "SSD1306AsciiAvrI2c.h" 39 | 40 | // Enconder PINs 41 | #define ENCODER_PIN_A 3 // Encoder pin A 42 | #define ENCODER_PIN_B 2 // Encoder pin B 43 | #define SWITCH_CMD 4 // ENCODER button or regular push button 44 | #define BUTTON_UP 6 // Button up command 45 | #define BUTTON_DOWN 5 // Button down command 46 | 47 | #define CMD_STEP 0 48 | #define CMD_FAVORITE 1 49 | #define CMD_CLK 2 50 | 51 | // OLED Diaplay constants 52 | #define I2C_ADDRESS 0x3C 53 | #define RST_PIN -1 // Define proper RST_PIN if required. 54 | 55 | // Change this value bellow (CORRECTION_FACTOR) to 0 if you do not know the correction factor of your Si5351A. 56 | #define CORRECTION_FACTOR -13500 // See how to calibrate your Si5351A (0 if you do not want). 57 | // #define CORRECTION_FACTOR 0 58 | 59 | #define MIN_VFO 3276800LLU 60 | #define MAX_VFO 16000000000LLU // VFO max. frequency 30MHz 61 | 62 | // Struct for step 63 | typedef struct 64 | { 65 | char *name; // step label: 50Hz, 10Hz, 500Hz and 100KHz 66 | long value; // Frequency value (unit 0.01Hz See documentation) to increment or decrement 67 | } Step; 68 | 69 | // Steps database. You can change the Steps and numbers of steps here if you need. 70 | Step step[] = { 71 | {(char *)"1Hz", 100}, // Minimum Frequency step (incremente or decrement) 1Hz 72 | {(char *)"10Hz", 1000}, 73 | {(char *)"100Hz", 10000}, 74 | {(char *)"500Hz", 50000}, 75 | {(char *)"1KHz", 100000}, 76 | {(char *)"5KHz", 500000}, 77 | {(char *)"10KHz", 1000000}, 78 | {(char *)"32.768K", 3276800}, 79 | {(char *)"50KHz", 5000000}, 80 | {(char *)"500KHz", 50000000}, 81 | {(char *)"1MHz", 100000000}}; // Maximum frequency step 500KHz 82 | 83 | // Calculate the index of last position of step[] array 84 | const int lastStepVFO = (sizeof step / sizeof(Step)) - 1; 85 | int currentStep = 4; 86 | 87 | // Your favotite frequencies 88 | uint64_t favorite[] = {3276800LLU, 350000000LLU, 710000000LLU, 800000000LLU, 1070000000LLU, 1200000000LLU, 89 | 1350000000LLU, 1600000000LLU, 2000000000LLU, 2400000000LLU, 2700000000LLU, 2800000000LLU, 90 | 3200000000LLU,4600000000LLU, 5000000000LLU, 10000000000LLU, 13300000000LLU, 14400000000LLU}; 91 | const int lastFavorite = (sizeof favorite / sizeof(uint64_t) ) - 1; 92 | int currentFavorite = 3; 93 | 94 | uint64_t currentOutputClock[]{1070000000LLU, 1350000000LLU, 3276800LLU}; // Stores the current clock on CLK0, CLK1 and CLK2 95 | const int lastCurrentOutputClock = (sizeof currentOutputClock / sizeof(uint64_t)) - 1; 96 | 97 | 98 | int currentClock = 0; // current clock output 0 99 | 100 | int currentCommand = CMD_STEP; 101 | 102 | char *cmd[] = {(char *) "Step", (char *) "Favorite", (char *) "Out. Clk" }; 103 | char *clk[] = {(char *) "CLK0", (char *) "CLK1", (char *) "CLK2" }; 104 | 105 | 106 | // Encoder controller 107 | Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B); 108 | 109 | // OLED - Declaration for a SSD1306 display connected to I2C (SDA, SCL pins) 110 | SSD1306AsciiAvrI2c display; 111 | 112 | // The Si5351 instance. 113 | Si5351 si5351; 114 | 115 | bool isFreqChanged = true; 116 | bool clearDisplay = false; 117 | 118 | uint64_t vfoFreq; 119 | uint64_t vfoLastValue; 120 | 121 | // Encoder control variables 122 | volatile int encoderCount = 0; 123 | 124 | void setup() 125 | { 126 | // Encoder pins 127 | pinMode(ENCODER_PIN_A, INPUT_PULLUP); 128 | pinMode(ENCODER_PIN_B, INPUT_PULLUP); 129 | // Si5351 contrtolers pins 130 | 131 | pinMode(SWITCH_CMD, INPUT_PULLUP); 132 | pinMode(BUTTON_UP, INPUT_PULLUP); 133 | pinMode(BUTTON_DOWN, INPUT_PULLUP); 134 | 135 | // Initiating the OLED Display 136 | display.begin(&Adafruit128x64, I2C_ADDRESS); 137 | display.setFont(Adafruit5x7); 138 | display.set1X(); 139 | display.clear(); 140 | display.setCursor(17, 0); 141 | display.print("Multipurpose"); 142 | display.setCursor(33, 1); 143 | display.print("Signal"); 144 | display.setCursor(23, 2); 145 | display.print("Generator"); 146 | display.setCursor(22, 5); 147 | display.print("BY PU2CLR"); 148 | delay(4000); 149 | display.clear(); 150 | 151 | vfoFreq = favorite[currentFavorite]; 152 | currentOutputClock[currentClock] = vfoFreq; 153 | 154 | showStatus(); 155 | // Initiating the Signal Generator (si5351) 156 | si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); 157 | // Adjusting the frequency (see how to calibrate the Si5351 - example si5351_calibration.ino) 158 | si5351.set_correction(CORRECTION_FACTOR, SI5351_PLL_INPUT_XO); 159 | si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); 160 | si5351.set_freq(vfoFreq, (si5351_clock) currentClock); // Start CLK0 (VFO) 161 | 162 | // Disable CLK 1 and 2 outputs 163 | si5351.output_enable(SI5351_CLK1, 0); 164 | si5351.output_enable(SI5351_CLK2, 0); 165 | si5351.update_status(); 166 | 167 | delay(500); 168 | 169 | // Encoder interrupt 170 | attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); 171 | attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE); 172 | 173 | delay(1000); 174 | } 175 | 176 | // Use Rotary.h and Rotary.cpp implementation to process encoder via interrupt 177 | void rotaryEncoder() 178 | { // rotary encoder events 179 | uint8_t encoderStatus = encoder.process(); 180 | if (encoderStatus) 181 | { 182 | if (encoderStatus == DIR_CW) 183 | { 184 | encoderCount = 1; 185 | } 186 | else 187 | { 188 | encoderCount = -1; 189 | } 190 | } 191 | } 192 | 193 | // Show Signal Generator Information 194 | // Verificar setCursor() em https://github.com/greiman/SSD1306Ascii/issues/53 195 | void showStatus() 196 | { 197 | char aux[15]; 198 | double vfo = vfoFreq / 100000.0; 199 | 200 | dtostrf(vfo,5,3,aux); 201 | 202 | display.setCursor(0, 0); 203 | display.set2X(); 204 | display.setCursor(0, 0); 205 | display.print(" "); 206 | display.setCursor(0, 0); 207 | display.print(aux); 208 | display.setCursor(95,2); 209 | display.set1X(); 210 | display.print("KHz"); 211 | display.setCursor(0, 4); 212 | display.print("Step: "); 213 | display.print(step[currentStep].name); 214 | display.print(" - "); 215 | display.print(clk[currentClock]); 216 | display.setCursor(0, 6); 217 | display.print("Command: "); 218 | display.print(cmd[currentCommand]); 219 | } 220 | 221 | // Change the frequency (increment or decrement) 222 | // direction parameter is 1 (clockwise) or -1 (counter-clockwise) 223 | void changeFreq(int direction) 224 | { 225 | vfoFreq += step[currentStep].value * direction; // currentStep * direction; 226 | // Check the VFO limits 227 | if (vfoFreq > MAX_VFO ) 228 | vfoFreq = MIN_VFO; 229 | else if (vfoFreq < MIN_VFO) 230 | vfoFreq = MAX_VFO; 231 | 232 | isFreqChanged = true; 233 | } 234 | 235 | void doCommandUp() { 236 | display.clear(); 237 | if ( currentCommand == CMD_STEP ) 238 | currentStep = (currentStep < lastStepVFO) ? (currentStep + 1) : 0; 239 | else if (currentCommand == CMD_FAVORITE ) 240 | { 241 | currentFavorite = (currentFavorite < lastFavorite) ? (currentFavorite + 1) : 0; 242 | vfoFreq = favorite[currentFavorite]; 243 | currentOutputClock[currentClock] = vfoFreq; 244 | isFreqChanged = true; 245 | } else { 246 | currentClock = (currentClock < lastCurrentOutputClock) ? (currentClock + 1) : 0; 247 | vfoFreq = currentOutputClock[currentClock]; 248 | } 249 | delay(200); 250 | } 251 | 252 | void doCommandDown() { 253 | display.clear(); 254 | if ( currentCommand == CMD_STEP ) 255 | currentStep = (currentStep > 0) ? (currentStep - 1) : lastStepVFO; 256 | else if (currentCommand == CMD_FAVORITE) 257 | { 258 | currentFavorite = (currentFavorite > 0) ? (currentFavorite - 1) : lastFavorite; 259 | vfoFreq = favorite[currentFavorite]; 260 | currentOutputClock[currentClock] = vfoFreq; 261 | isFreqChanged = true; 262 | } else { 263 | currentClock = (currentClock > 0) ? (currentClock - 1) : lastCurrentOutputClock; 264 | vfoFreq = currentOutputClock[currentClock]; 265 | } 266 | delay(200); 267 | } 268 | 269 | void loop() 270 | { 271 | // Check if the encoder has moved. 272 | if (encoderCount != 0) 273 | { 274 | if (encoderCount == 1) 275 | changeFreq(1); 276 | else 277 | changeFreq(-1); 278 | encoderCount = 0; 279 | } 280 | 281 | // Switch the current command control (step or favorite frequencies) 282 | if (digitalRead(SWITCH_CMD) == LOW) { 283 | display.clear(); 284 | currentCommand = (currentCommand >= 2)? 0: (currentCommand + 1);; 285 | showStatus(); 286 | delay(200); 287 | } else if (digitalRead(BUTTON_UP) == LOW) { 288 | doCommandUp(); 289 | showStatus(); 290 | } else if (digitalRead(BUTTON_DOWN) == LOW) { 291 | doCommandDown(); 292 | showStatus(); 293 | } 294 | 295 | if (isFreqChanged) 296 | { 297 | si5351.set_freq(vfoFreq, (si5351_clock) currentClock); 298 | currentOutputClock[currentClock] = vfoFreq; 299 | showStatus(); 300 | isFreqChanged = false; 301 | } 302 | delay(50); 303 | } 304 | -------------------------------------------------------------------------------- /source/si5351_signal_generator2/Rotary.cpp: -------------------------------------------------------------------------------- 1 | /* Rotary encoder handler for arduino. v1.1 2 | 3 | Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. 4 | Contact: bb@cactii.net 5 | 6 | A typical mechanical rotary encoder emits a two bit gray code 7 | on 3 output pins. Every step in the output (often accompanied 8 | by a physical 'click') generates a specific sequence of output 9 | codes on the pins. 10 | 11 | There are 3 pins used for the rotary encoding - one common and 12 | two 'bit' pins. 13 | 14 | The following is the typical sequence of code on the output when 15 | moving from one step to the next: 16 | 17 | Position Bit1 Bit2 18 | ---------------------- 19 | Step1 0 0 20 | 1/4 1 0 21 | 1/2 1 1 22 | 3/4 0 1 23 | Step2 0 0 24 | 25 | From this table, we can see that when moving from one 'click' to 26 | the next, there are 4 changes in the output code. 27 | 28 | - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. 29 | - Then both bits are high, halfway through the step. 30 | - Then Bit1 goes low, but Bit2 stays high. 31 | - Finally at the end of the step, both bits return to 0. 32 | 33 | Detecting the direction is easy - the table simply goes in the other 34 | direction (read up instead of down). 35 | 36 | To decode this, we use a simple state machine. Every time the output 37 | code changes, it follows state, until finally a full steps worth of 38 | code is received (in the correct order). At the final 0-0, it returns 39 | a value indicating a step in one direction or the other. 40 | 41 | It's also possible to use 'half-step' mode. This just emits an event 42 | at both the 0-0 and 1-1 positions. This might be useful for some 43 | encoders where you want to detect all positions. 44 | 45 | If an invalid state happens (for example we go from '0-1' straight 46 | to '1-0'), the state machine resets to the start until 0-0 and the 47 | next valid codes occur. 48 | 49 | The biggest advantage of using a state machine over other algorithms 50 | is that this has inherent debounce built in. Other algorithms emit spurious 51 | output with switch bounce, but this one will simply flip between 52 | sub-states until the bounce settles, then continue along the state 53 | machine. 54 | A side effect of debounce is that fast rotations can cause steps to 55 | be skipped. By not requiring debounce, fast rotations can be accurately 56 | measured. 57 | Another advantage is the ability to properly handle bad state, such 58 | as due to EMI, etc. 59 | It is also a lot simpler than others - a static state table and less 60 | than 10 lines of logic. */ 61 | 62 | #include "Arduino.h" 63 | #include "Rotary.h" 64 | 65 | /* The below state table has, for each state (row), the new state 66 | to set based on the next encoder output. From left to right in, 67 | the table, the encoder outputs are 00, 01, 10, 11, and the value 68 | in that position is the new state to set. */ 69 | 70 | #define R_START 0x0 71 | #ifdef HALF_STEP 72 | // Use the half-step state table (emits a code at 00 and 11) 73 | #define R_CCW_BEGIN 0x1 74 | #define R_CW_BEGIN 0x2 75 | #define R_START_M 0x3 76 | #define R_CW_BEGIN_M 0x4 77 | #define R_CCW_BEGIN_M 0x5 78 | const unsigned char ttable[6][4] = { 79 | // R_START (00) 80 | {R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 81 | // R_CCW_BEGIN 82 | {R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, 83 | // R_CW_BEGIN 84 | {R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, 85 | // R_START_M (11) 86 | {R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, 87 | // R_CW_BEGIN_M 88 | {R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, 89 | // R_CCW_BEGIN_M 90 | {R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, 91 | }; 92 | #else 93 | // Use the full-step state table (emits a code at 00 only) 94 | #define R_CW_FINAL 0x1 95 | #define R_CW_BEGIN 0x2 96 | #define R_CW_NEXT 0x3 97 | #define R_CCW_BEGIN 0x4 98 | #define R_CCW_FINAL 0x5 99 | #define R_CCW_NEXT 0x6 100 | 101 | const unsigned char ttable[7][4] = { 102 | // R_START 103 | {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 104 | // R_CW_FINAL 105 | {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, 106 | // R_CW_BEGIN 107 | {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, 108 | // R_CW_NEXT 109 | {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, 110 | // R_CCW_BEGIN 111 | {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, 112 | // R_CCW_FINAL 113 | {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, 114 | // R_CCW_NEXT 115 | {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, 116 | }; 117 | #endif 118 | 119 | // Constructor. Each arg is the pin number for each encoder contact 120 | Rotary::Rotary(char _pin1, char _pin2) { 121 | // Assign variables 122 | pin1 = _pin1; 123 | pin2 = _pin2; 124 | // Set pins to input. 125 | pinMode(pin1, INPUT); 126 | pinMode(pin2, INPUT); 127 | #ifdef ENABLE_PULLUPS 128 | digitalWrite(pin1, HIGH); 129 | digitalWrite(pin2, HIGH); 130 | #endif 131 | // Initialise state 132 | state = R_START; 133 | } 134 | 135 | unsigned char Rotary::process() { 136 | // Grab state of input pins 137 | unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1); 138 | // Determine new state from the pins and state table 139 | state = ttable[state & 0xf][pinstate]; 140 | // Return emit bits, ie the generated event 141 | return state & 0x30; 142 | } 143 | 144 | -------------------------------------------------------------------------------- /source/si5351_signal_generator2/Rotary.h: -------------------------------------------------------------------------------- 1 | // Rotary encoder library for Arduino. 2 | #include "Arduino.h" 3 | 4 | #ifndef rotary_h 5 | #define rotary_h 6 | 7 | // Enable this to emit codes twice per step 8 | // #define HALF_STEP 9 | 10 | #define ENABLE_PULLUPS // Enable weak pullups 11 | 12 | // Values returned by 'process' 13 | #define DIR_NONE 0x0 // No complete step yet 14 | #define DIR_CW 0x10 // Clockwise step 15 | #define DIR_CCW 0x20 // Anti-clockwise step 16 | 17 | class Rotary 18 | { 19 | public: 20 | Rotary(char, char); 21 | // Process pin(s) 22 | unsigned char process(); 23 | private: 24 | unsigned char state; 25 | unsigned char pin1; 26 | unsigned char pin2; 27 | }; 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /source/si5351_signal_generator2/si5351_signal_generator2.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | 4 | Under construction ..... 5 | 6 | It is a multipurpose signal generator controlled by Arduino. This project uses the SI5351 from Silicon Labs. 7 | This sketch is configured to control the SI5351 from 32.768KHz to 160MHz and steps from 1Hz to 1MHz. 8 | 9 | With this sketch you can control the three clock outputs. 10 | 11 | For SI5351 control, this sketch uses the the Etherkit/si5351 Arduino library from Jason Milldrum (https://github.com/etherkit/Si5351Arduino); 12 | For encoder control, this sketch uses the Rotary Encoder Class implementation from Ben Buxton (the source code is included together with this sketch) 13 | 14 | Arduino Pro Mini 3.3V (8MHz) and SI5351 breakout wire up 15 | 16 | | Device name | Device Pin / Description | Arduino Pin | 17 | | ---------------- | -------------------- | ------------ | 18 | | OLED | | | 19 | | | SDA | A4 | 20 | | | SCL | A5 | 21 | | SI5351 | | | 22 | | | SDIO (pin 18) | A4 | 23 | | | SCLK (pin 17) | A5 | 24 | | Buttons | | | 25 | | | SWITCH_CMD | 4 | 26 | | | BUTTON_UP | 6 | 27 | | | BUTTON_DOWN | 5 | 28 | | Encoder | | | 29 | | | A | 3 | 30 | | | B | 2 | 31 | 32 | See https://github.com/etherkit/Si5351Arduino and know how to calibrate your Si5351 33 | Author: Ricardo Lima Caratti 2020 34 | 35 | */ 36 | 37 | #include "./si5351wire.h" 38 | #include 39 | #include "Rotary.h" 40 | #include "SSD1306Ascii.h" 41 | #include "SSD1306AsciiAvrI2c.h" 42 | 43 | // Enconder PINs 44 | #define ENCODER_PIN_A 3 // Encoder pin A 45 | #define ENCODER_PIN_B 2 // Encoder pin B 46 | #define SWITCH_CMD 4 // ENCODER button or regular push button 47 | #define BUTTON_UP 6 // Button up command 48 | #define BUTTON_DOWN 5 // Button down command 49 | 50 | #define CMD_STEP 0 51 | #define CMD_FAVORITE 1 52 | #define CMD_CLK 2 53 | 54 | // OLED Diaplay constants 55 | #define I2C_ADDRESS 0x3C 56 | #define RST_PIN -1 // Define proper RST_PIN if required. 57 | 58 | // Change this value bellow (CORRECTION_FACTOR) to 0 if you do not know the correction factor of your Si5351A. 59 | #define CORRECTION_FACTOR -13500 // See how to calibrate your Si5351A (0 if you do not want). 60 | // #define CORRECTION_FACTOR 0 61 | 62 | #define MIN_VFO 3276800LLU 63 | #define MAX_VFO 16000000000LLU // VFO max. frequency 30MHz 64 | 65 | // Struct for step 66 | typedef struct 67 | { 68 | char *name; // step label: 50Hz, 10Hz, 500Hz and 100KHz 69 | long value; // Frequency value (unit 0.01Hz See documentation) to increment or decrement 70 | } Step; 71 | 72 | // Steps database. You can change the Steps and numbers of steps here if you need. 73 | Step step[] = { 74 | {(char *)"1Hz", 100}, // Minimum Frequency step (incremente or decrement) 1Hz 75 | {(char *)"10Hz", 1000}, 76 | {(char *)"100Hz", 10000}, 77 | {(char *)"500Hz", 50000}, 78 | {(char *)"1KHz", 100000}, 79 | {(char *)"5KHz", 500000}, 80 | {(char *)"10KHz", 1000000}, 81 | {(char *)"32.768K", 3276800}, 82 | {(char *)"50KHz", 5000000}, 83 | {(char *)"500KHz", 50000000}, 84 | {(char *)"1MHz", 100000000}}; // Maximum frequency step 500KHz 85 | 86 | // Calculate the index of last position of step[] array 87 | const int lastStepVFO = (sizeof step / sizeof(Step)) - 1; 88 | int currentStep = 4; 89 | 90 | // Your favotite frequencies 91 | uint64_t favorite[] = {3276800LLU, 350000000LLU, 710000000LLU, 800000000LLU, 1070000000LLU, 1200000000LLU, 92 | 1350000000LLU, 1600000000LLU, 2000000000LLU, 2400000000LLU, 2700000000LLU, 2800000000LLU, 93 | 3200000000LLU,4600000000LLU, 5000000000LLU, 10000000000LLU, 13300000000LLU, 14400000000LLU}; 94 | const int lastFavorite = (sizeof favorite / sizeof(uint64_t) ) - 1; 95 | int currentFavorite = 3; 96 | 97 | uint64_t currentOutputClock[]{1070000000LLU, 1350000000LLU, 3276800LLU}; // Stores the current clock on CLK0, CLK1 and CLK2 98 | const int lastCurrentOutputClock = (sizeof currentOutputClock / sizeof(uint64_t)) - 1; 99 | 100 | 101 | int currentClock = 0; // current clock output 0 102 | 103 | int currentCommand = CMD_STEP; 104 | 105 | char *cmd[] = {(char *) "Step", (char *) "Favorite", (char *) "Out. Clk" }; 106 | char *clk[] = {(char *) "CLK0", (char *) "CLK1", (char *) "CLK2" }; 107 | 108 | 109 | // Encoder controller 110 | Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B); 111 | 112 | // OLED - Declaration for a SSD1306 display connected to I2C (SDA, SCL pins) 113 | SSD1306AsciiAvrI2c display; 114 | 115 | // The Si5351 instance. 116 | Si5351 si5351; 117 | 118 | bool isFreqChanged = true; 119 | bool clearDisplay = false; 120 | 121 | uint64_t vfoFreq; 122 | uint64_t vfoLastValue; 123 | 124 | // Encoder control variables 125 | volatile int encoderCount = 0; 126 | 127 | void setup() 128 | { 129 | // Encoder pins 130 | pinMode(ENCODER_PIN_A, INPUT_PULLUP); 131 | pinMode(ENCODER_PIN_B, INPUT_PULLUP); 132 | // Si5351 contrtolers pins 133 | 134 | pinMode(SWITCH_CMD, INPUT_PULLUP); 135 | pinMode(BUTTON_UP, INPUT_PULLUP); 136 | pinMode(BUTTON_DOWN, INPUT_PULLUP); 137 | 138 | // Initiating the OLED Display 139 | display.begin(&Adafruit128x64, I2C_ADDRESS); 140 | display.setFont(Adafruit5x7); 141 | display.set1X(); 142 | display.clear(); 143 | display.setCursor(17, 0); 144 | display.print("Multipurpose"); 145 | display.setCursor(33, 1); 146 | display.print("Signal"); 147 | display.setCursor(23, 2); 148 | display.print("Generator"); 149 | display.setCursor(22, 5); 150 | display.print("BY PU2CLR"); 151 | delay(4000); 152 | display.clear(); 153 | 154 | vfoFreq = favorite[currentFavorite]; 155 | currentOutputClock[currentClock] = vfoFreq; 156 | 157 | showStatus(); 158 | // Initiating the Signal Generator (si5351) 159 | si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); 160 | // Adjusting the frequency (see how to calibrate the Si5351 - example si5351_calibration.ino) 161 | si5351.set_correction(CORRECTION_FACTOR, SI5351_PLL_INPUT_XO); 162 | si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); 163 | si5351.set_freq(vfoFreq, (si5351_clock) currentClock); // Start CLK0 (VFO) 164 | 165 | // Disable CLK 1 and 2 outputs 166 | si5351.output_enable(SI5351_CLK1, 0); 167 | si5351.output_enable(SI5351_CLK2, 0); 168 | si5351.update_status(); 169 | 170 | delay(500); 171 | 172 | // Encoder interrupt 173 | attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); 174 | attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE); 175 | 176 | delay(1000); 177 | } 178 | 179 | // Use Rotary.h and Rotary.cpp implementation to process encoder via interrupt 180 | void rotaryEncoder() 181 | { // rotary encoder events 182 | uint8_t encoderStatus = encoder.process(); 183 | if (encoderStatus) 184 | { 185 | if (encoderStatus == DIR_CW) 186 | { 187 | encoderCount = 1; 188 | } 189 | else 190 | { 191 | encoderCount = -1; 192 | } 193 | } 194 | } 195 | 196 | // Show Signal Generator Information 197 | // Verificar setCursor() em https://github.com/greiman/SSD1306Ascii/issues/53 198 | void showStatus() 199 | { 200 | char aux[15]; 201 | double vfo = vfoFreq / 100000.0; 202 | 203 | dtostrf(vfo,5,3,aux); 204 | 205 | display.setCursor(0, 0); 206 | display.set2X(); 207 | display.setCursor(0, 0); 208 | display.print(" "); 209 | display.setCursor(0, 0); 210 | display.print(aux); 211 | display.setCursor(95,2); 212 | display.set1X(); 213 | display.print("KHz"); 214 | display.setCursor(0, 4); 215 | display.print("Step: "); 216 | display.print(step[currentStep].name); 217 | display.print(" - "); 218 | display.print(clk[currentClock]); 219 | display.setCursor(0, 6); 220 | display.print("Command: "); 221 | display.print(cmd[currentCommand]); 222 | } 223 | 224 | // Change the frequency (increment or decrement) 225 | // direction parameter is 1 (clockwise) or -1 (counter-clockwise) 226 | void changeFreq(int direction) 227 | { 228 | vfoFreq += step[currentStep].value * direction; // currentStep * direction; 229 | // Check the VFO limits 230 | if (vfoFreq > MAX_VFO ) 231 | vfoFreq = MIN_VFO; 232 | else if (vfoFreq < MIN_VFO) 233 | vfoFreq = MAX_VFO; 234 | 235 | isFreqChanged = true; 236 | } 237 | 238 | void doCommandUp() { 239 | display.clear(); 240 | if ( currentCommand == CMD_STEP ) 241 | currentStep = (currentStep < lastStepVFO) ? (currentStep + 1) : 0; 242 | else if (currentCommand == CMD_FAVORITE ) 243 | { 244 | currentFavorite = (currentFavorite < lastFavorite) ? (currentFavorite + 1) : 0; 245 | vfoFreq = favorite[currentFavorite]; 246 | currentOutputClock[currentClock] = vfoFreq; 247 | isFreqChanged = true; 248 | } else { 249 | currentClock = (currentClock < lastCurrentOutputClock) ? (currentClock + 1) : 0; 250 | vfoFreq = currentOutputClock[currentClock]; 251 | } 252 | delay(200); 253 | } 254 | 255 | void doCommandDown() { 256 | display.clear(); 257 | if ( currentCommand == CMD_STEP ) 258 | currentStep = (currentStep > 0) ? (currentStep - 1) : lastStepVFO; 259 | else if (currentCommand == CMD_FAVORITE) 260 | { 261 | currentFavorite = (currentFavorite > 0) ? (currentFavorite - 1) : lastFavorite; 262 | vfoFreq = favorite[currentFavorite]; 263 | currentOutputClock[currentClock] = vfoFreq; 264 | isFreqChanged = true; 265 | } else { 266 | currentClock = (currentClock > 0) ? (currentClock - 1) : lastCurrentOutputClock; 267 | vfoFreq = currentOutputClock[currentClock]; 268 | } 269 | delay(200); 270 | } 271 | 272 | void loop() 273 | { 274 | // Check if the encoder has moved. 275 | if (encoderCount != 0) 276 | { 277 | if (encoderCount == 1) 278 | changeFreq(1); 279 | else 280 | changeFreq(-1); 281 | encoderCount = 0; 282 | } 283 | 284 | // Switch the current command control (step or favorite frequencies) 285 | if (digitalRead(SWITCH_CMD) == LOW) { 286 | display.clear(); 287 | currentCommand = (currentCommand >= 2)? 0: (currentCommand + 1);; 288 | showStatus(); 289 | delay(200); 290 | } else if (digitalRead(BUTTON_UP) == LOW) { 291 | doCommandUp(); 292 | showStatus(); 293 | } else if (digitalRead(BUTTON_DOWN) == LOW) { 294 | doCommandDown(); 295 | showStatus(); 296 | } 297 | 298 | if (isFreqChanged) 299 | { 300 | si5351.set_freq(vfoFreq, (si5351_clock) currentClock); 301 | currentOutputClock[currentClock] = vfoFreq; 302 | showStatus(); 303 | isFreqChanged = false; 304 | } 305 | delay(50); 306 | } 307 | -------------------------------------------------------------------------------- /source/si5351_signal_generator2/si5351wire.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * si5351wire.cpp - Si5351wire library for Arduino 3 | * 4 | * Copyright (C) 2015 - 2016 Jason Milldrum 5 | * Dana H. Myers 6 | * 7 | * Some tuning algorithms derived from clk-si5351wire.c in the Linux kernel. 8 | * Sebastian Hesselbarth 9 | * Rabeeh Khoury 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | #include 26 | 27 | #include "Arduino.h" 28 | #include "Wire.h" 29 | #include "si5351wire.h" 30 | 31 | 32 | /********************/ 33 | /* Public functions */ 34 | /********************/ 35 | 36 | Si5351wire::Si5351wire(uint8_t i2c_addr, uint8_t i2c_sda, uint8_t i2c_scl) 37 | { 38 | i2c_bus_addr = i2c_addr; 39 | xtal_freq[0] = SI5351wire_XTAL_FREQ; 40 | Wire.begin(i2c_sda, i2c_scl); 41 | 42 | // Start by using XO ref osc as default for each PLL 43 | plla_ref_osc = SI5351wire_PLL_INPUT_XO; 44 | pllb_ref_osc = SI5351wire_PLL_INPUT_XO; 45 | clkin_div = SI5351wire_CLKIN_DIV_1; 46 | } 47 | 48 | /* 49 | * init(uint8_t xtal_load_c, uint32_t ref_osc_freq, int32_t corr) 50 | * 51 | * Setup communications to the Si5351wire and set the crystal 52 | * load capacitance. 53 | * 54 | * xtal_load_c - Crystal load capacitance. Use the SI5351wire_CRYSTAL_LOAD_*PF 55 | * defines in the header file 56 | * xo_freq - Crystal/reference oscillator frequency in 1 Hz increments. 57 | * Defaults to 25000000 if a 0 is used here. 58 | * corr - Frequency correction constant in parts-per-billion 59 | * 60 | * Returns a boolean that indicates whether a device was found on the desired 61 | * i2c.i2c address. 62 | * 63 | */ 64 | 65 | 66 | bool Si5351wire::init(uint8_t xtal_load_c, uint32_t xo_freq, int32_t corr) 67 | { 68 | // Start I2C comms 69 | Wire.begin(); 70 | 71 | // Check for a device on the bus, bail out if it is not there 72 | Wire.beginTransmission(i2c_bus_addr); 73 | Wire.write(0); 74 | uint8_t reg_val; 75 | reg_val = Wire.endTransmission(); 76 | 77 | if(reg_val == 0) 78 | { 79 | // Wait for SYS_INIT flag to be clear, indicating that device is ready 80 | uint8_t status_reg = 0; 81 | 82 | do 83 | { 84 | status_reg = si5351wire_read(SI5351wire_DEVICE_STATUS); 85 | } while (status_reg >> 7 == 1); 86 | 87 | // Set crystal load capacitance 88 | si5351wire_write(SI5351wire_CRYSTAL_LOAD, (xtal_load_c & SI5351wire_CRYSTAL_LOAD_MASK) | 0b00010010); 89 | 90 | // Set up the XO reference frequency 91 | if (xo_freq != 0) 92 | { 93 | set_ref_freq(xo_freq, SI5351wire_PLL_INPUT_XO); 94 | } 95 | else 96 | { 97 | set_ref_freq(SI5351wire_XTAL_FREQ, SI5351wire_PLL_INPUT_XO); 98 | } 99 | 100 | // Set the frequency calibration for the XO 101 | set_correction(corr, SI5351wire_PLL_INPUT_XO); 102 | 103 | reset(); 104 | 105 | return true; 106 | } 107 | else 108 | { 109 | return false; 110 | } 111 | } 112 | 113 | /* 114 | * reset(void) 115 | * 116 | * Call to reset the Si5351wire to the state initialized by the library. 117 | * 118 | */ 119 | void Si5351wire::reset(void) 120 | { 121 | // Initialize the CLK outputs according to flowchart in datasheet 122 | // First, turn them off 123 | si5351wire_write(16, 0x80); 124 | si5351wire_write(17, 0x80); 125 | si5351wire_write(18, 0x80); 126 | si5351wire_write(19, 0x80); 127 | si5351wire_write(20, 0x80); 128 | si5351wire_write(21, 0x80); 129 | si5351wire_write(22, 0x80); 130 | si5351wire_write(23, 0x80); 131 | 132 | // Turn the clocks back on... 133 | si5351wire_write(16, 0x0c); 134 | si5351wire_write(17, 0x0c); 135 | si5351wire_write(18, 0x0c); 136 | si5351wire_write(19, 0x0c); 137 | si5351wire_write(20, 0x0c); 138 | si5351wire_write(21, 0x0c); 139 | si5351wire_write(22, 0x0c); 140 | si5351wire_write(23, 0x0c); 141 | 142 | // Set PLLA and PLLB to 800 MHz for automatic tuning 143 | set_pll(SI5351wire_PLL_FIXED, SI5351wire_PLLA); 144 | set_pll(SI5351wire_PLL_FIXED, SI5351wire_PLLB); 145 | 146 | // Make PLL to CLK assignments for automatic tuning 147 | pll_assignment[0] = SI5351wire_PLLA; 148 | pll_assignment[1] = SI5351wire_PLLB; 149 | pll_assignment[2] = SI5351wire_PLLB; 150 | pll_assignment[3] = SI5351wire_PLLA; 151 | pll_assignment[4] = SI5351wire_PLLA; 152 | pll_assignment[5] = SI5351wire_PLLA; 153 | pll_assignment[6] = SI5351wire_PLLB; 154 | pll_assignment[7] = SI5351wire_PLLB; 155 | 156 | set_ms_source(SI5351wire_CLK0, SI5351wire_PLLA); 157 | set_ms_source(SI5351wire_CLK1, SI5351wire_PLLB); 158 | set_ms_source(SI5351wire_CLK2, SI5351wire_PLLB); 159 | set_ms_source(SI5351wire_CLK3, SI5351wire_PLLA); 160 | set_ms_source(SI5351wire_CLK4, SI5351wire_PLLA); 161 | set_ms_source(SI5351wire_CLK5, SI5351wire_PLLA); 162 | set_ms_source(SI5351wire_CLK6, SI5351wire_PLLB); 163 | set_ms_source(SI5351wire_CLK7, SI5351wire_PLLB); 164 | 165 | // Reset the VCXO param 166 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_LOW, 0); 167 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_MID, 0); 168 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_HIGH, 0); 169 | 170 | // Then reset the PLLs 171 | pll_reset(SI5351wire_PLLA); 172 | pll_reset(SI5351wire_PLLB); 173 | 174 | // Set initial frequencies 175 | uint8_t i; 176 | for(i = 0; i < 8; i++) 177 | { 178 | clk_freq[i] = 0; 179 | output_enable((enum si5351wire_clock)i, 0); 180 | clk_first_set[i] = false; 181 | ms_reg_save[i].p1 = 0; 182 | ms_reg_save[i].p2 = 0; 183 | ms_reg_save[i].p3 = 0; 184 | } 185 | } 186 | 187 | /* 188 | * set_freq(uint64_t freq, enum si5351wire_clock clk) 189 | * 190 | * Sets the clock frequency of the specified CLK output. 191 | * Frequency range of 8 kHz to 150 MHz 192 | * 193 | * freq - Output frequency in Hz 194 | * clk - Clock output 195 | * (use the si5351wire_clock enum) 196 | */ 197 | uint8_t Si5351wire::set_freq(uint64_t freq, enum si5351wire_clock clk,uint8_t reset_pll) 198 | { 199 | struct Si5351wireRegSet ms_reg; 200 | uint64_t pll_freq; 201 | uint8_t int_mode = 0; 202 | uint8_t div_by_4 = 0; 203 | uint8_t r_div = 0; 204 | uint8_t ms_change = 0; 205 | 206 | // Check which Multisynth is being set 207 | if((uint8_t)clk <= (uint8_t)SI5351wire_CLK5) 208 | { 209 | // MS0 through MS5 logic 210 | // --------------------- 211 | 212 | // Lower bounds check 213 | if(freq > 0 && freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT) 214 | { 215 | freq = SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT; 216 | } 217 | 218 | // Upper bounds check 219 | if(freq > SI5351wire_MULTISYNTH_MAX_FREQ * SI5351wire_FREQ_MULT) 220 | { 221 | freq = SI5351wire_MULTISYNTH_MAX_FREQ * SI5351wire_FREQ_MULT; 222 | } 223 | 224 | // If requested freq >100 MHz and no other outputs are already >100 MHz, 225 | // we need to recalculate PLLA and then recalculate all other CLK outputs 226 | // on same PLL 227 | if(freq > (SI5351wire_MULTISYNTH_SHARE_MAX * SI5351wire_FREQ_MULT)) 228 | { 229 | // Check other clocks on same PLL 230 | uint8_t i; 231 | for(i = 0; i < 6; i++) 232 | { 233 | if(clk_freq[i] > (SI5351wire_MULTISYNTH_SHARE_MAX * SI5351wire_FREQ_MULT)) 234 | { 235 | if(i != (uint8_t)clk && pll_assignment[i] == pll_assignment[clk]) 236 | { 237 | return 1; // won't set if any other clks already >100 MHz 238 | } 239 | } 240 | } 241 | 242 | // Enable the output on first set_freq only 243 | if(clk_first_set[(uint8_t)clk] == false) 244 | { 245 | output_enable(clk, 1); 246 | clk_first_set[(uint8_t)clk] = true; 247 | reset_pll = 1; 248 | } 249 | 250 | // Set the freq in memory 251 | clk_freq[(uint8_t)clk] = freq; 252 | 253 | // Calculate the proper PLL frequency 254 | pll_freq = multisynth_calc(freq, 0, &ms_reg); 255 | 256 | // Set PLL 257 | set_pll(pll_freq, pll_assignment[clk]); 258 | 259 | // Recalculate params for all synths on same PLL 260 | for(i = 0; i < 6; i++) 261 | { 262 | if(clk_freq[i] != 0) 263 | { 264 | if(pll_assignment[i] == pll_assignment[clk]) 265 | { 266 | struct Si5351wireRegSet temp_reg; 267 | uint64_t temp_freq; 268 | 269 | // Select the proper R div value 270 | temp_freq = clk_freq[i]; 271 | r_div = select_r_div(&temp_freq); 272 | 273 | multisynth_calc(temp_freq, pll_freq, &temp_reg); 274 | if (ms_reg_save[i].p1 != temp_reg.p1) 275 | ms_change = 1; 276 | if (ms_reg_save[i].p2 != temp_reg.p2) 277 | ms_change = 1; 278 | if (ms_reg_save[i].p3 != temp_reg.p3) 279 | ms_change = 1; 280 | ms_reg_save[i] = temp_reg; 281 | 282 | // If freq > 150 MHz, we need to use DIVBY4 and integer mode 283 | if(temp_freq >= SI5351wire_MULTISYNTH_DIVBY4_FREQ * SI5351wire_FREQ_MULT) 284 | { 285 | div_by_4 = 1; 286 | int_mode = 1; 287 | } 288 | else 289 | { 290 | div_by_4 = 0; 291 | int_mode = 0; 292 | } 293 | 294 | // Set multisynth registers 295 | if (ms_change == 1) 296 | set_ms((enum si5351wire_clock)i, temp_reg, int_mode, r_div, div_by_4); 297 | } 298 | } 299 | } 300 | 301 | // Reset the PLL 302 | if (reset_pll) 303 | pll_reset(pll_assignment[clk]); 304 | } 305 | else 306 | { 307 | clk_freq[(uint8_t)clk] = freq; 308 | 309 | // Enable the output on first set_freq only 310 | if(clk_first_set[(uint8_t)clk] == false) 311 | { 312 | output_enable(clk, 1); 313 | clk_first_set[(uint8_t)clk] = true; 314 | } 315 | 316 | // Select the proper R div value 317 | r_div = select_r_div(&freq); 318 | 319 | // Calculate the synth parameters 320 | if(pll_assignment[clk] == SI5351wire_PLLA) 321 | { 322 | multisynth_calc(freq, plla_freq, &ms_reg); 323 | } 324 | else 325 | { 326 | multisynth_calc(freq, pllb_freq, &ms_reg); 327 | } 328 | 329 | // Set multisynth registers 330 | set_ms(clk, ms_reg, int_mode, r_div, div_by_4); 331 | } 332 | 333 | return 0; 334 | } 335 | else 336 | { 337 | // MS6 and MS7 logic 338 | // ----------------- 339 | 340 | // Lower bounds check 341 | if(freq > 0 && freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT) 342 | { 343 | freq = SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT; 344 | } 345 | 346 | // Upper bounds check 347 | if(freq >= SI5351wire_MULTISYNTH_DIVBY4_FREQ * SI5351wire_FREQ_MULT) 348 | { 349 | freq = SI5351wire_MULTISYNTH_DIVBY4_FREQ * SI5351wire_FREQ_MULT - 1; 350 | } 351 | 352 | // If one of CLK6 or CLK7 is already set when trying to set the other, 353 | // we have to ensure that it will also have an integer division ratio 354 | // with the same PLL, otherwise do not set it. 355 | if(clk == SI5351wire_CLK6) 356 | { 357 | if(clk_freq[7] != 0) 358 | { 359 | if(pllb_freq % freq == 0) 360 | { 361 | if((pllb_freq / freq) % 2 != 0) 362 | { 363 | // Not an even divide ratio, no bueno 364 | return 1; 365 | } 366 | else 367 | { 368 | // Set the freq in memory 369 | clk_freq[(uint8_t)clk] = freq; 370 | 371 | // Select the proper R div value 372 | r_div = select_r_div_ms67(&freq); 373 | 374 | multisynth67_calc(freq, pllb_freq, &ms_reg); 375 | } 376 | } 377 | else 378 | { 379 | // Not an integer divide ratio, no good 380 | return 1; 381 | } 382 | } 383 | else 384 | { 385 | // No previous assignment, so set PLLB based on CLK6 386 | 387 | // Set the freq in memory 388 | clk_freq[(uint8_t)clk] = freq; 389 | 390 | // Select the proper R div value 391 | r_div = select_r_div_ms67(&freq); 392 | 393 | pll_freq = multisynth67_calc(freq, 0, &ms_reg); 394 | //pllb_freq = pll_freq; 395 | set_pll(pll_freq, SI5351wire_PLLB); 396 | } 397 | } 398 | else 399 | { 400 | if(clk_freq[6] != 0) 401 | { 402 | if(pllb_freq % freq == 0) 403 | { 404 | if((pllb_freq / freq) % 2 != 0) 405 | { 406 | // Not an even divide ratio, no bueno 407 | return 1; 408 | } 409 | else 410 | { 411 | // Set the freq in memory 412 | clk_freq[(uint8_t)clk] = freq; 413 | 414 | // Select the proper R div value 415 | r_div = select_r_div_ms67(&freq); 416 | 417 | multisynth67_calc(freq, pllb_freq, &ms_reg); 418 | } 419 | } 420 | else 421 | { 422 | // Not an integer divide ratio, no good 423 | return 1; 424 | } 425 | } 426 | else 427 | { 428 | // No previous assignment, so set PLLB based on CLK7 429 | 430 | // Set the freq in memory 431 | clk_freq[(uint8_t)clk] = freq; 432 | 433 | // Select the proper R div value 434 | r_div = select_r_div_ms67(&freq); 435 | 436 | pll_freq = multisynth67_calc(freq, 0, &ms_reg); 437 | //pllb_freq = pll_freq; 438 | set_pll(pll_freq, pll_assignment[clk]); 439 | } 440 | } 441 | 442 | div_by_4 = 0; 443 | int_mode = 0; 444 | 445 | // Set multisynth registers (MS must be set before PLL) 446 | set_ms(clk, ms_reg, int_mode, r_div, div_by_4); 447 | 448 | return 0; 449 | } 450 | } 451 | 452 | /* 453 | * set_freq_manual(uint64_t freq, uint64_t pll_freq, enum si5351wire_clock clk) 454 | * 455 | * Sets the clock frequency of the specified CLK output using the given PLL 456 | * frequency. You must ensure that the MS is assigned to the correct PLL and 457 | * that the PLL is set to the correct frequency before using this method. 458 | * 459 | * It is important to note that if you use this method, you will have to 460 | * track that all settings are sane yourself. 461 | * 462 | * freq - Output frequency in Hz 463 | * pll_freq - Frequency of the PLL driving the Multisynth in Hz * 100 464 | * clk - Clock output 465 | * (use the si5351wire_clock enum) 466 | */ 467 | uint8_t Si5351wire::set_freq_manual(uint64_t freq, uint64_t pll_freq, enum si5351wire_clock clk) 468 | { 469 | struct Si5351wireRegSet ms_reg; 470 | uint8_t int_mode = 0; 471 | uint8_t div_by_4 = 0; 472 | 473 | // Lower bounds check 474 | if(freq > 0 && freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT) 475 | { 476 | freq = SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT; 477 | } 478 | 479 | // Upper bounds check 480 | if(freq > SI5351wire_CLKOUT_MAX_FREQ * SI5351wire_FREQ_MULT) 481 | { 482 | freq = SI5351wire_CLKOUT_MAX_FREQ * SI5351wire_FREQ_MULT; 483 | } 484 | 485 | uint8_t r_div; 486 | 487 | clk_freq[(uint8_t)clk] = freq; 488 | 489 | set_pll(pll_freq, pll_assignment[clk]); 490 | 491 | // Enable the output 492 | output_enable(clk, 1); 493 | 494 | // Select the proper R div value 495 | r_div = select_r_div(&freq); 496 | 497 | // Calculate the synth parameters 498 | multisynth_calc(freq, pll_freq, &ms_reg); 499 | 500 | // If freq > 150 MHz, we need to use DIVBY4 and integer mode 501 | if(freq >= SI5351wire_MULTISYNTH_DIVBY4_FREQ * SI5351wire_FREQ_MULT) 502 | { 503 | div_by_4 = 1; 504 | int_mode = 1; 505 | } 506 | 507 | // Set multisynth registers (MS must be set before PLL) 508 | set_ms(clk, ms_reg, int_mode, r_div, div_by_4); 509 | 510 | return 0; 511 | } 512 | 513 | /* 514 | * set_pll(uint64_t pll_freq, enum si5351wire_pll target_pll) 515 | * 516 | * Set the specified PLL to a specific oscillation frequency 517 | * 518 | * pll_freq - Desired PLL frequency in Hz * 100 519 | * target_pll - Which PLL to set 520 | * (use the si5351wire_pll enum) 521 | */ 522 | void Si5351wire::set_pll(uint64_t pll_freq, enum si5351wire_pll target_pll) 523 | { 524 | struct Si5351wireRegSet pll_reg; 525 | 526 | if(target_pll == SI5351wire_PLLA) 527 | { 528 | pll_calc(SI5351wire_PLLA, pll_freq, &pll_reg, ref_correction[plla_ref_osc], 0); 529 | } 530 | else 531 | { 532 | pll_calc(SI5351wire_PLLB, pll_freq, &pll_reg, ref_correction[pllb_ref_osc], 0); 533 | } 534 | 535 | // Derive the register values to write 536 | 537 | // Prepare an array for parameters to be written to 538 | uint8_t *params = new uint8_t[20]; 539 | uint8_t i = 0; 540 | uint8_t temp; 541 | 542 | // Registers 26-27 543 | temp = ((pll_reg.p3 >> 8) & 0xFF); 544 | params[i++] = temp; 545 | 546 | temp = (uint8_t)(pll_reg.p3 & 0xFF); 547 | params[i++] = temp; 548 | 549 | // Register 28 550 | temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03); 551 | params[i++] = temp; 552 | 553 | // Registers 29-30 554 | temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF); 555 | params[i++] = temp; 556 | 557 | temp = (uint8_t)(pll_reg.p1 & 0xFF); 558 | params[i++] = temp; 559 | 560 | // Register 31 561 | temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0); 562 | temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F); 563 | params[i++] = temp; 564 | 565 | // Registers 32-33 566 | temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF); 567 | params[i++] = temp; 568 | 569 | temp = (uint8_t)(pll_reg.p2 & 0xFF); 570 | params[i++] = temp; 571 | 572 | // Write the parameters 573 | if(target_pll == SI5351wire_PLLA) 574 | { 575 | si5351wire_write_bulk(SI5351wire_PLLA_PARAMETERS, i, params); 576 | plla_freq = pll_freq; 577 | } 578 | else if(target_pll == SI5351wire_PLLB) 579 | { 580 | si5351wire_write_bulk(SI5351wire_PLLB_PARAMETERS, i, params); 581 | pllb_freq = pll_freq; 582 | } 583 | 584 | delete params; 585 | } 586 | 587 | /* 588 | * set_ms(enum si5351wire_clock clk, struct Si5351wireRegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4) 589 | * 590 | * Set the specified multisynth parameters. Not normally needed, but public for advanced users. 591 | * 592 | * clk - Clock output 593 | * (use the si5351wire_clock enum) 594 | * int_mode - Set integer mode 595 | * Set to 1 to enable, 0 to disable 596 | * r_div - Desired r_div ratio 597 | * div_by_4 - Set Divide By 4 mode 598 | * Set to 1 to enable, 0 to disable 599 | */ 600 | void Si5351wire::set_ms(enum si5351wire_clock clk, struct Si5351wireRegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4) 601 | { 602 | uint8_t *params = new uint8_t[20]; 603 | uint8_t i = 0; 604 | uint8_t temp; 605 | uint8_t reg_val; 606 | 607 | 608 | if((uint8_t)clk <= (uint8_t)SI5351wire_CLK5) 609 | { 610 | // Registers 42-43 for CLK0 611 | temp = (uint8_t)((ms_reg.p3 >> 8) & 0xFF); 612 | params[i++] = temp; 613 | 614 | temp = (uint8_t)(ms_reg.p3 & 0xFF); 615 | params[i++] = temp; 616 | 617 | // Register 44 for CLK0 618 | reg_val = si5351wire_read((SI5351wire_CLK0_PARAMETERS + 2) + (clk * 8)); 619 | reg_val &= ~(0x03); 620 | temp = reg_val | ((uint8_t)((ms_reg.p1 >> 16) & 0x03)); 621 | params[i++] = temp; 622 | 623 | // Registers 45-46 for CLK0 624 | temp = (uint8_t)((ms_reg.p1 >> 8) & 0xFF); 625 | params[i++] = temp; 626 | 627 | temp = (uint8_t)(ms_reg.p1 & 0xFF); 628 | params[i++] = temp; 629 | 630 | // Register 47 for CLK0 631 | temp = (uint8_t)((ms_reg.p3 >> 12) & 0xF0); 632 | temp += (uint8_t)((ms_reg.p2 >> 16) & 0x0F); 633 | params[i++] = temp; 634 | 635 | // Registers 48-49 for CLK0 636 | temp = (uint8_t)((ms_reg.p2 >> 8) & 0xFF); 637 | params[i++] = temp; 638 | 639 | temp = (uint8_t)(ms_reg.p2 & 0xFF); 640 | params[i++] = temp; 641 | } 642 | else 643 | { 644 | // MS6 and MS7 only use one register 645 | temp = ms_reg.p1; 646 | } 647 | 648 | // Write the parameters 649 | switch(clk) 650 | { 651 | case SI5351wire_CLK0: 652 | si5351wire_write_bulk(SI5351wire_CLK0_PARAMETERS, i, params); 653 | set_int(clk, int_mode); 654 | ms_div(clk, r_div, div_by_4); 655 | break; 656 | case SI5351wire_CLK1: 657 | si5351wire_write_bulk(SI5351wire_CLK1_PARAMETERS, i, params); 658 | set_int(clk, int_mode); 659 | ms_div(clk, r_div, div_by_4); 660 | break; 661 | case SI5351wire_CLK2: 662 | si5351wire_write_bulk(SI5351wire_CLK2_PARAMETERS, i, params); 663 | set_int(clk, int_mode); 664 | ms_div(clk, r_div, div_by_4); 665 | break; 666 | case SI5351wire_CLK3: 667 | si5351wire_write_bulk(SI5351wire_CLK3_PARAMETERS, i, params); 668 | set_int(clk, int_mode); 669 | ms_div(clk, r_div, div_by_4); 670 | break; 671 | case SI5351wire_CLK4: 672 | si5351wire_write_bulk(SI5351wire_CLK4_PARAMETERS, i, params); 673 | set_int(clk, int_mode); 674 | ms_div(clk, r_div, div_by_4); 675 | break; 676 | case SI5351wire_CLK5: 677 | si5351wire_write_bulk(SI5351wire_CLK5_PARAMETERS, i, params); 678 | set_int(clk, int_mode); 679 | ms_div(clk, r_div, div_by_4); 680 | break; 681 | case SI5351wire_CLK6: 682 | si5351wire_write(SI5351wire_CLK6_PARAMETERS, temp); 683 | ms_div(clk, r_div, div_by_4); 684 | break; 685 | case SI5351wire_CLK7: 686 | si5351wire_write(SI5351wire_CLK7_PARAMETERS, temp); 687 | ms_div(clk, r_div, div_by_4); 688 | break; 689 | } 690 | 691 | delete params; 692 | } 693 | 694 | /* 695 | * output_enable(enum si5351wire_clock clk, uint8_t enable) 696 | * 697 | * Enable or disable a chosen output 698 | * clk - Clock output 699 | * (use the si5351wire_clock enum) 700 | * enable - Set to 1 to enable, 0 to disable 701 | */ 702 | void Si5351wire::output_enable(enum si5351wire_clock clk, uint8_t enable) 703 | { 704 | uint8_t reg_val; 705 | reg_val = si5351wire_read(SI5351wire_OUTPUT_ENABLE_CTRL); 706 | 707 | if(enable == 1) 708 | { 709 | reg_val &= ~(1<<(uint8_t)clk); 710 | } 711 | else 712 | { 713 | reg_val |= (1<<(uint8_t)clk); 714 | } 715 | si5351wire_write(SI5351wire_OUTPUT_ENABLE_CTRL, reg_val); 716 | } 717 | 718 | /* 719 | * drive_strength(enum si5351wire_clock clk, enum si5351wire_drive drive) 720 | * 721 | * Sets the drive strength of the specified clock output 722 | * 723 | * clk - Clock output 724 | * (use the si5351wire_clock enum) 725 | * drive - Desired drive level 726 | * (use the si5351wire_drive enum) 727 | */ 728 | void Si5351wire::drive_strength(enum si5351wire_clock clk, enum si5351wire_drive drive) 729 | { 730 | uint8_t reg_val; 731 | const uint8_t mask = 0x03; 732 | 733 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 734 | reg_val &= ~(mask); 735 | 736 | switch(drive) 737 | { 738 | case SI5351wire_DRIVE_2MA: 739 | reg_val |= 0x00; 740 | break; 741 | case SI5351wire_DRIVE_4MA: 742 | reg_val |= 0x01; 743 | break; 744 | case SI5351wire_DRIVE_6MA: 745 | reg_val |= 0x02; 746 | break; 747 | case SI5351wire_DRIVE_8MA: 748 | reg_val |= 0x03; 749 | break; 750 | default: 751 | break; 752 | } 753 | 754 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 755 | } 756 | 757 | /* 758 | * update_status(void) 759 | * 760 | * Call this to update the status structs, then access them 761 | * via the dev_status and dev_int_status global members. 762 | * 763 | * See the header file for the struct definitions. These 764 | * correspond to the flag names for registers 0 and 1 in 765 | * the Si5351wire datasheet. 766 | */ 767 | void Si5351wire::update_status(void) 768 | { 769 | update_sys_status(&dev_status); 770 | update_int_status(&dev_int_status); 771 | } 772 | 773 | /* 774 | * set_correction(int32_t corr, enum si5351wire_pll_input ref_osc) 775 | * 776 | * corr - Correction factor in ppb 777 | * ref_osc - Desired reference oscillator 778 | * (use the si5351wire_pll_input enum) 779 | * 780 | * Use this to set the oscillator correction factor. 781 | * This value is a signed 32-bit integer of the 782 | * parts-per-billion value that the actual oscillation 783 | * frequency deviates from the specified frequency. 784 | * 785 | * The frequency calibration is done as a one-time procedure. 786 | * Any desired test frequency within the normal range of the 787 | * Si5351wire should be set, then the actual output frequency 788 | * should be measured as accurately as possible. The 789 | * difference between the measured and specified frequencies 790 | * should be calculated in Hertz, then multiplied by 10 in 791 | * order to get the parts-per-billion value. 792 | * 793 | * Since the Si5351wire itself has an intrinsic 0 PPM error, this 794 | * correction factor is good across the entire tuning range of 795 | * the Si5351wire. Once this calibration is done accurately, it 796 | * should not have to be done again for the same Si5351wire and 797 | * crystal. 798 | */ 799 | void Si5351wire::set_correction(int32_t corr, enum si5351wire_pll_input ref_osc) 800 | { 801 | ref_correction[(uint8_t)ref_osc] = corr; 802 | 803 | // Recalculate and set PLL freqs based on correction value 804 | set_pll(plla_freq, SI5351wire_PLLA); 805 | set_pll(pllb_freq, SI5351wire_PLLB); 806 | } 807 | 808 | /* 809 | * set_phase(enum si5351wire_clock clk, uint8_t phase) 810 | * 811 | * clk - Clock output 812 | * (use the si5351wire_clock enum) 813 | * phase - 7-bit phase word 814 | * (in units of VCO/4 period) 815 | * 816 | * Write the 7-bit phase register. This must be used 817 | * with a user-set PLL frequency so that the user can 818 | * calculate the proper tuning word based on the PLL period. 819 | */ 820 | void Si5351wire::set_phase(enum si5351wire_clock clk, uint8_t phase) 821 | { 822 | // Mask off the upper bit since it is reserved 823 | phase = phase & 0b01111111; 824 | 825 | si5351wire_write(SI5351wire_CLK0_PHASE_OFFSET + (uint8_t)clk, phase); 826 | } 827 | 828 | /* 829 | * get_correction(enum si5351wire_pll_input ref_osc) 830 | * 831 | * ref_osc - Desired reference oscillator 832 | * 0: crystal oscillator (XO) 833 | * 1: external clock input (CLKIN) 834 | * 835 | * Returns the oscillator correction factor stored 836 | * in RAM. 837 | */ 838 | int32_t Si5351wire::get_correction(enum si5351wire_pll_input ref_osc) 839 | { 840 | return ref_correction[(uint8_t)ref_osc]; 841 | } 842 | 843 | /* 844 | * pll_reset(enum si5351wire_pll target_pll) 845 | * 846 | * target_pll - Which PLL to reset 847 | * (use the si5351wire_pll enum) 848 | * 849 | * Apply a reset to the indicated PLL. 850 | */ 851 | void Si5351wire::pll_reset(enum si5351wire_pll target_pll) 852 | { 853 | if(target_pll == SI5351wire_PLLA) 854 | { 855 | si5351wire_write(SI5351wire_PLL_RESET, SI5351wire_PLL_RESET_A); 856 | } 857 | else if(target_pll == SI5351wire_PLLB) 858 | { 859 | si5351wire_write(SI5351wire_PLL_RESET, SI5351wire_PLL_RESET_B); 860 | } 861 | } 862 | 863 | /* 864 | * set_ms_source(enum si5351wire_clock clk, enum si5351wire_pll pll) 865 | * 866 | * clk - Clock output 867 | * (use the si5351wire_clock enum) 868 | * pll - Which PLL to use as the source 869 | * (use the si5351wire_pll enum) 870 | * 871 | * Set the desired PLL source for a multisynth. 872 | */ 873 | void Si5351wire::set_ms_source(enum si5351wire_clock clk, enum si5351wire_pll pll) 874 | { 875 | uint8_t reg_val; 876 | 877 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 878 | 879 | if(pll == SI5351wire_PLLA) 880 | { 881 | reg_val &= ~(SI5351wire_CLK_PLL_SELECT); 882 | } 883 | else if(pll == SI5351wire_PLLB) 884 | { 885 | reg_val |= SI5351wire_CLK_PLL_SELECT; 886 | } 887 | 888 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 889 | 890 | pll_assignment[(uint8_t)clk] = pll; 891 | } 892 | 893 | /* 894 | * set_int(enum si5351wire_clock clk, uint8_t int_mode) 895 | * 896 | * clk - Clock output 897 | * (use the si5351wire_clock enum) 898 | * enable - Set to 1 to enable, 0 to disable 899 | * 900 | * Set the indicated multisynth into integer mode. 901 | */ 902 | void Si5351wire::set_int(enum si5351wire_clock clk, uint8_t enable) 903 | { 904 | uint8_t reg_val; 905 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 906 | 907 | if(enable == 1) 908 | { 909 | reg_val |= (SI5351wire_CLK_INTEGER_MODE); 910 | } 911 | else 912 | { 913 | reg_val &= ~(SI5351wire_CLK_INTEGER_MODE); 914 | } 915 | 916 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 917 | 918 | // Integer mode indication 919 | /* 920 | switch(clk) 921 | { 922 | case SI5351wire_CLK0: 923 | clk0_int_mode = enable; 924 | break; 925 | case SI5351wire_CLK1: 926 | clk1_int_mode = enable; 927 | break; 928 | case SI5351wire_CLK2: 929 | clk2_int_mode = enable; 930 | break; 931 | default: 932 | break; 933 | } 934 | */ 935 | } 936 | 937 | /* 938 | * set_clock_pwr(enum si5351wire_clock clk, uint8_t pwr) 939 | * 940 | * clk - Clock output 941 | * (use the si5351wire_clock enum) 942 | * pwr - Set to 1 to enable, 0 to disable 943 | * 944 | * Enable or disable power to a clock output (a power 945 | * saving feature). 946 | */ 947 | void Si5351wire::set_clock_pwr(enum si5351wire_clock clk, uint8_t pwr) 948 | { 949 | uint8_t reg_val; //, reg; 950 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 951 | 952 | if(pwr == 1) 953 | { 954 | reg_val &= 0b01111111; 955 | } 956 | else 957 | { 958 | reg_val |= 0b10000000; 959 | } 960 | 961 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 962 | } 963 | 964 | /* 965 | * set_clock_invert(enum si5351wire_clock clk, uint8_t inv) 966 | * 967 | * clk - Clock output 968 | * (use the si5351wire_clock enum) 969 | * inv - Set to 1 to enable, 0 to disable 970 | * 971 | * Enable to invert the clock output waveform. 972 | */ 973 | void Si5351wire::set_clock_invert(enum si5351wire_clock clk, uint8_t inv) 974 | { 975 | uint8_t reg_val; 976 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 977 | 978 | if(inv == 1) 979 | { 980 | reg_val |= (SI5351wire_CLK_INVERT); 981 | } 982 | else 983 | { 984 | reg_val &= ~(SI5351wire_CLK_INVERT); 985 | } 986 | 987 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 988 | } 989 | 990 | /* 991 | * set_clock_source(enum si5351wire_clock clk, enum si5351wire_clock_source src) 992 | * 993 | * clk - Clock output 994 | * (use the si5351wire_clock enum) 995 | * src - Which clock source to use for the multisynth 996 | * (use the si5351wire_clock_source enum) 997 | * 998 | * Set the clock source for a multisynth (based on the options 999 | * presented for Registers 16-23 in the Silicon Labs AN619 document). 1000 | * Choices are XTAL, CLKIN, MS0, or the multisynth associated with 1001 | * the clock output. 1002 | */ 1003 | void Si5351wire::set_clock_source(enum si5351wire_clock clk, enum si5351wire_clock_source src) 1004 | { 1005 | uint8_t reg_val; 1006 | reg_val = si5351wire_read(SI5351wire_CLK0_CTRL + (uint8_t)clk); 1007 | 1008 | // Clear the bits first 1009 | reg_val &= ~(SI5351wire_CLK_INPUT_MASK); 1010 | 1011 | switch(src) 1012 | { 1013 | case SI5351wire_CLK_SRC_XTAL: 1014 | reg_val |= (SI5351wire_CLK_INPUT_XTAL); 1015 | break; 1016 | case SI5351wire_CLK_SRC_CLKIN: 1017 | reg_val |= (SI5351wire_CLK_INPUT_CLKIN); 1018 | break; 1019 | case SI5351wire_CLK_SRC_MS0: 1020 | if(clk == SI5351wire_CLK0) 1021 | { 1022 | return; 1023 | } 1024 | 1025 | reg_val |= (SI5351wire_CLK_INPUT_MULTISYNTH_0_4); 1026 | break; 1027 | case SI5351wire_CLK_SRC_MS: 1028 | reg_val |= (SI5351wire_CLK_INPUT_MULTISYNTH_N); 1029 | break; 1030 | default: 1031 | return; 1032 | } 1033 | 1034 | si5351wire_write(SI5351wire_CLK0_CTRL + (uint8_t)clk, reg_val); 1035 | } 1036 | 1037 | /* 1038 | * set_clock_disable(enum si5351wire_clock clk, enum si5351wire_clock_disable dis_state) 1039 | * 1040 | * clk - Clock output 1041 | * (use the si5351wire_clock enum) 1042 | * dis_state - Desired state of the output upon disable 1043 | * (use the si5351wire_clock_disable enum) 1044 | * 1045 | * Set the state of the clock output when it is disabled. Per page 27 1046 | * of AN619 (Registers 24 and 25), there are four possible values: low, 1047 | * high, high impedance, and never disabled. 1048 | */ 1049 | void Si5351wire::set_clock_disable(enum si5351wire_clock clk, enum si5351wire_clock_disable dis_state) 1050 | { 1051 | uint8_t reg_val, reg; 1052 | 1053 | if (clk >= SI5351wire_CLK0 && clk <= SI5351wire_CLK3) 1054 | { 1055 | reg = SI5351wire_CLK3_0_DISABLE_STATE; 1056 | } 1057 | else if(clk >= SI5351wire_CLK4 && clk <= SI5351wire_CLK7) 1058 | { 1059 | reg = SI5351wire_CLK7_4_DISABLE_STATE; 1060 | } 1061 | 1062 | reg_val = si5351wire_read(reg); 1063 | 1064 | if (clk >= SI5351wire_CLK0 && clk <= SI5351wire_CLK3) 1065 | { 1066 | reg_val &= ~(0b11 << (clk * 2)); 1067 | reg_val |= dis_state << (clk * 2); 1068 | } 1069 | else if(clk >= SI5351wire_CLK4 && clk <= SI5351wire_CLK7) 1070 | { 1071 | reg_val &= ~(0b11 << ((clk - 4) * 2)); 1072 | reg_val |= dis_state << ((clk - 4) * 2); 1073 | } 1074 | 1075 | si5351wire_write(reg, reg_val); 1076 | } 1077 | 1078 | /* 1079 | * set_clock_fanout(enum si5351wire_clock_fanout fanout, uint8_t enable) 1080 | * 1081 | * fanout - Desired clock fanout 1082 | * (use the si5351wire_clock_fanout enum) 1083 | * enable - Set to 1 to enable, 0 to disable 1084 | * 1085 | * Use this function to enable or disable the clock fanout options 1086 | * for individual clock outputs. If you intend to output the XO or 1087 | * CLKIN on the clock outputs, enable this first. 1088 | * 1089 | * By default, only the Multisynth fanout is enabled at startup. 1090 | */ 1091 | void Si5351wire::set_clock_fanout(enum si5351wire_clock_fanout fanout, uint8_t enable) 1092 | { 1093 | uint8_t reg_val; 1094 | reg_val = si5351wire_read(SI5351wire_FANOUT_ENABLE); 1095 | 1096 | switch(fanout) 1097 | { 1098 | case SI5351wire_FANOUT_CLKIN: 1099 | if(enable) 1100 | { 1101 | reg_val |= SI5351wire_CLKIN_ENABLE; 1102 | } 1103 | else 1104 | { 1105 | reg_val &= ~(SI5351wire_CLKIN_ENABLE); 1106 | } 1107 | break; 1108 | case SI5351wire_FANOUT_XO: 1109 | if(enable) 1110 | { 1111 | reg_val |= SI5351wire_XTAL_ENABLE; 1112 | } 1113 | else 1114 | { 1115 | reg_val &= ~(SI5351wire_XTAL_ENABLE); 1116 | } 1117 | break; 1118 | case SI5351wire_FANOUT_MS: 1119 | if(enable) 1120 | { 1121 | reg_val |= SI5351wire_MULTISYNTH_ENABLE; 1122 | } 1123 | else 1124 | { 1125 | reg_val &= ~(SI5351wire_MULTISYNTH_ENABLE); 1126 | } 1127 | break; 1128 | } 1129 | 1130 | si5351wire_write(SI5351wire_FANOUT_ENABLE, reg_val); 1131 | } 1132 | 1133 | /* 1134 | * set_pll_input(enum si5351wire_pll pll, enum si5351wire_pll_input input) 1135 | * 1136 | * pll - Which PLL to use as the source 1137 | * (use the si5351wire_pll enum) 1138 | * input - Which reference oscillator to use as PLL input 1139 | * (use the si5351wire_pll_input enum) 1140 | * 1141 | * Set the desired reference oscillator source for the given PLL. 1142 | */ 1143 | void Si5351wire::set_pll_input(enum si5351wire_pll pll, enum si5351wire_pll_input input) 1144 | { 1145 | uint8_t reg_val; 1146 | reg_val = si5351wire_read(SI5351wire_PLL_INPUT_SOURCE); 1147 | 1148 | // Clear the bits first 1149 | //reg_val &= ~(SI5351wire_CLKIN_DIV_MASK); 1150 | 1151 | switch(pll) 1152 | { 1153 | case SI5351wire_PLLA: 1154 | if(input == SI5351wire_PLL_INPUT_CLKIN) 1155 | { 1156 | reg_val |= SI5351wire_PLLA_SOURCE; 1157 | reg_val |= clkin_div; 1158 | plla_ref_osc = SI5351wire_PLL_INPUT_CLKIN; 1159 | } 1160 | else 1161 | { 1162 | reg_val &= ~(SI5351wire_PLLA_SOURCE); 1163 | plla_ref_osc = SI5351wire_PLL_INPUT_XO; 1164 | } 1165 | break; 1166 | case SI5351wire_PLLB: 1167 | if(input == SI5351wire_PLL_INPUT_CLKIN) 1168 | { 1169 | reg_val |= SI5351wire_PLLB_SOURCE; 1170 | reg_val |= clkin_div; 1171 | pllb_ref_osc = SI5351wire_PLL_INPUT_CLKIN; 1172 | } 1173 | else 1174 | { 1175 | reg_val &= ~(SI5351wire_PLLB_SOURCE); 1176 | pllb_ref_osc = SI5351wire_PLL_INPUT_XO; 1177 | } 1178 | break; 1179 | default: 1180 | return; 1181 | } 1182 | 1183 | si5351wire_write(SI5351wire_PLL_INPUT_SOURCE, reg_val); 1184 | 1185 | set_pll(plla_freq, SI5351wire_PLLA); 1186 | set_pll(pllb_freq, SI5351wire_PLLB); 1187 | } 1188 | 1189 | /* 1190 | * set_vcxo(uint64_t pll_freq, uint8_t ppm) 1191 | * 1192 | * pll_freq - Desired PLL base frequency in Hz * 100 1193 | * ppm - VCXO pull limit in ppm 1194 | * 1195 | * Set the parameters for the VCXO on the Si5351wireB. 1196 | */ 1197 | void Si5351wire::set_vcxo(uint64_t pll_freq, uint8_t ppm) 1198 | { 1199 | struct Si5351wireRegSet pll_reg; 1200 | uint64_t vcxo_param; 1201 | 1202 | // Bounds check 1203 | if(ppm < SI5351wire_VCXO_PULL_MIN) 1204 | { 1205 | ppm = SI5351wire_VCXO_PULL_MIN; 1206 | } 1207 | 1208 | if(ppm > SI5351wire_VCXO_PULL_MAX) 1209 | { 1210 | ppm = SI5351wire_VCXO_PULL_MAX; 1211 | } 1212 | 1213 | // Set PLLB params 1214 | vcxo_param = pll_calc(SI5351wire_PLLB, pll_freq, &pll_reg, ref_correction[pllb_ref_osc], 1); 1215 | 1216 | // Derive the register values to write 1217 | 1218 | // Prepare an array for parameters to be written to 1219 | uint8_t *params = new uint8_t[20]; 1220 | uint8_t i = 0; 1221 | uint8_t temp; 1222 | 1223 | // Registers 26-27 1224 | temp = ((pll_reg.p3 >> 8) & 0xFF); 1225 | params[i++] = temp; 1226 | 1227 | temp = (uint8_t)(pll_reg.p3 & 0xFF); 1228 | params[i++] = temp; 1229 | 1230 | // Register 28 1231 | temp = (uint8_t)((pll_reg.p1 >> 16) & 0x03); 1232 | params[i++] = temp; 1233 | 1234 | // Registers 29-30 1235 | temp = (uint8_t)((pll_reg.p1 >> 8) & 0xFF); 1236 | params[i++] = temp; 1237 | 1238 | temp = (uint8_t)(pll_reg.p1 & 0xFF); 1239 | params[i++] = temp; 1240 | 1241 | // Register 31 1242 | temp = (uint8_t)((pll_reg.p3 >> 12) & 0xF0); 1243 | temp += (uint8_t)((pll_reg.p2 >> 16) & 0x0F); 1244 | params[i++] = temp; 1245 | 1246 | // Registers 32-33 1247 | temp = (uint8_t)((pll_reg.p2 >> 8) & 0xFF); 1248 | params[i++] = temp; 1249 | 1250 | temp = (uint8_t)(pll_reg.p2 & 0xFF); 1251 | params[i++] = temp; 1252 | 1253 | // Write the parameters 1254 | si5351wire_write_bulk(SI5351wire_PLLB_PARAMETERS, i, params); 1255 | 1256 | delete params; 1257 | 1258 | // Write the VCXO parameters 1259 | vcxo_param = ((vcxo_param * ppm * SI5351wire_VCXO_MARGIN) / 100ULL) / 1000000ULL; 1260 | 1261 | temp = (uint8_t)(vcxo_param & 0xFF); 1262 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_LOW, temp); 1263 | 1264 | temp = (uint8_t)((vcxo_param >> 8) & 0xFF); 1265 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_MID, temp); 1266 | 1267 | temp = (uint8_t)((vcxo_param >> 16) & 0x3F); 1268 | si5351wire_write(SI5351wire_VXCO_PARAMETERS_HIGH, temp); 1269 | } 1270 | 1271 | /* 1272 | * set_ref_freq(uint32_t ref_freq, enum si5351wire_pll_input ref_osc) 1273 | * 1274 | * ref_freq - Reference oscillator frequency in Hz 1275 | * ref_osc - Which reference oscillator frequency to set 1276 | * (use the si5351wire_pll_input enum) 1277 | * 1278 | * Set the reference frequency value for the desired reference oscillator 1279 | */ 1280 | void Si5351wire::set_ref_freq(uint32_t ref_freq, enum si5351wire_pll_input ref_osc) 1281 | { 1282 | // uint8_t reg_val; 1283 | //reg_val = si5351wire_read(SI5351wire_PLL_INPUT_SOURCE); 1284 | 1285 | // Clear the bits first 1286 | //reg_val &= ~(SI5351wire_CLKIN_DIV_MASK); 1287 | 1288 | if(ref_freq <= 35000000UL) 1289 | { 1290 | xtal_freq[(uint8_t)ref_osc] = ref_freq; 1291 | //reg_val |= SI5351wire_CLKIN_DIV_1; 1292 | if(ref_osc == SI5351wire_PLL_INPUT_CLKIN) 1293 | { 1294 | clkin_div = SI5351wire_CLKIN_DIV_1; 1295 | } 1296 | } 1297 | else if(ref_freq > 35000000UL && ref_freq <= 60000000UL) 1298 | { 1299 | xtal_freq[(uint8_t)ref_osc] = ref_freq / 2; 1300 | //reg_val |= SI5351wire_CLKIN_DIV_2; 1301 | if(ref_osc == SI5351wire_PLL_INPUT_CLKIN) 1302 | { 1303 | clkin_div = SI5351wire_CLKIN_DIV_2; 1304 | } 1305 | } 1306 | else if(ref_freq > 60000000UL && ref_freq <= 100000000UL) 1307 | { 1308 | xtal_freq[(uint8_t)ref_osc] = ref_freq / 4; 1309 | //reg_val |= SI5351wire_CLKIN_DIV_4; 1310 | if(ref_osc == SI5351wire_PLL_INPUT_CLKIN) 1311 | { 1312 | clkin_div = SI5351wire_CLKIN_DIV_4; 1313 | } 1314 | } 1315 | else 1316 | { 1317 | //reg_val |= SI5351wire_CLKIN_DIV_1; 1318 | } 1319 | 1320 | //si5351wire_write(SI5351wire_PLL_INPUT_SOURCE, reg_val); 1321 | } 1322 | 1323 | uint8_t Si5351wire::si5351wire_write_bulk(uint8_t addr, uint8_t bytes, uint8_t *data) 1324 | { 1325 | Wire.beginTransmission(i2c_bus_addr); 1326 | Wire.write(addr); 1327 | for(int i = 0; i < bytes; i++) 1328 | { 1329 | Wire.write(data[i]); 1330 | } 1331 | return Wire.endTransmission(); 1332 | 1333 | } 1334 | 1335 | 1336 | uint8_t Si5351wire::si5351wire_write(uint8_t addr, uint8_t data) 1337 | { 1338 | Wire.beginTransmission(i2c_bus_addr); 1339 | //Serial.print("Write:"); 1340 | //Serial.print(i2c_bus_addr,HEX); 1341 | //Serial.print("/"); 1342 | //Serial.print(addr,HEX); 1343 | //Serial.print("/"); 1344 | //Serial.println(data,HEX); 1345 | Wire.write(addr); 1346 | Wire.write(data); 1347 | return Wire.endTransmission(); 1348 | } 1349 | 1350 | //uint8_t Si5351wire::i2c_receive_byte_data(uint8_t reg ) 1351 | uint8_t Si5351wire::si5351wire_read(uint8_t reg ) 1352 | { 1353 | Wire.beginTransmission(i2c_bus_addr); 1354 | Wire.write(reg); 1355 | Wire.endTransmission(); 1356 | Wire.requestFrom(i2c_bus_addr,1,1); 1357 | uint8_t regVal = Wire.read(); // read data 1358 | 1359 | //Serial.print("Read:"); 1360 | //Serial.print(i2c_bus_addr,HEX); 1361 | //Serial.print("/"); 1362 | //Serial.print(reg,HEX); 1363 | //Serial.print("/"); 1364 | //Serial.println(regVal,HEX); 1365 | 1366 | // i2c_stop_condition(); 1367 | return regVal; // return zero if NACKed 1368 | } 1369 | 1370 | // uint8_t Si5351wire::si5351wire_read(uint8_t addr) 1371 | // { 1372 | // uint8_t reg_val = 0; 1373 | 1374 | // Wire.beginTransmission(i2c_bus_addr); 1375 | // Wire.write(addr); 1376 | // Wire.endTransmission(); 1377 | 1378 | // Wire.requestFrom(i2c_bus_addr, (uint8_t)1, (uint8_t)false); 1379 | 1380 | // while(Wire.available()) 1381 | // { 1382 | // reg_val = Wire.read(); 1383 | // } 1384 | 1385 | // return reg_val; 1386 | // } 1387 | 1388 | /*********************/ 1389 | /* Private functions */ 1390 | /*********************/ 1391 | 1392 | uint64_t Si5351wire::pll_calc(enum si5351wire_pll pll, uint64_t freq, struct Si5351wireRegSet *reg, int32_t correction, uint8_t vcxo) 1393 | { 1394 | uint64_t ref_freq; 1395 | if(pll == SI5351wire_PLLA) 1396 | { 1397 | ref_freq = xtal_freq[(uint8_t)plla_ref_osc] * SI5351wire_FREQ_MULT; 1398 | } 1399 | else 1400 | { 1401 | ref_freq = xtal_freq[(uint8_t)pllb_ref_osc] * SI5351wire_FREQ_MULT; 1402 | } 1403 | //ref_freq = 15974400ULL * SI5351wire_FREQ_MULT; 1404 | uint32_t a, b, c, p1, p2, p3; 1405 | uint64_t lltmp; //, denom; 1406 | 1407 | // Factor calibration value into nominal crystal frequency 1408 | // Measured in parts-per-billion 1409 | 1410 | ref_freq = ref_freq + (int32_t)((((((int64_t)correction) << 31) / 1000000000LL) * ref_freq) >> 31); 1411 | 1412 | // PLL bounds checking 1413 | if (freq < SI5351wire_PLL_VCO_MIN * SI5351wire_FREQ_MULT) 1414 | { 1415 | freq = SI5351wire_PLL_VCO_MIN * SI5351wire_FREQ_MULT; 1416 | } 1417 | if (freq > SI5351wire_PLL_VCO_MAX * SI5351wire_FREQ_MULT) 1418 | { 1419 | freq = SI5351wire_PLL_VCO_MAX * SI5351wire_FREQ_MULT; 1420 | } 1421 | 1422 | // Determine integer part of feedback equation 1423 | a = freq / ref_freq; 1424 | 1425 | if (a < SI5351wire_PLL_A_MIN) 1426 | { 1427 | freq = ref_freq * SI5351wire_PLL_A_MIN; 1428 | } 1429 | if (a > SI5351wire_PLL_A_MAX) 1430 | { 1431 | freq = ref_freq * SI5351wire_PLL_A_MAX; 1432 | } 1433 | 1434 | // Find best approximation for b/c = fVCO mod fIN 1435 | // denom = 1000ULL * 1000ULL; 1436 | // lltmp = freq % ref_freq; 1437 | // lltmp *= denom; 1438 | // do_div(lltmp, ref_freq); 1439 | 1440 | //b = (((uint64_t)(freq % ref_freq)) * RFRAC_DENOM) / ref_freq; 1441 | if(vcxo) 1442 | { 1443 | b = (((uint64_t)(freq % ref_freq)) * 1000000ULL) / ref_freq; 1444 | c = 1000000ULL; 1445 | } 1446 | else 1447 | { 1448 | b = (((uint64_t)(freq % ref_freq)) * RFRAC_DENOM) / ref_freq; 1449 | c = b ? RFRAC_DENOM : 1; 1450 | } 1451 | 1452 | // Calculate parameters 1453 | p1 = 128 * a + ((128 * b) / c) - 512; 1454 | p2 = 128 * b - c * ((128 * b) / c); 1455 | p3 = c; 1456 | 1457 | // Recalculate frequency as fIN * (a + b/c) 1458 | lltmp = ref_freq; 1459 | lltmp *= b; 1460 | do_div(lltmp, c); 1461 | freq = lltmp; 1462 | freq += ref_freq * a; 1463 | 1464 | reg->p1 = p1; 1465 | reg->p2 = p2; 1466 | reg->p3 = p3; 1467 | 1468 | if(vcxo) 1469 | { 1470 | return (uint64_t)(128 * a * 1000000ULL + b); 1471 | } 1472 | else 1473 | { 1474 | return freq; 1475 | } 1476 | } 1477 | 1478 | uint64_t Si5351wire::multisynth_calc(uint64_t freq, uint64_t pll_freq, struct Si5351wireRegSet *reg) 1479 | { 1480 | uint64_t lltmp; 1481 | uint32_t a, b, c, p1, p2, p3; 1482 | uint8_t divby4 = 0; 1483 | uint8_t ret_val = 0; 1484 | 1485 | // Multisynth bounds checking 1486 | if (freq > SI5351wire_MULTISYNTH_MAX_FREQ * SI5351wire_FREQ_MULT) 1487 | { 1488 | freq = SI5351wire_MULTISYNTH_MAX_FREQ * SI5351wire_FREQ_MULT; 1489 | } 1490 | if (freq < SI5351wire_MULTISYNTH_MIN_FREQ * SI5351wire_FREQ_MULT) 1491 | { 1492 | freq = SI5351wire_MULTISYNTH_MIN_FREQ * SI5351wire_FREQ_MULT; 1493 | } 1494 | 1495 | if (freq >= SI5351wire_MULTISYNTH_DIVBY4_FREQ * SI5351wire_FREQ_MULT) 1496 | { 1497 | divby4 = 1; 1498 | } 1499 | 1500 | if(pll_freq == 0) 1501 | { 1502 | // Find largest integer divider for max 1503 | // VCO frequency and given target frequency 1504 | if(divby4 == 0) 1505 | { 1506 | lltmp = SI5351wire_PLL_VCO_MAX * SI5351wire_FREQ_MULT; // margin needed? 1507 | do_div(lltmp, freq); 1508 | if(lltmp == 5) 1509 | { 1510 | lltmp = 4; 1511 | } 1512 | else if(lltmp == 7) 1513 | { 1514 | lltmp = 6; 1515 | } 1516 | a = (uint32_t)lltmp; 1517 | } 1518 | else 1519 | { 1520 | a = 4; 1521 | } 1522 | 1523 | b = 0; 1524 | c = 1; 1525 | pll_freq = a * freq; 1526 | } 1527 | else 1528 | { 1529 | // Preset PLL, so return the actual freq for these params instead of PLL freq 1530 | ret_val = 1; 1531 | 1532 | // Determine integer part of feedback equation 1533 | a = pll_freq / freq; 1534 | 1535 | if (a < SI5351wire_MULTISYNTH_A_MIN) 1536 | { 1537 | freq = pll_freq / SI5351wire_MULTISYNTH_A_MIN; 1538 | } 1539 | if (a > SI5351wire_MULTISYNTH_A_MAX) 1540 | { 1541 | freq = pll_freq / SI5351wire_MULTISYNTH_A_MAX; 1542 | } 1543 | 1544 | b = (pll_freq % freq * RFRAC_DENOM) / freq; 1545 | c = b ? RFRAC_DENOM : 1; 1546 | } 1547 | 1548 | // Calculate parameters 1549 | if (divby4 == 1) 1550 | { 1551 | p3 = 1; 1552 | p2 = 0; 1553 | p1 = 0; 1554 | } 1555 | else 1556 | { 1557 | p1 = 128 * a + ((128 * b) / c) - 512; 1558 | p2 = 128 * b - c * ((128 * b) / c); 1559 | p3 = c; 1560 | } 1561 | /* 1562 | //Serial.print("Devider :"); 1563 | //Serial.println(a); 1564 | 1565 | //Serial.print("b :"); 1566 | //Serial.println(b); 1567 | 1568 | //Serial.print("c :"); 1569 | //Serial.println(c); 1570 | 1571 | //Serial.print("p1 :"); 1572 | //Serial.println(p1); 1573 | //Serial.print("p2 :"); 1574 | //Serial.println(p2); 1575 | //Serial.print("p3 :"); 1576 | //Serial.println(p3); 1577 | */ 1578 | 1579 | reg->p1 = p1; 1580 | reg->p2 = p2; 1581 | reg->p3 = p3; 1582 | 1583 | if(ret_val == 0) 1584 | { 1585 | return pll_freq; 1586 | } 1587 | else 1588 | { 1589 | return freq; 1590 | } 1591 | } 1592 | 1593 | uint64_t Si5351wire::multisynth67_calc(uint64_t freq, uint64_t pll_freq, struct Si5351wireRegSet *reg) 1594 | { 1595 | //uint8_t p1; 1596 | // uint8_t ret_val = 0; 1597 | uint32_t a; 1598 | uint64_t lltmp; 1599 | 1600 | // Multisynth bounds checking 1601 | if(freq > SI5351wire_MULTISYNTH67_MAX_FREQ * SI5351wire_FREQ_MULT) 1602 | { 1603 | freq = SI5351wire_MULTISYNTH67_MAX_FREQ * SI5351wire_FREQ_MULT; 1604 | } 1605 | if(freq < SI5351wire_MULTISYNTH_MIN_FREQ * SI5351wire_FREQ_MULT) 1606 | { 1607 | freq = SI5351wire_MULTISYNTH_MIN_FREQ * SI5351wire_FREQ_MULT; 1608 | } 1609 | 1610 | if(pll_freq == 0) 1611 | { 1612 | // Find largest integer divider for max 1613 | // VCO frequency and given target frequency 1614 | lltmp = (SI5351wire_PLL_VCO_MAX * SI5351wire_FREQ_MULT) - 100000000UL; // margin needed? 1615 | do_div(lltmp, freq); 1616 | a = (uint32_t)lltmp; 1617 | 1618 | // Divisor has to be even 1619 | if(a % 2 != 0) 1620 | { 1621 | a++; 1622 | } 1623 | 1624 | // Divisor bounds check 1625 | if(a < SI5351wire_MULTISYNTH_A_MIN) 1626 | { 1627 | a = SI5351wire_MULTISYNTH_A_MIN; 1628 | } 1629 | if(a > SI5351wire_MULTISYNTH67_A_MAX) 1630 | { 1631 | a = SI5351wire_MULTISYNTH67_A_MAX; 1632 | } 1633 | 1634 | pll_freq = a * freq; 1635 | 1636 | // PLL bounds checking 1637 | if(pll_freq > (SI5351wire_PLL_VCO_MAX * SI5351wire_FREQ_MULT)) 1638 | { 1639 | a -= 2; 1640 | pll_freq = a * freq; 1641 | } 1642 | else if(pll_freq < (SI5351wire_PLL_VCO_MIN * SI5351wire_FREQ_MULT)) 1643 | { 1644 | a += 2; 1645 | pll_freq = a * freq; 1646 | } 1647 | 1648 | reg->p1 = (uint8_t)a; 1649 | reg->p2 = 0; 1650 | reg->p3 = 0; 1651 | return pll_freq; 1652 | } 1653 | else 1654 | { 1655 | // Multisynth frequency must be integer division of PLL 1656 | if(pll_freq % freq) 1657 | { 1658 | // No good 1659 | return 0; 1660 | } 1661 | else 1662 | { 1663 | a = pll_freq / freq; 1664 | 1665 | // Division ratio bounds check 1666 | if(a < SI5351wire_MULTISYNTH_A_MIN || a > SI5351wire_MULTISYNTH67_A_MAX) 1667 | { 1668 | // No bueno 1669 | return 0; 1670 | } 1671 | else 1672 | { 1673 | reg->p1 = (uint8_t)a; 1674 | reg->p2 = 0; 1675 | reg->p3 = 0; 1676 | return 1; 1677 | } 1678 | } 1679 | } 1680 | } 1681 | 1682 | void Si5351wire::update_sys_status(struct Si5351wireStatus *status) 1683 | { 1684 | uint8_t reg_val = 0; 1685 | 1686 | reg_val = si5351wire_read(SI5351wire_DEVICE_STATUS); 1687 | 1688 | // Parse the register 1689 | status->SYS_INIT = (reg_val >> 7) & 0x01; 1690 | status->LOL_B = (reg_val >> 6) & 0x01; 1691 | status->LOL_A = (reg_val >> 5) & 0x01; 1692 | status->LOS = (reg_val >> 4) & 0x01; 1693 | status->REVID = reg_val & 0x03; 1694 | } 1695 | 1696 | void Si5351wire::update_int_status(struct Si5351wireIntStatus *int_status) 1697 | { 1698 | uint8_t reg_val = 0; 1699 | 1700 | reg_val = si5351wire_read(SI5351wire_INTERRUPT_STATUS); 1701 | 1702 | // Parse the register 1703 | int_status->SYS_INIT_STKY = (reg_val >> 7) & 0x01; 1704 | int_status->LOL_B_STKY = (reg_val >> 6) & 0x01; 1705 | int_status->LOL_A_STKY = (reg_val >> 5) & 0x01; 1706 | int_status->LOS_STKY = (reg_val >> 4) & 0x01; 1707 | } 1708 | 1709 | void Si5351wire::ms_div(enum si5351wire_clock clk, uint8_t r_div, uint8_t div_by_4) 1710 | { 1711 | uint8_t reg_val = 0; 1712 | uint8_t reg_addr = 0; 1713 | 1714 | switch(clk) 1715 | { 1716 | case SI5351wire_CLK0: 1717 | reg_addr = SI5351wire_CLK0_PARAMETERS + 2; 1718 | break; 1719 | case SI5351wire_CLK1: 1720 | reg_addr = SI5351wire_CLK1_PARAMETERS + 2; 1721 | break; 1722 | case SI5351wire_CLK2: 1723 | reg_addr = SI5351wire_CLK2_PARAMETERS + 2; 1724 | break; 1725 | case SI5351wire_CLK3: 1726 | reg_addr = SI5351wire_CLK3_PARAMETERS + 2; 1727 | break; 1728 | case SI5351wire_CLK4: 1729 | reg_addr = SI5351wire_CLK4_PARAMETERS + 2; 1730 | break; 1731 | case SI5351wire_CLK5: 1732 | reg_addr = SI5351wire_CLK5_PARAMETERS + 2; 1733 | break; 1734 | case SI5351wire_CLK6: 1735 | reg_addr = SI5351wire_CLK6_7_OUTPUT_DIVIDER; 1736 | break; 1737 | case SI5351wire_CLK7: 1738 | reg_addr = SI5351wire_CLK6_7_OUTPUT_DIVIDER; 1739 | break; 1740 | } 1741 | 1742 | reg_val = si5351wire_read(reg_addr); 1743 | 1744 | if(clk <= (uint8_t)SI5351wire_CLK5) 1745 | { 1746 | // Clear the relevant bits 1747 | reg_val &= ~(0x7c); 1748 | 1749 | if(div_by_4 == 0) 1750 | { 1751 | reg_val &= ~(SI5351wire_OUTPUT_CLK_DIVBY4); 1752 | } 1753 | else 1754 | { 1755 | reg_val |= (SI5351wire_OUTPUT_CLK_DIVBY4); 1756 | } 1757 | 1758 | reg_val |= (r_div << SI5351wire_OUTPUT_CLK_DIV_SHIFT); 1759 | } 1760 | else if(clk == SI5351wire_CLK6) 1761 | { 1762 | // Clear the relevant bits 1763 | reg_val &= ~(0x07); 1764 | 1765 | reg_val |= r_div; 1766 | } 1767 | else if(clk == SI5351wire_CLK7) 1768 | { 1769 | // Clear the relevant bits 1770 | reg_val &= ~(0x70); 1771 | 1772 | reg_val |= (r_div << SI5351wire_OUTPUT_CLK_DIV_SHIFT); 1773 | } 1774 | 1775 | si5351wire_write(reg_addr, reg_val); 1776 | } 1777 | 1778 | uint8_t Si5351wire::select_r_div(uint64_t *freq) 1779 | { 1780 | uint8_t r_div = SI5351wire_OUTPUT_CLK_DIV_1; 1781 | 1782 | // Choose the correct R divider 1783 | if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 2)) 1784 | { 1785 | r_div = SI5351wire_OUTPUT_CLK_DIV_128; 1786 | *freq *= 128ULL; 1787 | } 1788 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 2) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 4)) 1789 | { 1790 | r_div = SI5351wire_OUTPUT_CLK_DIV_64; 1791 | *freq *= 64ULL; 1792 | } 1793 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 4) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 8)) 1794 | { 1795 | r_div = SI5351wire_OUTPUT_CLK_DIV_32; 1796 | *freq *= 32ULL; 1797 | } 1798 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 8) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 16)) 1799 | { 1800 | r_div = SI5351wire_OUTPUT_CLK_DIV_16; 1801 | *freq *= 16ULL; 1802 | } 1803 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 16) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 32)) 1804 | { 1805 | r_div = SI5351wire_OUTPUT_CLK_DIV_8; 1806 | *freq *= 8ULL; 1807 | } 1808 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 32) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 64)) 1809 | { 1810 | r_div = SI5351wire_OUTPUT_CLK_DIV_4; 1811 | *freq *= 4ULL; 1812 | } 1813 | else if((*freq >= SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 64) && (*freq < SI5351wire_CLKOUT_MIN_FREQ * SI5351wire_FREQ_MULT * 128)) 1814 | { 1815 | r_div = SI5351wire_OUTPUT_CLK_DIV_2; 1816 | *freq *= 2ULL; 1817 | } 1818 | 1819 | return r_div; 1820 | } 1821 | 1822 | uint8_t Si5351wire::select_r_div_ms67(uint64_t *freq) 1823 | { 1824 | uint8_t r_div = SI5351wire_OUTPUT_CLK_DIV_1; 1825 | 1826 | // Choose the correct R divider 1827 | if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 2)) 1828 | { 1829 | r_div = SI5351wire_OUTPUT_CLK_DIV_128; 1830 | *freq *= 128ULL; 1831 | } 1832 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 2) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 4)) 1833 | { 1834 | r_div = SI5351wire_OUTPUT_CLK_DIV_64; 1835 | *freq *= 64ULL; 1836 | } 1837 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 4) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 8)) 1838 | { 1839 | r_div = SI5351wire_OUTPUT_CLK_DIV_32; 1840 | *freq *= 32ULL; 1841 | } 1842 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 8) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 16)) 1843 | { 1844 | r_div = SI5351wire_OUTPUT_CLK_DIV_16; 1845 | *freq *= 16ULL; 1846 | } 1847 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 16) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 32)) 1848 | { 1849 | r_div = SI5351wire_OUTPUT_CLK_DIV_8; 1850 | *freq *= 8ULL; 1851 | } 1852 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 32) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 64)) 1853 | { 1854 | r_div = SI5351wire_OUTPUT_CLK_DIV_4; 1855 | *freq *= 4ULL; 1856 | } 1857 | else if((*freq >= SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 64) && (*freq < SI5351wire_CLKOUT67_MIN_FREQ * SI5351wire_FREQ_MULT * 128)) 1858 | { 1859 | r_div = SI5351wire_OUTPUT_CLK_DIV_2; 1860 | *freq *= 2ULL; 1861 | } 1862 | 1863 | return r_div; 1864 | } 1865 | -------------------------------------------------------------------------------- /source/si5351_signal_generator2/si5351wire.h: -------------------------------------------------------------------------------- 1 | /* 2 | * si5351wire.h - Si5351wire library for Arduino 3 | * 4 | * Copyright (C) 2015 - 2016 Jason Milldrum 5 | * Dana H. Myers 6 | * 7 | * Many defines derived from clk-si5351wire.h in the Linux kernel. 8 | * Sebastian Hesselbarth 9 | * Rabeeh Khoury 10 | * 11 | * do_div() macro derived from /include/asm-generic/div64.h in 12 | * the Linux kernel. 13 | * Copyright (C) 2003 Bernardo Innocenti 14 | * 15 | * This program is free software: you can redistribute it and/or modify 16 | * it under the terms of the GNU General Public License as published by 17 | * the Free Software Foundation, either version 3 of the License, or 18 | * (at your option) any later version. 19 | * 20 | * This program is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * You should have received a copy of the GNU General Public License 26 | * along with this program. If not, see . 27 | */ 28 | 29 | #ifndef SI5351wire_H_ 30 | #define SI5351wire_H_ 31 | 32 | #include "Arduino.h" 33 | #include 34 | #include 35 | 36 | /* Define definitions */ 37 | #define SI_SDA 21 38 | #define SI_SCL 22 39 | 40 | #define SI5351wire_BUS_BASE_ADDR 0x60 41 | #define SI5351wire_XTAL_FREQ 25000000 42 | #define SI5351wire_PLL_FIXED 80000000000ULL 43 | #define SI5351wire_FREQ_MULT 100ULL 44 | #define SI5351wire_DEFAULT_CLK 1000000000ULL 45 | 46 | #define SI5351wire_PLL_VCO_MIN 600000000 47 | #define SI5351wire_PLL_VCO_MAX 900000000 48 | #define SI5351wire_MULTISYNTH_MIN_FREQ 500000 49 | #define SI5351wire_MULTISYNTH_DIVBY4_FREQ 150000000 50 | #define SI5351wire_MULTISYNTH_MAX_FREQ 225000000 51 | #define SI5351wire_MULTISYNTH_SHARE_MAX 100000000 52 | #define SI5351wire_MULTISYNTH_SHARE_MIN 1024000 53 | #define SI5351wire_MULTISYNTH67_MAX_FREQ SI5351wire_MULTISYNTH_DIVBY4_FREQ 54 | #define SI5351wire_CLKOUT_MIN_FREQ 4000 55 | #define SI5351wire_CLKOUT_MAX_FREQ SI5351wire_MULTISYNTH_MAX_FREQ 56 | #define SI5351wire_CLKOUT67_MS_MIN SI5351wire_PLL_VCO_MIN / SI5351wire_MULTISYNTH67_A_MAX 57 | #define SI5351wire_CLKOUT67_MIN_FREQ SI5351wire_CLKOUT67_MS_MIN / 128 58 | #define SI5351wire_CLKOUT67_MAX_FREQ SI5351wire_MULTISYNTH67_MAX_FREQ 59 | 60 | #define SI5351wire_PLL_A_MIN 15 61 | #define SI5351wire_PLL_A_MAX 90 62 | #define SI5351wire_PLL_B_MAX (SI5351wire_PLL_C_MAX-1) 63 | #define SI5351wire_PLL_C_MAX 1048575 64 | #define SI5351wire_MULTISYNTH_A_MIN 6 65 | #define SI5351wire_MULTISYNTH_A_MAX 1800 66 | #define SI5351wire_MULTISYNTH67_A_MAX 254 67 | #define SI5351wire_MULTISYNTH_B_MAX (SI5351wire_MULTISYNTH_C_MAX-1) 68 | #define SI5351wire_MULTISYNTH_C_MAX 1048575 69 | #define SI5351wire_MULTISYNTH_P1_MAX ((1<<18)-1) 70 | #define SI5351wire_MULTISYNTH_P2_MAX ((1<<20)-1) 71 | #define SI5351wire_MULTISYNTH_P3_MAX ((1<<20)-1) 72 | #define SI5351wire_VCXO_PULL_MIN 30 73 | #define SI5351wire_VCXO_PULL_MAX 240 74 | #define SI5351wire_VCXO_MARGIN 103 75 | 76 | #define SI5351wire_DEVICE_STATUS 0 77 | #define SI5351wire_INTERRUPT_STATUS 1 78 | #define SI5351wire_INTERRUPT_MASK 2 79 | #define SI5351wire_STATUS_SYS_INIT (1<<7) 80 | #define SI5351wire_STATUS_LOL_B (1<<6) 81 | #define SI5351wire_STATUS_LOL_A (1<<5) 82 | #define SI5351wire_STATUS_LOS (1<<4) 83 | #define SI5351wire_OUTPUT_ENABLE_CTRL 3 84 | #define SI5351wire_OEB_PIN_ENABLE_CTRL 9 85 | #define SI5351wire_PLL_INPUT_SOURCE 15 86 | #define SI5351wire_CLKIN_DIV_MASK (3<<6) 87 | #define SI5351wire_CLKIN_DIV_1 (0<<6) 88 | #define SI5351wire_CLKIN_DIV_2 (1<<6) 89 | #define SI5351wire_CLKIN_DIV_4 (2<<6) 90 | #define SI5351wire_CLKIN_DIV_8 (3<<6) 91 | #define SI5351wire_PLLB_SOURCE (1<<3) 92 | #define SI5351wire_PLLA_SOURCE (1<<2) 93 | 94 | #define SI5351wire_CLK0_CTRL 16 95 | #define SI5351wire_CLK1_CTRL 17 96 | #define SI5351wire_CLK2_CTRL 18 97 | #define SI5351wire_CLK3_CTRL 19 98 | #define SI5351wire_CLK4_CTRL 20 99 | #define SI5351wire_CLK5_CTRL 21 100 | #define SI5351wire_CLK6_CTRL 22 101 | #define SI5351wire_CLK7_CTRL 23 102 | #define SI5351wire_CLK_POWERDOWN (1<<7) 103 | #define SI5351wire_CLK_INTEGER_MODE (1<<6) 104 | #define SI5351wire_CLK_PLL_SELECT (1<<5) 105 | #define SI5351wire_CLK_INVERT (1<<4) 106 | #define SI5351wire_CLK_INPUT_MASK (3<<2) 107 | #define SI5351wire_CLK_INPUT_XTAL (0<<2) 108 | #define SI5351wire_CLK_INPUT_CLKIN (1<<2) 109 | #define SI5351wire_CLK_INPUT_MULTISYNTH_0_4 (2<<2) 110 | #define SI5351wire_CLK_INPUT_MULTISYNTH_N (3<<2) 111 | #define SI5351wire_CLK_DRIVE_STRENGTH_MASK (3<<0) 112 | #define SI5351wire_CLK_DRIVE_STRENGTH_2MA (0<<0) 113 | #define SI5351wire_CLK_DRIVE_STRENGTH_4MA (1<<0) 114 | #define SI5351wire_CLK_DRIVE_STRENGTH_6MA (2<<0) 115 | #define SI5351wire_CLK_DRIVE_STRENGTH_8MA (3<<0) 116 | 117 | #define SI5351wire_CLK3_0_DISABLE_STATE 24 118 | #define SI5351wire_CLK7_4_DISABLE_STATE 25 119 | #define SI5351wire_CLK_DISABLE_STATE_MASK 3 120 | #define SI5351wire_CLK_DISABLE_STATE_LOW 0 121 | #define SI5351wire_CLK_DISABLE_STATE_HIGH 1 122 | #define SI5351wire_CLK_DISABLE_STATE_FLOAT 2 123 | #define SI5351wire_CLK_DISABLE_STATE_NEVER 3 124 | 125 | #define SI5351wire_PARAMETERS_LENGTH 8 126 | #define SI5351wire_PLLA_PARAMETERS 26 127 | #define SI5351wire_PLLB_PARAMETERS 34 128 | #define SI5351wire_CLK0_PARAMETERS 42 129 | #define SI5351wire_CLK1_PARAMETERS 50 130 | #define SI5351wire_CLK2_PARAMETERS 58 131 | #define SI5351wire_CLK3_PARAMETERS 66 132 | #define SI5351wire_CLK4_PARAMETERS 74 133 | #define SI5351wire_CLK5_PARAMETERS 82 134 | #define SI5351wire_CLK6_PARAMETERS 90 135 | #define SI5351wire_CLK7_PARAMETERS 91 136 | #define SI5351wire_CLK6_7_OUTPUT_DIVIDER 92 137 | #define SI5351wire_OUTPUT_CLK_DIV_MASK (7 << 4) 138 | #define SI5351wire_OUTPUT_CLK6_DIV_MASK (7 << 0) 139 | #define SI5351wire_OUTPUT_CLK_DIV_SHIFT 4 140 | #define SI5351wire_OUTPUT_CLK_DIV6_SHIFT 0 141 | #define SI5351wire_OUTPUT_CLK_DIV_1 0 142 | #define SI5351wire_OUTPUT_CLK_DIV_2 1 143 | #define SI5351wire_OUTPUT_CLK_DIV_4 2 144 | #define SI5351wire_OUTPUT_CLK_DIV_8 3 145 | #define SI5351wire_OUTPUT_CLK_DIV_16 4 146 | #define SI5351wire_OUTPUT_CLK_DIV_32 5 147 | #define SI5351wire_OUTPUT_CLK_DIV_64 6 148 | #define SI5351wire_OUTPUT_CLK_DIV_128 7 149 | #define SI5351wire_OUTPUT_CLK_DIVBY4 (3<<2) 150 | 151 | #define SI5351wire_SSC_PARAM0 149 152 | #define SI5351wire_SSC_PARAM1 150 153 | #define SI5351wire_SSC_PARAM2 151 154 | #define SI5351wire_SSC_PARAM3 152 155 | #define SI5351wire_SSC_PARAM4 153 156 | #define SI5351wire_SSC_PARAM5 154 157 | #define SI5351wire_SSC_PARAM6 155 158 | #define SI5351wire_SSC_PARAM7 156 159 | #define SI5351wire_SSC_PARAM8 157 160 | #define SI5351wire_SSC_PARAM9 158 161 | #define SI5351wire_SSC_PARAM10 159 162 | #define SI5351wire_SSC_PARAM11 160 163 | #define SI5351wire_SSC_PARAM12 161 164 | 165 | #define SI5351wire_VXCO_PARAMETERS_LOW 162 166 | #define SI5351wire_VXCO_PARAMETERS_MID 163 167 | #define SI5351wire_VXCO_PARAMETERS_HIGH 164 168 | 169 | #define SI5351wire_CLK0_PHASE_OFFSET 165 170 | #define SI5351wire_CLK1_PHASE_OFFSET 166 171 | #define SI5351wire_CLK2_PHASE_OFFSET 167 172 | #define SI5351wire_CLK3_PHASE_OFFSET 168 173 | #define SI5351wire_CLK4_PHASE_OFFSET 169 174 | #define SI5351wire_CLK5_PHASE_OFFSET 170 175 | 176 | #define SI5351wire_PLL_RESET 177 177 | #define SI5351wire_PLL_RESET_B (1<<7) 178 | #define SI5351wire_PLL_RESET_A (1<<5) 179 | 180 | #define SI5351wire_CRYSTAL_LOAD 183 181 | #define SI5351wire_CRYSTAL_LOAD_MASK (3<<6) 182 | #define SI5351wire_CRYSTAL_LOAD_0PF (0<<6) 183 | #define SI5351wire_CRYSTAL_LOAD_6PF (1<<6) 184 | #define SI5351wire_CRYSTAL_LOAD_8PF (2<<6) 185 | #define SI5351wire_CRYSTAL_LOAD_10PF (3<<6) 186 | 187 | #define SI5351wire_FANOUT_ENABLE 187 188 | #define SI5351wire_CLKIN_ENABLE (1<<7) 189 | #define SI5351wire_XTAL_ENABLE (1<<6) 190 | #define SI5351wire_MULTISYNTH_ENABLE (1<<4) 191 | 192 | 193 | /* Macro definitions */ 194 | 195 | //#define RFRAC_DENOM ((1L << 20) - 1) 196 | #define RFRAC_DENOM 1000000ULL 197 | 198 | /* 199 | * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h 200 | * 201 | * The semantics of do_div() are: 202 | * 203 | * uint32_t do_div(uint64_t *n, uint32_t base) 204 | * { 205 | * uint32_t remainder = *n % base; 206 | * *n = *n / base; 207 | * return remainder; 208 | * } 209 | * 210 | * NOTE: macro parameter n is evaluated multiple times, 211 | * beware of side effects! 212 | */ 213 | 214 | # define do_div(n,base) ({ \ 215 | uint64_t __base = (base); \ 216 | uint64_t __rem; \ 217 | __rem = ((uint64_t)(n)) % __base; \ 218 | (n) = ((uint64_t)(n)) / __base; \ 219 | __rem; \ 220 | }) 221 | 222 | /* Enum definitions */ 223 | 224 | /* 225 | * enum si5351wire_variant - SiLabs Si5351wire chip variant 226 | * @SI5351wire_VARIANT_A: Si5351wireA (8 output clocks, XTAL input) 227 | * @SI5351wire_VARIANT_A3: Si5351wireA MSOP10 (3 output clocks, XTAL input) 228 | * @SI5351wire_VARIANT_B: Si5351wireB (8 output clocks, XTAL/VXCO input) 229 | * @SI5351wire_VARIANT_C: Si5351wireC (8 output clocks, XTAL/CLKIN input) 230 | */ 231 | /* 232 | enum si5351wire_variant { 233 | SI5351wire_VARIANT_A = 1, 234 | SI5351wire_VARIANT_A3 = 2, 235 | SI5351wire_VARIANT_B = 3, 236 | SI5351wire_VARIANT_C = 4, 237 | }; 238 | */ 239 | 240 | enum si5351wire_clock {SI5351wire_CLK0, SI5351wire_CLK1, SI5351wire_CLK2, SI5351wire_CLK3, 241 | SI5351wire_CLK4, SI5351wire_CLK5, SI5351wire_CLK6, SI5351wire_CLK7}; 242 | 243 | enum si5351wire_pll {SI5351wire_PLLA, SI5351wire_PLLB}; 244 | 245 | enum si5351wire_drive {SI5351wire_DRIVE_2MA, SI5351wire_DRIVE_4MA, SI5351wire_DRIVE_6MA, SI5351wire_DRIVE_8MA}; 246 | 247 | enum si5351wire_clock_source {SI5351wire_CLK_SRC_XTAL, SI5351wire_CLK_SRC_CLKIN, SI5351wire_CLK_SRC_MS0, SI5351wire_CLK_SRC_MS}; 248 | 249 | enum si5351wire_clock_disable {SI5351wire_CLK_DISABLE_LOW, SI5351wire_CLK_DISABLE_HIGH, SI5351wire_CLK_DISABLE_HI_Z, SI5351wire_CLK_DISABLE_NEVER}; 250 | 251 | enum si5351wire_clock_fanout {SI5351wire_FANOUT_CLKIN, SI5351wire_FANOUT_XO, SI5351wire_FANOUT_MS}; 252 | 253 | enum si5351wire_pll_input {SI5351wire_PLL_INPUT_XO, SI5351wire_PLL_INPUT_CLKIN}; 254 | 255 | /* Struct definitions */ 256 | 257 | struct Si5351wireRegSet 258 | { 259 | uint32_t p1; 260 | uint32_t p2; 261 | uint32_t p3; 262 | }; 263 | 264 | struct Si5351wireStatus 265 | { 266 | uint8_t SYS_INIT; 267 | uint8_t LOL_B; 268 | uint8_t LOL_A; 269 | uint8_t LOS; 270 | uint8_t REVID; 271 | }; 272 | 273 | struct Si5351wireIntStatus 274 | { 275 | uint8_t SYS_INIT_STKY; 276 | uint8_t LOL_B_STKY; 277 | uint8_t LOL_A_STKY; 278 | uint8_t LOS_STKY; 279 | }; 280 | 281 | class Si5351wire 282 | { 283 | public: 284 | Si5351wire(uint8_t i2c_addr = SI5351wire_BUS_BASE_ADDR,uint8_t i2c_sda = SI_SDA,uint8_t i2c_scl = SI_SCL); 285 | 286 | bool init(uint8_t, uint32_t, int32_t); 287 | void reset(void); 288 | uint8_t set_freq(uint64_t, enum si5351wire_clock, uint8_t reset_pll = 0); 289 | uint8_t set_freq_manual(uint64_t, uint64_t, enum si5351wire_clock); 290 | void set_pll(uint64_t, enum si5351wire_pll); 291 | void set_ms(enum si5351wire_clock, struct Si5351wireRegSet, uint8_t, uint8_t, uint8_t); 292 | void output_enable(enum si5351wire_clock, uint8_t); 293 | void drive_strength(enum si5351wire_clock, enum si5351wire_drive); 294 | void update_status(void); 295 | void set_correction(int32_t, enum si5351wire_pll_input); 296 | void set_phase(enum si5351wire_clock, uint8_t); 297 | int32_t get_correction(enum si5351wire_pll_input); 298 | void pll_reset(enum si5351wire_pll); 299 | void set_ms_source(enum si5351wire_clock, enum si5351wire_pll); 300 | void set_int(enum si5351wire_clock, uint8_t); 301 | void set_clock_pwr(enum si5351wire_clock, uint8_t); 302 | void set_clock_invert(enum si5351wire_clock, uint8_t); 303 | void set_clock_source(enum si5351wire_clock, enum si5351wire_clock_source); 304 | void set_clock_disable(enum si5351wire_clock, enum si5351wire_clock_disable); 305 | void set_clock_fanout(enum si5351wire_clock_fanout, uint8_t); 306 | void set_pll_input(enum si5351wire_pll, enum si5351wire_pll_input); 307 | void set_vcxo(uint64_t, uint8_t); 308 | void set_ref_freq(uint32_t, enum si5351wire_pll_input); 309 | uint8_t si5351wire_write_bulk(uint8_t, uint8_t, uint8_t *); 310 | uint8_t i2c_receive_byte_data(uint8_t); 311 | uint8_t si5351wire_write(uint8_t, uint8_t); 312 | uint8_t si5351wire_read(uint8_t); 313 | struct Si5351wireStatus dev_status = {.SYS_INIT = 0, .LOL_B = 0, .LOL_A = 0, 314 | .LOS = 0, .REVID = 0}; 315 | struct Si5351wireIntStatus dev_int_status = {.SYS_INIT_STKY = 0, .LOL_B_STKY = 0, 316 | .LOL_A_STKY = 0, .LOS_STKY = 0}; 317 | enum si5351wire_pll pll_assignment[8]; 318 | uint64_t clk_freq[8]; 319 | uint64_t plla_freq; 320 | uint64_t pllb_freq; 321 | enum si5351wire_pll_input plla_ref_osc; 322 | enum si5351wire_pll_input pllb_ref_osc; 323 | uint32_t xtal_freq[2]; 324 | 325 | private: 326 | uint64_t pll_calc(enum si5351wire_pll, uint64_t, struct Si5351wireRegSet *, int32_t, uint8_t); 327 | uint64_t multisynth_calc(uint64_t, uint64_t, struct Si5351wireRegSet *); 328 | uint64_t multisynth67_calc(uint64_t, uint64_t, struct Si5351wireRegSet *); 329 | void update_sys_status(struct Si5351wireStatus *); 330 | void update_int_status(struct Si5351wireIntStatus *); 331 | void ms_div(enum si5351wire_clock, uint8_t, uint8_t); 332 | uint8_t select_r_div(uint64_t *); 333 | 334 | //sI2c_direct i2c; 335 | 336 | uint8_t select_r_div_ms67(uint64_t *); 337 | int32_t ref_correction[2]; 338 | uint8_t clkin_div; 339 | uint8_t i2c_bus_addr; 340 | bool clk_first_set[8]; 341 | struct Si5351wireRegSet ms_reg_save[8]; 342 | uint8_t sda; 343 | uint8_t scl; 344 | }; 345 | 346 | #endif /* SI5351wire_H_ */ 347 | --------------------------------------------------------------------------------