adding adf4350 board to my project. how to use, how to code for arduino? - Page 1
Forum section: electronics / rf-microwave

author: drspastic
author_rank: Contributor
timestamp: February 03, 2025, 12:02:22 am
content: so im making a semi dual frequency oscillator mainly for qo100 as an if for uplink and downlink, but also to do rtty and cw without having to take a radio with me.i have the lcd, esp32, and 2 rotary encoders all working on a nice (if imperfect) ui. i can set vfo a top of screen, and vfo b bottom of screen, select digit and change digit. i have 2 gpio that are to xor the two vfos to the adf4350 board where i will use either or both phase outputs to the mixers.this is the board:https://vi.aliexpress.com/item/1005006036325446.htmlthis is lcd:https://vi.aliexpress.com/item/1005005978400236.htmland esp32 is lolin litemy connections are like:Code:esp encoder a32 sw35 dt34 clkencoder b4 sw16 dt17 clklcd22 led18 sck23 sda0 a02 reset5 csadf435019 ce33 clk25 le26 data27 mout14 ld12 pdrf13 vfoa out15 vfob outthis is the workin ui code:Code:#include <Adafruit_GFX.h>#include <Adafruit_ST7735.h>#include <ezButton.h>#define CLK_PIN 34#define DT_PIN 35#define SW_PIN 32#define CLK_PIN2 4#define DT_PIN2 16#define SW_PIN2 17#define DIRECTION_CW 0#define DIRECTION_CCW 1#define TFT_CS 5#define TFT_RST 2#define TFT_DC 0#define TFT_BL 22long long vfoa = 740000000;long long vfob = 2400000000;long long adjustmentA = 1;long long adjustmentB = 1;int direction;int direction2;int prev_CLK_state, prev_CLK_state2;bool updateVFOA = false, updateVFOB = false;Adafruit_ST7735 tft(TFT_CS, TFT_DC, TFT_RST);ezButton button(SW_PIN);ezButton button2(SW_PIN2);void highlightDigitRightJustified(long long value, long long adjustment, int y, uint16_t normalColor, uint16_t highlightColor) {char buffer; // Ensure buffer size is large enough for leading zerossprintf(buffer, "%010lld", value); // Format frequency as 10 digits with leading zeros// Calculate the total text width (12 pixels per character for text size 2)int textWidth = strlen(buffer) * 12;// Starting X position for right justification (160 is screen width)int xStart = 160 - textWidth;// Find the index of the digit to highlightint digitIndex = 9 - log10(adjustment); // Determine which digit corresponds to the adjustment// Render the frequency string with highlightingfor (int i = 0, pos = 0; i < strlen(buffer); i++) {// Highlight the correct digitif (pos == digitIndex) {tft.setTextColor(highlightColor, ST77XX_BLACK);} else {tft.setTextColor(normalColor, ST77XX_BLACK);}tft.setCursor(xStart + pos * 12, y);tft.print(buffer);pos++;}}void updateDisplayVFOA() {tft.fillRect(0, 20, 160, 20, ST77XX_BLACK); // Clear the areahighlightDigitRightJustified(vfoa, adjustmentA, 20, ST77XX_GREEN, ST77XX_RED);// Correctly justify "MHz" under the VFOA displaytft.setCursor(124, 40); // Position "MHz" to be right-alignedtft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);tft.print("MHz");}void updateDisplayVFOB() {tft.fillRect(0, 70, 160, 20, ST77XX_BLACK); // Clear the areahighlightDigitRightJustified(vfob, adjustmentB, 70, ST77XX_YELLOW, ST77XX_RED);// Correctly justify "MHz" under the VFOB displaytft.setCursor(124, 90); // Position "MHz" to be right-alignedtft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);tft.print("MHz");}void setup() {tft.initR(INITR_BLACKTAB);pinMode(CLK_PIN, INPUT);pinMode(DT_PIN, INPUT);pinMode(CLK_PIN2, INPUT);pinMode(DT_PIN2, INPUT);pinMode(TFT_BL, OUTPUT);digitalWrite(TFT_BL, HIGH);button.setDebounceTime(50);button2.setDebounceTime(50);prev_CLK_state = digitalRead(CLK_PIN);prev_CLK_state2 = digitalRead(CLK_PIN2);tft.setRotation(1);tft.fillScreen(ST77XX_BLACK);tft.setTextSize(2);updateDisplayVFOA();updateDisplayVFOB();}void loop() {button.loop();button2.loop();int CLK_state = digitalRead(CLK_PIN);if (CLK_state != prev_CLK_state && CLK_state == HIGH) {if (digitalRead(DT_PIN) == HIGH) {vfoa -= adjustmentA;direction = DIRECTION_CCW;} else {vfoa += adjustmentA;direction = DIRECTION_CW;}updateVFOA = true;}prev_CLK_state = CLK_state;int CLK_state2 = digitalRead(CLK_PIN2);if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {if (digitalRead(DT_PIN2) == HIGH) {vfob -= adjustmentB;direction2 = DIRECTION_CCW;} else {vfob += adjustmentB;direction2 = DIRECTION_CW;}updateVFOB = true;}prev_CLK_state2 = CLK_state2;if (updateVFOA) {updateVFOA = false;updateDisplayVFOA();}if (updateVFOB) {updateVFOB = false;updateDisplayVFOB();}if (button.isPressed()) {adjustmentA *= 10;if (adjustmentA > 1000000000) {adjustmentA = 1;}updateVFOA = true;}if (button2.isPressed()) {adjustmentB *= 10;if (adjustmentB > 1000000000) {adjustmentB = 1;}updateVFOB = true;}}and this is what ai churned out after i drew a blank on how to proceed with adding the adf4350:Code:#include <Adafruit_GFX.h>#include <Adafruit_ST7735.h>#include <ezButton.h>#include <SPI.h>#include <Preferences.h> // Include the Preferences library// Pin Definitions#define CLK_PIN 34#define DT_PIN 35#define SW_PIN 32#define CLK_PIN2 4#define DT_PIN2 16#define SW_PIN2 17#define TFT_CS 15#define TFT_RST 4#define TFT_DC 2#define TFT_BL 22// Global Variableslong long vfoa = 740000000; // Default valuelong long vfob = 2400000000; // Default valuelong long adjustmentA = 1;long long adjustmentB = 1;int direction, direction2;int prev_CLK_state, prev_CLK_state2;bool updateVFOA = false, updateVFOB = false;// Display and Button ObjectsAdafruit_ST7735 tft(TFT_CS, TFT_DC, TFT_RST);ezButton button(SW_PIN);ezButton button2(SW_PIN2);// Preferences Object for Persistent StoragePreferences preferences;void highlightDigitRightJustified(long long value, long long adjustment, int y, uint16_t normalColor, uint16_t highlightColor) {char buffer;sprintf(buffer, "%010lld", value);int textWidth = strlen(buffer) * 12;int xStart = 160 - textWidth;int digitIndex = 9 - log10(adjustment);for (int i = 0, pos = 0; i < strlen(buffer); i++) {if (pos == digitIndex) {tft.setTextColor(highlightColor, ST77XX_BLACK);} else {tft.setTextColor(normalColor, ST77XX_BLACK);}tft.setCursor(xStart + pos * 12, y);tft.print(buffer);pos++;}}void updateDisplayVFOA() {tft.fillRect(0, 20, 160, 20, ST77XX_BLACK);highlightDigitRightJustified(vfoa, adjustmentA, 20, ST77XX_GREEN, ST77XX_RED);tft.setCursor(124, 40);tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);tft.print("MHz");}void updateDisplayVFOB() {tft.fillRect(0, 70, 160, 20, ST77XX_BLACK);highlightDigitRightJustified(vfob, adjustmentB, 70, ST77XX_YELLOW, ST77XX_RED);tft.setCursor(124, 90);tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);tft.print("MHz");}void setup() {Serial.begin(115200);// Initialize SPISPI.begin();// Initialize Displaytft.initR(INITR_144GREENTAB); // Use the correct constant for your displaytft.setRotation(1);tft.fillScreen(ST77XX_BLACK);tft.setTextSize(2);pinMode(TFT_BL, OUTPUT);digitalWrite(TFT_BL, HIGH);// Initialize Rotary Encoder PinspinMode(CLK_PIN, INPUT);pinMode(DT_PIN, INPUT);pinMode(CLK_PIN2, INPUT);pinMode(DT_PIN2, INPUT);// Initialize Buttonsbutton.setDebounceTime(100);button2.setDebounceTime(100);// Read Initial Encoder Statesprev_CLK_state = digitalRead(CLK_PIN);prev_CLK_state2 = digitalRead(CLK_PIN2);// Initialize Preferencespreferences.begin("vfo", false); // Open namespace "vfo" in read/write mode// Load Persistent Valuesvfoa = preferences.getLongLong("vfoa", 740000000); // Load saved value or use defaultvfob = preferences.getLongLong("vfob", 2400000000); // Load saved value or use default// Update DisplayupdateDisplayVFOA();updateDisplayVFOB();}void loop() {button.loop();button2.loop();// Handle Rotary Encoder 1 (VFOA)int CLK_state = digitalRead(CLK_PIN);if (CLK_state != prev_CLK_state && CLK_state == HIGH) {if (digitalRead(DT_PIN) == HIGH) {vfoa -= adjustmentA;direction = DIRECTION_CCW;} else {vfoa += adjustmentA;direction = DIRECTION_CW;}updateVFOA = true;preferences.putLongLong("vfoa", vfoa); // Save new value to NVS}prev_CLK_state = CLK_state;// Handle Rotary Encoder 2 (VFOB)int CLK_state2 = digitalRead(CLK_PIN2);if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {if (digitalRead(DT_PIN2) == HIGH) {vfob -= adjustmentB;direction2 = DIRECTION_CCW;} else {vfob += adjustmentB;direction2 = DIRECTION_CW;}updateVFOB = true;preferences.putLongLong("vfob", vfob); // Save new value to NVS}prev_CLK_state2 = CLK_state2;// Update Display if Necessaryif (updateVFOA) {updateVFOA = false;updateDisplayVFOA();}if (updateVFOB) {updateVFOB = false;updateDisplayVFOB();}// Handle Button Presses (Adjustment Multipliers)if (button.isPressed()) {adjustmentA *= 10;if (adjustmentA > 1000000000) {adjustmentA = 1;}updateVFOA = true;}if (button2.isPressed()) {adjustmentB *= 10;if (adjustmentB > 1000000000) {adjustmentB = 1;}updateVFOB = true;}}which of course doesnt output rf.now i dont have a scooby doo about how to use the adf generator, i cant find any useful guides to using this board, and the only thing i have seen work is a green light comes on IF i power the whole bunch of circuitry via the usbc port on the adf board (this brings up the esp and peripherals, but powering by the esp usb port doesnt bring up the adf board)as you see above, i connected all the adf board connections to power rails or gpio. i dont know if they are all needed or if its just clock, data, and chip select.so i beg you kind gentlemen to advise me how to proceed. this is all to be FOSS design for others to copy and i will stick it up on github once its working enough to be considered a starting point.

author: shabaz
author_rank: Frequent Contributor
timestamp: February 03, 2025, 01:07:44 am
content: There's an amount of code that needs to be written to configure that chip, and your code doesn't seem to include anything like that, so it's no surprise it doesn't work.You can see the code that is required by looking at any existing library for that chip, e.g. 'ADF4350 github' Google search reveals this:https://github.com/darkbyte-ru/ADF4350That URL goes to a library of code for Arduino (as a C++ class, because that's how Arduino expects the library to be done), plus there are some example programs that use that library, in another folder in that GitHub repo (files with a .ino file suffix which is what Arduino happens to use, but it's just plain normal C/C++ code).As I understand, ESP32 can be used in an Arduino manner, so that may be the quickest and easiest. Otherwise, you'd need to copy-paste and possibly rework some code - depending on whether you're building C code or C++ code for your ESP32 (I don't know what the default is, for whichever development environment you're using), and removing anything Arduino-specific and replacing with whichever SDK functions you have to work with; for instance, GPIO, SPI and so on, will be accessible with function names that may be similar-ish but slightly different compared to what Arduino uses.Also, there are confusing items in your text. I didn't understand what you meant by using GPIO to XOR VFO's. The VFO outputs operate at VHF and upwards, so I presume you don't mean to connect the outputs to the microcontroller; it needs clarification from you.Also, please can you use the caps-key? It's difficult to scan through when it's all lower-case.

author: drspastic
author_rank: Contributor
timestamp: February 03, 2025, 04:56:45 pm
content: https://speakyssb.blogspot.com/was the example i was looking at. its very close to what i wanted but without a UI. i got the ui done so being a ppor programmer, i told ai to graft the code in

author: shabaz
author_rank: Frequent Contributor
timestamp: February 04, 2025, 04:48:30 pm
content: I had a look at this, and there may be some misunderstanding. There's only a single PLL inside that ADF chip, you won't get two outputs (unless you want one output to be a certain specific multiple of the other), whereas it seems your code attempts to allow the user to select two arbitrary frequencies.If you really need two outputs with no dependency between the two outputs, then you'll need a second ADF board.Is that what you're planning, or is my understanding incorrect?

author: EggertEnjoyer123
author_rank: Frequent Contributor
timestamp: February 05, 2025, 04:26:33 am
content: The way I generally approach these types of problems is by going through the register map. Luckily for you the ADF4350 doesn't have that many, the TI chip I used had over 100 registers. Then start at the very end and go to the very beginning (generally the order the registers are programmed in is last to first, but you should check). Create a function called setRegister that takes in a series of bytes and writes that register using SPI (you can ask AI for help). For you, just have it send a set of 4 bytes because each command is 32 bits. Then, you have to go through all the registers and calculate the correct hex value which matches your settings. You can't have AI do this part because it's not smart enough to know what you're doing, but all you have to do is call the setRegister function. Finally, create a function that takes a frequency and sets the divider register, N, numerator, and denominator of the PLL. This can probably also be done with AI.The AI is usually too stupid to do any big tasks. Whenever you program with AI you need to break it down into small tasks. Imagine the AI as a high school student with access to infinite resources. A high schooler is going to be completely confused if you ask it to program your PLL, but you can give it small tasks like "Create a function that uses SPI and sends over 4 bytes" and then check if it works. Something like "Set the PLL to this frequency" is too hard, but something like "Given an integer called freq, calculate A + b/c in lowest terms such that reference * (A + b/c) = freq" would get you the right answer. Just always make sure to test each piece the AI gives you separately. At the very end you can figure out how to integrate all the pieces, and it's much easier if you know all the pieces work before you assemble them. So for you, just ask the AI to modify the display code to call a function which just prints out the frequency on Serial. Make sure it works, then get the AI to generate the PLL code in small steps, and then at the end just replace the Serial code with the PLL code.

author: drspastic
author_rank: Contributor
timestamp: February 05, 2025, 04:59:59 pm
content: only one frequency is used at any time. its not duplex. one vfo is for receive and the other for transmit. most cheap walkie talkies use a similar scheme. the pll board has phase and antiphase outputs that both need to have a 50 ohm load so they will go into small rf amplifiers to buffer and protect.i went to the suffested library by darkbyte and even had a little chat with dimitre about my confusion over the reference oscillator. the library also demands spi.h so i installed the esp32 library that contains it. i didnt want to use the default spi pins because it would make the wiring messy, so i read up about spi.h and added lines at the top of my code to set the pins i wanted. LE (link enable) has, i believe, to be set high to allow the adf43x to accept spi commands.someone also just clarified for me the purpose of # being to tell the compiler something, which i didnt understand before.i have also realised that the granularity of the adf chip is just 1khz so i edited the ui to reflect that. i added the last 2 pin designations for the 2 control gpio. im now trying to rearrange things to increase font size and readibility. then im back on the actual output frequency and control.

author: drspastic
author_rank: Contributor
timestamp: February 05, 2025, 05:01:51 pm
content: I updated it and tried getting the pll working but no outputCode://first the PLL stuff#include <SPI.h>#include <ADF4350.h>#define PLL_LE_PIN 25ADF4350 PLL(PLL_LE_PIN);#define BME_SCK 33//#define BME_MISO 32#define BME_MOSI 26#define BME_CS 19// which is supposed to have set the spi pins#define PLL_REFERENCE_FREQ 25 //MHz clock//set up the pins that will eventually activate the output#define vfoaEN 13#define vfobEN 15//now the display stuff#include <Adafruit_GFX.h>#include <Adafruit_ST7735.h>#include <ezButton.h>//the rotary encoders#define CLK_PIN 34#define DT_PIN 35#define SW_PIN 32#define CLK_PIN2 4#define DT_PIN2 16#define SW_PIN2 17#define DIRECTION_CW 0#define DIRECTION_CCW 1//more display pins#define TFT_CS 5#define TFT_RST 2#define TFT_DC 0#define TFT_BL 22//initial vfo valueslong long vfoa = 740000;long long vfob = 2400000;long long adjustmentA = 1;long long adjustmentB = 1;int direction;int direction2;int prev_CLK_state, prev_CLK_state2;bool updateVFOA = false, updateVFOB = false;Adafruit_ST7735 tft(TFT_CS, TFT_DC, TFT_RST);ezButton button(SW_PIN);ezButton button2(SW_PIN2);void highlightDigitRightJustified(long long value, long long adjustment, int y, uint16_t normalColor, uint16_t highlightColor) {char buffer; // Ensure buffer size is large enough for leading zerossprintf(buffer, "%007lld", value); // Format frequency as 7 digits with leading zeros(reduced from 10)// Calculate the total text width (12 pixels per character for text size 2)int textWidth = strlen(buffer) * 12;//was 12 but texsize 3 was getting trimmed// Starting X position for right justification (160 is screen width) revised to 100int xStart = 90 - textWidth;// Find the index of the digit to highlightint digitIndex = 6 - log10(adjustment); // Determine which digit corresponds to the adjustment// Render the frequency string with highlightingfor (int i = 0, pos = 0; i < strlen(buffer); i++) {// Highlight the correct digitif (pos == digitIndex) {tft.setTextColor(highlightColor, ST77XX_BLACK);} else {tft.setTextColor(normalColor, ST77XX_BLACK);}tft.setCursor(xStart + pos * 12, y);//was12tft.print(buffer);pos++;}}void updateDisplayVFOA() {// tft.setTextSize(3);tft.fillRect(0, 20, 160, 20, ST77XX_BLACK); // Clear the areahighlightDigitRightJustified(vfoa, adjustmentA, 20, ST77XX_GREEN, ST77XX_RED);// Correctly justify "KHz" under the VFOA displaytft.setTextSize(2);tft.setCursor(124, 20); // Position "KHz" to be right-aligned was 40tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);tft.print("KHz");}void updateDisplayVFOB() {tft.fillRect(0, 70, 160, 20, ST77XX_BLACK); // Clear the areahighlightDigitRightJustified(vfob, adjustmentB, 70, ST77XX_YELLOW, ST77XX_RED);// Correctly justify "KHz" under the VFOB displaytft.setTextSize(2);tft.setCursor(124, 70); // Position "KHz" to be right-aligned was 90tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);tft.print("KHz");}void setup() {//init ADF4350 with 25MHz reference and tune to 433000 KHz just for testingSerial.begin(115200);PLL.initialize(432000, PLL_REFERENCE_FREQ);//pinMode(BME_CS, OUTPUT);//digitalWrite(BME_CS,HIGH);//default power is 0, which means -4dBm//PLL.setRfPower(0);//display setuptft.initR(INITR_BLACKTAB);//its green tab but this works bestpinMode(CLK_PIN, INPUT);pinMode(DT_PIN, INPUT);pinMode(CLK_PIN2, INPUT);pinMode(DT_PIN2, INPUT);pinMode(TFT_BL, OUTPUT);digitalWrite(TFT_BL, HIGH);//buttons and rotary encodersbutton.setDebounceTime(50);button2.setDebounceTime(50);prev_CLK_state = digitalRead(CLK_PIN);prev_CLK_state2 = digitalRead(CLK_PIN2);//more display stufftft.setRotation(1);tft.fillScreen(ST77XX_BLACK);tft.setTextSize(2);//was 2 damn i want 3 but it wont format rightupdateDisplayVFOA();updateDisplayVFOB();}void loop() {button.loop();button2.loop();int CLK_state = digitalRead(CLK_PIN);if (CLK_state != prev_CLK_state && CLK_state == HIGH) {if (digitalRead(DT_PIN) == HIGH) {vfoa -= adjustmentA;direction = DIRECTION_CCW;} else {vfoa += adjustmentA;direction = DIRECTION_CW;}updateVFOA = true;}prev_CLK_state = CLK_state;int CLK_state2 = digitalRead(CLK_PIN2);if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {if (digitalRead(DT_PIN2) == HIGH) {vfob -= adjustmentB;direction2 = DIRECTION_CCW;} else {vfob += adjustmentB;direction2 = DIRECTION_CW;}updateVFOB = true;}prev_CLK_state2 = CLK_state2;if (updateVFOA) {updateVFOA = false;updateDisplayVFOA();}if (updateVFOB) {updateVFOB = false;updateDisplayVFOB();}//these i took 3 zeroes off to register only in khz and not loop to hz which is no longer on screenif (button.isPressed()) {adjustmentA *= 10;if (adjustmentA > 1000000) {adjustmentA = 1;}updateVFOA = true;}if (button2.isPressed()) {adjustmentB *= 10;if (adjustmentB > 1000000) {adjustmentB = 1;}updateVFOB = true;}}btw, the reason i hardly use capitals is because i mashed my left arm in a bike crash 25 years ago, and just had an operation on the right one. holding shift is really awkward

author: shabaz
author_rank: Frequent Contributor
timestamp: February 06, 2025, 03:48:27 am
content: I'm sorry about that regarding your arm : (I took a stab at the integration of the UI code and the ADF code, it is here:https://gist.github.com/shabaz123/ff486699ef471132e940f55f26f74a66You can download it as a zip file (including the ADF4350 library code) temporarily from here:https://1drv.ms/u/c/0cdd191798dd1158/EUDjOh3BzztFp_zjmnCk-sABmOdayXN02pVMOaSKeRK3FQ?e=2jLbyTI don't have the same ESP32 board as you, so I needed to change some pin definitions, you'll need to adjust according to what you have. Also, I don't have an ADF board nor the TFT screen, so all I could do is look at the signals to see if they look semi-realistic.Note: with this implementation, both the TFT and the ADF board share SPI connections for MOSI and SCK. You can't change those pins, because the libraries use them. You can change the other pins in the #define lines however.The attached screenshots show what happens at startup (TFT initialization) and a repeated attempt at writing to the ADF board (I wrote a temporary loop to test that). You don't need to try to decode that (it's not possible from those basic screenshots), it's just to show that there is a chance the code may somewhat work.You'll still have work to do modifying and testing, but hopefully this code can be a start. I wont really be able to do much more without the same setup and more time unfortunately.

author: drspastic
author_rank: Contributor
timestamp: February 07, 2025, 02:30:13 pm
content: so im having problems getting anything out of the adf board. im thinking i may have got a duff one

author: Georgy.Moshkin
author_rank: Frequent Contributor
timestamp: February 07, 2025, 03:28:53 pm
content: It's better to buy a sandwich board with cypress chip. Then you can useevaluation board softwarefor original evaluation boards to test the registers. Here is my ADF5355, and ADF4350 port looks very similar: there is an USB port and windows application to control the registers.

author: abeyer
author_rank: Frequent Contributor
timestamp: February 07, 2025, 03:51:59 pm
content: Quote from: drspastic on February 05, 2025, 05:01:51 pmHope your recovery goes well. When i had an injury that had me typing one hand, I leaned a lot on the "sticky keys" feature, it makes key combos much easier, you might want to try it out -- it varies a little bit between different platforms, but it's usually somewhere in the accessibility settings for your os. (or at least on windows you can just tap shift 5 or 6 times in a row to toggle it)