├── Arduino_I2C_BPF_RX ├── Arduino_I2C_BPF_RX.ino ├── Wire_.cpp ├── Wire_.h ├── twi_.c └── twi_.h ├── Arduino_uSDR_Pico_FFT.png ├── Arduino_uSDX_Pico_FFT ├── Arduino_uSDX_Pico_FFT.ino ├── CwDecoder.cpp ├── CwDecoder.h ├── Dflash.cpp ├── Dflash.h ├── _kiss_fft_guts.h ├── display_tft.cpp ├── display_tft.h ├── dsp.cpp ├── dsp.h ├── hmi.cpp ├── hmi.h ├── kiss_fft.cpp ├── kiss_fft.h ├── kiss_fftr.cpp ├── kiss_fftr.h ├── monitor.cpp ├── monitor.h ├── relay.cpp ├── relay.h ├── si5351.cpp ├── si5351.h ├── uSDR.cpp └── uSDR.h ├── Aux ├── Components │ ├── LM386_amplifier.png │ ├── MP2315_DCDC.png │ ├── RP2040_YD2040_comparison.png │ └── TEA2025_pcb.png └── TFilter_Engineerjs.zip ├── Encoder_selection.png ├── FFT_LCD_pico.png ├── FFT_LCD_pico_MOD.png ├── FFT_LCD_pico_MOD2.png ├── HFD3_characteristics.png ├── PCB ├── SWR │ ├── SWR_B_Mask.pdf │ ├── SWR_F_Mask.pdf │ ├── SWR_KiCad.zip │ ├── SWR_PCB.pdf │ └── SWR_SCH.pdf ├── uSDR_Pico_BPF_RX_Kicad.zip ├── uSDR_Pico_BPF_RX_SCH.pdf ├── uSDR_Pico_FFT_Kicad.zip └── uSDR_Pico_FFT_SCH.pdf ├── Pict1.png ├── Pictures ├── Arjan_5_BPF_RX.png ├── Arjan_5_block_diagram.png ├── Cardboard_box_prototype.png ├── CwDecoder_display_area.png ├── Display_SWR_Power.png ├── Mod_include_RC_audio.png ├── Mod_include_diode_Vcc.png ├── PTTout.png ├── Prototype_2.png ├── uSDR_Pico_FFT_PCB_botton.png └── uSDR_Pico_FFT_PCB_top.png ├── README.md ├── Reg_monted.jpg ├── Senos FFT.xlsx ├── VREF_TPS79933_AXX.png ├── leitura fft samp.xlsx └── uSDX_TX ├── uSDX_I2C.cpp ├── uSDX_I2C.h ├── uSDX_SI5351.cpp ├── uSDX_SI5351.h ├── uSDX_TX.ino ├── uSDX_TX_PhaseAmpl.cpp └── uSDX_TX_PhaseAmpl.h /Arduino_I2C_BPF_RX/Arduino_I2C_BPF_RX.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | I2C Slave with multiple addresses 4 | 5 | It receives I2C commands to activate the BandPass and/or Receiver Relays. 6 | It receives I2C commands as more than one I2C slave (multiple addresses) 7 | to replace the two PCF8574 in Arjan-5 (uSDX Pico FFT project) 8 | Adding new I2C command to read the SWR 9 | it uses A0 and A1 analog inputs to read the SWR Forward and Reflected information. 10 | 11 | Reference: 12 | https://stackoverflow.com/questions/34691478/arduino-as-slave-with-multiple-i2c-addresses 13 | https://github.com/alexisgaziello/TwoWireSimulator 14 | 15 | I2C Wire library with multi address: 16 | https://github.com/arduino/ArduinoCore-avr/pull/90/files#diff-e4603cea13a2a6370bdf819d929e8fb9b272c812bc1df9a9190b365875c47db3 17 | 18 | 19 | 20 | Arduino Pro Mini 3V3 ATmega328P 8Mhz 21 | Flash: 32KB 22 | SRAM: 2KB 23 | EEPROM: 1KB 24 | 14 digital pins, input or output 25 | Serial: 0 (RX) and 1 (TX) 26 | External Interrupts: 2 and 3 27 | PWM: 3, 5, 6, 9, 10, and 11 28 | PI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK) 29 | LED: 13 30 | 8 analog inputs, 10 bits of resolution: A0 - A7 31 | I2C: A4 (SDA) and A5 (SCL) 32 | 33 | 34 | Created: May 2023 35 | Author: Klaus Fensterseifer PY2KLA 36 | https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 37 | 38 | */ 39 | 40 | #include "Wire_.h" //Local Wire Library (same as Wire but with addings) 41 | 42 | 43 | 44 | /* I2C BPF = Band Pass Filters Relays */ 45 | #define I2C_BPF 0x20 46 | #define REL_LPF2_pin 2 //160MHz 47 | #define REL_BPF6_pin 3 //80MHz 48 | #define REL_BPF12_pin 4 //40MHz 49 | #define REL_BPF24_pin 5 //20MHz 50 | #define REL_BPF40_pin 6 //10Mhz 51 | 52 | #define REL_BPF_val_num 5 53 | #define REL_LPF2_val 0x01 54 | #define REL_BPF6_val 0x02 55 | #define REL_BPF12_val 0x04 56 | #define REL_BPF24_val 0x08 57 | #define REL_BPF40_val 0x10 58 | 59 | /* I2C RX = Attenuators & LNA Relays */ 60 | #define I2C_RX 0x21 61 | #define REL_ATT_20_pin 9 62 | #define REL_ATT_10_pin 8 63 | #define REL_PRE_10_pin 7 64 | 65 | #define REL_ATT_val_num 5 66 | #define REL_ATT_30_val 0x03 67 | #define REL_ATT_20_val 0x01 68 | #define REL_ATT_10_val 0x02 69 | #define REL_ATT_00_val 0x00 70 | #define REL_PRE_10_val 0x04 71 | #define REL_PRE_00_val 0x00 72 | 73 | /* I2C SWR read */ 74 | #define I2C_SWR 0x22 // read 3 bytes = SWR, FOR and REF 75 | #define vForwardPin A0 // select the input pin for the swr analog reading 76 | #define vReflectedPin A1 // select the input pin for the swr analog reading 77 | #define VMIN 20 // min forward AD value for swr 78 | #define SWR_BASE10 10 // numeric base for SWR decimal 79 | #define SWR_MIN (1 * SWR_BASE10) // SWR = 1.0 80 | #define SWR_MAX ((15 * SWR_BASE10)+(SWR_BASE10-1)) // byte max value, SWR max = 15.9 81 | 82 | uint8_t SWR[3]; //save swr info to send to I2C master = swr, forward and reflected 83 | 84 | #define I2C_TWAR (I2C_BPF | I2C_RX | I2C_SWR) // TWAR = main mask address = 0010 0011 85 | #define I2C_TWAMR ((I2C_BPF | I2C_RX | I2C_SWR) ^ (I2C_BPF & I2C_RX & I2C_SWR)) // xor = 0000 0011 -> it receives addresses 0010 00XX -> from 0x20 to 0x23 86 | // TWAMR = on bit set, accept any value for the address bit received 87 | 88 | 89 | 90 | #define ledPin 13 // define LED pin number 91 | 92 | 93 | uint8_t RX_Relays=0, RX_Relays_old=0xff; 94 | uint8_t BPF_Relays=0, BPF_Relays_old=0xff; 95 | uint8_t rec=0, I2C_Address; 96 | const uint8_t REL_BPF_val[REL_BPF_val_num] = {REL_LPF2_val, REL_BPF6_val, REL_BPF12_val, REL_BPF24_val, REL_BPF40_val}; 97 | const uint8_t REL_ATT_val[REL_ATT_val_num] = {REL_PRE_10_val, REL_ATT_30_val, REL_ATT_20_val, REL_ATT_10_val, REL_ATT_00_val}; 98 | uint8_t LastAddress; //just for debug 99 | 100 | 101 | 102 | unsigned long nextMillis; 103 | #define LOOP_PERIOD_MILLIS 100 104 | 105 | 106 | /*****************************************************************************************/ 107 | void setup() 108 | { 109 | pinMode(ledPin, OUTPUT); 110 | 111 | Serial.begin(115200); //choose the right clock for Arduino Pro Mini at Tools Processor 112 | for(int i=0; i<50; i++) // wait some time for Serial to open 113 | { 114 | digitalWrite(ledPin, 1); //toggle led 115 | delay(50); // wait 116 | digitalWrite(ledPin, 0); //toggle led 117 | delay(50); // wait 118 | if(Serial) //serial opened 119 | break; 120 | } // If the serial does not open, the print commands will have no effect 121 | Serial.println("\nArduino I2C Slave Multi Address"); 122 | //Serial.print("FREQ CPU: "); 123 | //Serial.println(F_CPU); //prints the clock frequency, chose the right clock for Arduino Pro Mini at Tools Processor 124 | 125 | 126 | pinMode(REL_LPF2_pin, OUTPUT); 127 | pinMode(REL_BPF6_pin, OUTPUT); 128 | pinMode(REL_BPF12_pin, OUTPUT); 129 | pinMode(REL_BPF24_pin, OUTPUT); 130 | pinMode(REL_BPF40_pin, OUTPUT); 131 | pinMode(REL_ATT_20_pin, OUTPUT); 132 | pinMode(REL_ATT_10_pin, OUTPUT); 133 | pinMode(REL_PRE_10_pin, OUTPUT); 134 | 135 | digitalWrite(REL_LPF2_pin, 1); 136 | digitalWrite(REL_BPF6_pin, 0); 137 | digitalWrite(REL_BPF12_pin, 0); 138 | digitalWrite(REL_BPF24_pin, 0); 139 | digitalWrite(REL_BPF40_pin, 0); 140 | digitalWrite(REL_ATT_20_pin, 0); 141 | digitalWrite(REL_ATT_10_pin, 0); 142 | digitalWrite(REL_PRE_10_pin, 0); 143 | 144 | 145 | pinMode(vForwardPin, INPUT); 146 | pinMode(vReflectedPin, INPUT); 147 | 148 | Wire.begin(I2C_TWAR, I2C_TWAMR); // base address for all slaves running here (valid only for ATmega328P Arduinos) 149 | Wire.onRequest(requestEvent); // register callback function for I2C = master read 150 | Wire.onReceive(receiveEvent); // register callback function for I2C = master write 151 | 152 | 153 | 154 | nextMillis = millis() + LOOP_PERIOD_MILLIS; 155 | 156 | } 157 | 158 | 159 | /*****************************************************************************************/ 160 | void requestEvent (){ // master read = request data from slave 161 | switch (Wire.getLastAddress()) { // address from last byte on the bus 162 | case (I2C_BPF): 163 | Wire.write(BPF_Relays); // send byte relays state 164 | LastAddress = I2C_BPF; 165 | break; 166 | 167 | case (I2C_RX): 168 | Wire.write(RX_Relays); // send byte relays state 169 | LastAddress = I2C_RX; 170 | break; 171 | 172 | case (I2C_SWR): 173 | //Wire.write(SWR[0]); 174 | Wire.write(SWR, 3); // send back 3 bytes 175 | LastAddress = I2C_SWR; 176 | break; 177 | 178 | default: 179 | break; 180 | } 181 | } 182 | 183 | 184 | /*****************************************************************************************/ 185 | void receiveEvent(int howManyBytesReceived) { // master write = send data to slave 186 | switch (Wire.getLastAddress()) { // address from last byte on the bus 187 | case (I2C_BPF): 188 | BPF_Relays = Wire.read(); // receive byte 189 | LastAddress = I2C_BPF; 190 | break; 191 | 192 | case (I2C_RX): 193 | RX_Relays = Wire.read(); // receive byte 194 | LastAddress = I2C_RX; 195 | break; 196 | 197 | default: 198 | break; 199 | } 200 | 201 | } 202 | 203 | 204 | 205 | /*****************************************************************************************/ 206 | void Set_BPF_Relays() { 207 | 208 | Serial.print("Set BPF Relays "); 209 | 210 | if(BPF_Relays == REL_LPF2_val) 211 | { 212 | digitalWrite(REL_LPF2_pin, 1); 213 | digitalWrite(REL_BPF6_pin, 0); 214 | digitalWrite(REL_BPF12_pin, 0); 215 | digitalWrite(REL_BPF24_pin, 0); 216 | digitalWrite(REL_BPF40_pin, 0); 217 | 218 | Serial.print(BPF_Relays); 219 | Serial.println(" REL_LPF2_val band=0"); 220 | } 221 | else if(BPF_Relays == REL_BPF6_val) 222 | { 223 | digitalWrite(REL_LPF2_pin, 0); 224 | digitalWrite(REL_BPF6_pin, 1); 225 | digitalWrite(REL_BPF12_pin, 0); 226 | digitalWrite(REL_BPF24_pin, 0); 227 | digitalWrite(REL_BPF40_pin, 0); 228 | 229 | Serial.print(BPF_Relays); 230 | Serial.println(" REL_BPF6_val band=1"); 231 | } 232 | else if(BPF_Relays == REL_BPF12_val) 233 | { 234 | digitalWrite(REL_LPF2_pin, 0); 235 | digitalWrite(REL_BPF6_pin, 0); 236 | digitalWrite(REL_BPF12_pin, 1); 237 | digitalWrite(REL_BPF24_pin, 0); 238 | digitalWrite(REL_BPF40_pin, 0); 239 | 240 | Serial.print(BPF_Relays); 241 | Serial.println(" REL_BPF12_val band=3"); 242 | } 243 | else if(BPF_Relays == REL_BPF24_val) 244 | { 245 | digitalWrite(REL_LPF2_pin, 0); 246 | digitalWrite(REL_BPF6_pin, 0); 247 | digitalWrite(REL_BPF12_pin, 0); 248 | digitalWrite(REL_BPF24_pin, 1); 249 | digitalWrite(REL_BPF40_pin, 0); 250 | 251 | Serial.print(BPF_Relays); 252 | Serial.println(" REL_BPF24_val band=4"); 253 | } 254 | else //BPF40 at least 1 filter connected 255 | { 256 | digitalWrite(REL_LPF2_pin, 0); 257 | digitalWrite(REL_BPF6_pin, 0); 258 | digitalWrite(REL_BPF12_pin, 0); 259 | digitalWrite(REL_BPF24_pin, 0); 260 | digitalWrite(REL_BPF40_pin, 1); 261 | 262 | Serial.print(BPF_Relays); 263 | Serial.println(" REL_BPF40_val band=2"); 264 | } 265 | 266 | // digitalWrite(ledPin, !digitalRead(ledPin)); //toggle led 267 | } 268 | 269 | 270 | 271 | /*****************************************************************************************/ 272 | void Set_RX_Relays() { 273 | 274 | Serial.print("Set RX Relays "); 275 | Serial.print(RX_Relays); 276 | 277 | if((RX_Relays & REL_ATT_20_val) == REL_ATT_20_val) 278 | { 279 | digitalWrite(REL_ATT_20_pin, 1); 280 | Serial.print(" REL_ATT_20_val"); 281 | } 282 | else 283 | { 284 | digitalWrite(REL_ATT_20_pin, 0); 285 | } 286 | 287 | if((RX_Relays & REL_ATT_10_val) == REL_ATT_10_val) 288 | { 289 | digitalWrite(REL_ATT_10_pin, 1); 290 | Serial.print(" REL_ATT_10_val"); 291 | } 292 | else 293 | { 294 | digitalWrite(REL_ATT_10_pin, 0); 295 | } 296 | 297 | if((RX_Relays & REL_PRE_10_val) == REL_PRE_10_val) 298 | { 299 | digitalWrite(REL_PRE_10_pin, 1); 300 | Serial.print(" REL_PRE_10_pin"); 301 | } 302 | else 303 | { 304 | digitalWrite(REL_PRE_10_pin, 0); 305 | } 306 | 307 | Serial.println(" "); 308 | // digitalWrite(ledPin, !digitalRead(ledPin)); //toggle led 309 | } 310 | 311 | 312 | char s[64]; 313 | 314 | 315 | /*****************************************************************************************/ 316 | void SWR_read() 317 | { 318 | uint16_t vForward, vForward0; 319 | static uint16_t vForward1=1; //last AD reading 320 | uint16_t vReflected, vReflected0; 321 | static uint16_t vReflected1=1; //last AD reading 322 | uint16_t swr, swr_unid, swr_dec; 323 | 324 | /* read the AD for SWR */ 325 | vForward0 = analogRead(vForwardPin); //actual value 326 | vForward = (vForward0 + vForward1) >> 1; //average with last value 327 | 328 | vReflected0 = analogRead(vReflectedPin); //actual value 329 | vReflected = (vReflected0 + vReflected1) >> 1; //average with last value 330 | 331 | //vForward = 100; 332 | //vReflected = 10; 333 | if((vForward0 != vForward1) || 334 | (vReflected0 != vReflected1)) 335 | { 336 | /* 337 | Serial.print("ADC for0= "); 338 | Serial.print(vForward0); 339 | Serial.print(" ref0= "); 340 | Serial.println(vReflected0); 341 | */ 342 | sprintf(s, "ADC for0= %02x ref0= %02x", vForward0, vReflected0); 343 | Serial.println(s); 344 | } 345 | 346 | vForward1 = vForward0; //save last value for average 347 | vReflected1 = vReflected0; //save last value for average 348 | 349 | 350 | 351 | if(vForward < VMIN) 352 | { 353 | swr = SWR_MIN; 354 | } 355 | else if(vForward <= vReflected) 356 | { 357 | swr = SWR_MAX; 358 | } 359 | else 360 | { 361 | swr = (SWR_BASE10 * (vForward + vReflected)) / (vForward - vReflected); 362 | if(swr > SWR_MAX) 363 | { 364 | swr = SWR_MAX; 365 | } 366 | } 367 | /* swr value will be from 1.0 to 15.9 */ 368 | /* 1 byte -> bit7-bit4 = integer bit3-bit0 = decimal base 10 */ 369 | 370 | swr_unid = swr/SWR_BASE10; //integer part max = 15 371 | if(swr_unid > 15) swr_unid = 15; 372 | swr_dec = swr%SWR_BASE10; //decimal part 373 | if(swr_dec > 9) swr_dec = 9; //double check, just in case SWR_BASE10 changed 374 | SWR[0] = (swr_unid << 4) + swr_dec; 375 | SWR[1] = vForward >> 2; //10 bits AD to 8 bits 376 | SWR[2] = vReflected >> 2; 377 | 378 | if(vForward > 0) 379 | { 380 | /* 381 | Serial.print("SWR For= "); 382 | Serial.print(SWR[1]); 383 | Serial.print(" Ref= "); 384 | Serial.print(SWR[2]); 385 | Serial.print(" swr= "); 386 | Serial.print(SWR[0]>>4); //swr_unid 387 | Serial.print("."); 388 | Serial.println(SWR[0]&0x0f); //swr_dec 389 | */ 390 | sprintf(s, "SWR swr= %d.%d For= %02x Ref= %02x", SWR[0]>>4, SWR[0]&0x0f, SWR[1], SWR[2]); 391 | Serial.println(s); 392 | } 393 | else 394 | { 395 | SWR[0] = 0; // the min SWR when tx is 0x10 = 1.0, if not TX, SWR = 0.0 (for debug) 396 | } 397 | } 398 | 399 | 400 | 401 | 402 | uint8_t InitCheck(void) 403 | { 404 | static uint16_t cont_BPF_relay = 0; 405 | static uint16_t cont_ATT_relay = 0; 406 | static uint16_t cont_test = 0; 407 | uint8_t retValue; 408 | 409 | if(cont_test < 10) 410 | { 411 | /*******************************************************************************************************/ 412 | /* initial test switching relays after reset (just switch through all relays) */ 413 | /*******************************************************************************************************/ 414 | if(cont_test < 5) 415 | { 416 | BPF_Relays = REL_BPF_val[cont_BPF_relay]; 417 | //Serial.println("Set BPF Relays " + String(cont_BPF_relay) + " " + String(BPF_Relays)); 418 | Serial.print(cont_BPF_relay); Serial.print(" "); 419 | cont_BPF_relay++; 420 | if(cont_BPF_relay >= REL_BPF_val_num) 421 | cont_BPF_relay = 0; 422 | Set_BPF_Relays(); 423 | } 424 | else 425 | { 426 | RX_Relays = REL_ATT_val[cont_ATT_relay]; 427 | //Serial.println("Set RX Relays " + String(cont_ATT_relay) + " " + String(RX_Relays)); 428 | Serial.print(cont_ATT_relay); Serial.print(" "); 429 | cont_ATT_relay++; 430 | if(cont_ATT_relay >= REL_ATT_val_num) 431 | cont_ATT_relay = 0; 432 | Set_RX_Relays(); 433 | } 434 | cont_test++; 435 | 436 | delay(250); //ms 437 | 438 | retValue = false; 439 | } 440 | else 441 | { 442 | retValue = true; 443 | } 444 | 445 | 446 | return retValue; 447 | 448 | } 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | /*****************************************************************************************/ 457 | void loop() { 458 | 459 | if(InitCheck() == true) /* it runs a test sequence after reset, true means ready for normal processing */ 460 | { 461 | /**************************/ 462 | /* loop normal processing */ 463 | /**************************/ 464 | digitalWrite(ledPin, 1); 465 | 466 | SWR_read(); /* read the ADC for SWR calculation */ 467 | 468 | 469 | /* 470 | if(LastAddress != 0) 471 | { 472 | Serial.print('_'); 473 | Serial.print(LastAddress); 474 | LastAddress = 0; 475 | } 476 | */ 477 | 478 | digitalWrite(ledPin, 0); 479 | 480 | /* wait loop period to end - pocesses loop at specific period */ 481 | while(millis() < nextMillis) 482 | { 483 | 484 | /* check if received new value to set the BPF relays */ 485 | if(BPF_Relays_old != BPF_Relays) 486 | { 487 | Set_BPF_Relays(); 488 | BPF_Relays_old = BPF_Relays; 489 | } 490 | 491 | /* check if received new value to set the RX relays */ 492 | if(RX_Relays_old != RX_Relays) 493 | { 494 | Set_RX_Relays(); 495 | RX_Relays_old = RX_Relays; 496 | } 497 | 498 | } 499 | /* time for next loop period */ 500 | nextMillis += LOOP_PERIOD_MILLIS; 501 | 502 | } 503 | 504 | } 505 | 506 | 507 | 508 | 509 | -------------------------------------------------------------------------------- /Arduino_I2C_BPF_RX/Wire_.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | TwoWire.cpp - TWI/I2C library for Wiring & Arduino 3 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | You should have received a copy of the GNU Lesser General Public 13 | License along with this library; if not, write to the Free Software 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 15 | 16 | Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts 17 | Modified 2017 by Chuck Todd (ctodd@cableone.net) to correct Unconfigured Slave Mode reboot 18 | */ 19 | 20 | extern "C" { 21 | #include 22 | #include 23 | #include 24 | #include "twi_.h" 25 | } 26 | 27 | #include "Wire_.h" 28 | 29 | // Initialize Class Variables ////////////////////////////////////////////////// 30 | 31 | uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; 32 | uint8_t TwoWire::rxBufferIndex = 0; 33 | uint8_t TwoWire::rxBufferLength = 0; 34 | uint8_t TwoWire::rxAddress = 0; 35 | 36 | uint8_t TwoWire::txAddress = 0; 37 | uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; 38 | uint8_t TwoWire::txBufferIndex = 0; 39 | uint8_t TwoWire::txBufferLength = 0; 40 | 41 | uint8_t TwoWire::transmitting = 0; 42 | void (*TwoWire::user_onRequest)(void); 43 | void (*TwoWire::user_onReceive)(int); 44 | 45 | // Constructors //////////////////////////////////////////////////////////////// 46 | 47 | TwoWire::TwoWire() 48 | { 49 | } 50 | 51 | // Public Methods ////////////////////////////////////////////////////////////// 52 | 53 | void TwoWire::begin(void) 54 | { 55 | rxBufferIndex = 0; 56 | rxBufferLength = 0; 57 | 58 | txBufferIndex = 0; 59 | txBufferLength = 0; 60 | 61 | twi_init(); 62 | twi_attachSlaveTxEvent(onRequestService); // default callback must exist 63 | twi_attachSlaveRxEvent(onReceiveService); // default callback must exist 64 | } 65 | 66 | void TwoWire::begin(uint8_t address, uint8_t mask) 67 | { 68 | begin(); 69 | twi_setAddress(address, mask); 70 | } 71 | 72 | void TwoWire::begin(int address, int mask) 73 | { 74 | begin((uint8_t)address, (uint8_t)mask); 75 | } 76 | 77 | void TwoWire::end(void) 78 | { 79 | twi_disable(); 80 | } 81 | 82 | void TwoWire::setClock(uint32_t clock) 83 | { 84 | twi_setFrequency(clock); 85 | } 86 | 87 | uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop) 88 | { 89 | if (isize > 0) { 90 | // send internal address; this mode allows sending a repeated start to access 91 | // some devices' internal registers. This function is executed by the hardware 92 | // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) 93 | 94 | beginTransmission(address); 95 | 96 | // the maximum size of internal address is 3 bytes 97 | if (isize > 3){ 98 | isize = 3; 99 | } 100 | 101 | // write internal register address - most significant byte first 102 | while (isize-- > 0) 103 | write((uint8_t)(iaddress >> (isize*8))); 104 | endTransmission(false); 105 | } 106 | 107 | // clamp to buffer length 108 | if(quantity > BUFFER_LENGTH){ 109 | quantity = BUFFER_LENGTH; 110 | } 111 | // perform blocking read into buffer 112 | uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); 113 | // set rx buffer iterator vars 114 | rxBufferIndex = 0; 115 | rxBufferLength = read; 116 | 117 | return read; 118 | } 119 | 120 | uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { 121 | return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop); 122 | } 123 | 124 | uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) 125 | { 126 | return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); 127 | } 128 | 129 | uint8_t TwoWire::requestFrom(int address, int quantity) 130 | { 131 | return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); 132 | } 133 | 134 | uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) 135 | { 136 | return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); 137 | } 138 | 139 | void TwoWire::beginTransmission(uint8_t address) 140 | { 141 | // indicate that we are transmitting 142 | transmitting = 1; 143 | // set address of targeted slave 144 | txAddress = address; 145 | // reset tx buffer iterator vars 146 | txBufferIndex = 0; 147 | txBufferLength = 0; 148 | } 149 | 150 | void TwoWire::beginTransmission(int address) 151 | { 152 | beginTransmission((uint8_t)address); 153 | } 154 | 155 | // 156 | // Originally, 'endTransmission' was an f(void) function. 157 | // It has been modified to take one parameter indicating 158 | // whether or not a STOP should be performed on the bus. 159 | // Calling endTransmission(false) allows a sketch to 160 | // perform a repeated start. 161 | // 162 | // WARNING: Nothing in the library keeps track of whether 163 | // the bus tenure has been properly ended with a STOP. It 164 | // is very possible to leave the bus in a hung state if 165 | // no call to endTransmission(true) is made. Some I2C 166 | // devices will behave oddly if they do not see a STOP. 167 | // 168 | uint8_t TwoWire::endTransmission(uint8_t sendStop) 169 | { 170 | // transmit buffer (blocking) 171 | uint8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); 172 | // reset tx buffer iterator vars 173 | txBufferIndex = 0; 174 | txBufferLength = 0; 175 | // indicate that we are done transmitting 176 | transmitting = 0; 177 | return ret; 178 | } 179 | 180 | // This provides backwards compatibility with the original 181 | // definition, and expected behaviour, of endTransmission 182 | // 183 | uint8_t TwoWire::endTransmission(void) 184 | { 185 | return endTransmission(true); 186 | } 187 | 188 | // must be called in: 189 | // slave tx event callback 190 | // or after beginTransmission(address) 191 | size_t TwoWire::write(uint8_t data) 192 | { 193 | if(transmitting){ 194 | // in master transmitter mode 195 | // don't bother if buffer is full 196 | if(txBufferLength >= BUFFER_LENGTH){ 197 | setWriteError(); 198 | return 0; 199 | } 200 | // put byte in tx buffer 201 | txBuffer[txBufferIndex] = data; 202 | ++txBufferIndex; 203 | // update amount in buffer 204 | txBufferLength = txBufferIndex; 205 | }else{ 206 | // in slave send mode 207 | // reply to master 208 | twi_transmit(&data, 1); 209 | } 210 | return 1; 211 | } 212 | 213 | // must be called in: 214 | // slave tx event callback 215 | // or after beginTransmission(address) 216 | size_t TwoWire::write(const uint8_t *data, size_t quantity) 217 | { 218 | if(transmitting){ 219 | // in master transmitter mode 220 | for(size_t i = 0; i < quantity; ++i){ 221 | write(data[i]); 222 | } 223 | }else{ 224 | // in slave send mode 225 | // reply to master 226 | twi_transmit(data, quantity); 227 | } 228 | return quantity; 229 | } 230 | 231 | // must be called in: 232 | // slave rx event callback 233 | // or after requestFrom(address, numBytes) 234 | int TwoWire::available(void) 235 | { 236 | return rxBufferLength - rxBufferIndex; 237 | } 238 | 239 | // must be called in: 240 | // slave rx event callback 241 | // or after requestFrom(address, numBytes) 242 | int TwoWire::read(void) 243 | { 244 | int value = -1; 245 | 246 | // get each successive byte on each call 247 | if(rxBufferIndex < rxBufferLength){ 248 | value = rxBuffer[rxBufferIndex]; 249 | ++rxBufferIndex; 250 | } 251 | 252 | return value; 253 | } 254 | 255 | // must be called in: 256 | // slave rx event callback 257 | // or after requestFrom(address, numBytes) 258 | int TwoWire::peek(void) 259 | { 260 | int value = -1; 261 | 262 | if(rxBufferIndex < rxBufferLength){ 263 | value = rxBuffer[rxBufferIndex]; 264 | } 265 | 266 | return value; 267 | } 268 | 269 | void TwoWire::flush(void) 270 | { 271 | // XXX: to be implemented. 272 | } 273 | 274 | uint8_t TwoWire::getLastAddress() 275 | { 276 | //return TWDR >> 1; // retrieve address from last byte on the bus 277 | return rxAddress; 278 | }; 279 | 280 | // behind the scenes function that is called when data is received 281 | void TwoWire::onReceiveService(uint8_t addr, uint8_t* inBytes, int numBytes) 282 | { 283 | // don't bother if user hasn't registered a callback 284 | if(!user_onReceive){ 285 | return; 286 | } 287 | // don't bother if rx buffer is in use by a master requestFrom() op 288 | // i know this drops data, but it allows for slight stupidity 289 | // meaning, they may not have read all the master requestFrom() data yet 290 | if(rxBufferIndex < rxBufferLength){ 291 | return; 292 | } 293 | // copy twi rx buffer into local read buffer 294 | // this enables new reads to happen in parallel 295 | for(uint8_t i = 0; i < numBytes; ++i){ 296 | rxBuffer[i] = inBytes[i]; 297 | } 298 | // set rx iterator vars 299 | rxBufferIndex = 0; 300 | rxBufferLength = numBytes; 301 | rxAddress = addr; 302 | // alert user program 303 | user_onReceive(numBytes); 304 | } 305 | 306 | // behind the scenes function that is called when data is requested 307 | void TwoWire::onRequestService(uint8_t addr) 308 | { 309 | rxAddress = addr; 310 | // don't bother if user hasn't registered a callback 311 | if(!user_onRequest){ 312 | return; 313 | } 314 | // reset tx buffer iterator vars 315 | // !!! this will kill any pending pre-master sendTo() activity 316 | txBufferIndex = 0; 317 | txBufferLength = 0; 318 | // alert user program 319 | user_onRequest(); 320 | } 321 | 322 | // sets function called on slave write 323 | void TwoWire::onReceive( void (*function)(int) ) 324 | { 325 | user_onReceive = function; 326 | } 327 | 328 | // sets function called on slave read 329 | void TwoWire::onRequest( void (*function)(void) ) 330 | { 331 | user_onRequest = function; 332 | } 333 | 334 | // Preinstantiate Objects ////////////////////////////////////////////////////// 335 | 336 | TwoWire Wire = TwoWire(); 337 | -------------------------------------------------------------------------------- /Arduino_I2C_BPF_RX/Wire_.h: -------------------------------------------------------------------------------- 1 | /* 2 | TwoWire.h - TWI/I2C library for Arduino & Wiring 3 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | You should have received a copy of the GNU Lesser General Public 13 | License along with this library; if not, write to the Free Software 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 15 | Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts 16 | */ 17 | 18 | #ifndef TwoWire_h 19 | #define TwoWire_h 20 | 21 | #include 22 | #include "Stream.h" 23 | 24 | #define BUFFER_LENGTH 32 25 | 26 | // WIRE_HAS_END means Wire has end() 27 | #define WIRE_HAS_END 1 28 | 29 | class TwoWire : public Stream 30 | { 31 | private: 32 | static uint8_t rxBuffer[]; 33 | static uint8_t rxBufferIndex; 34 | static uint8_t rxBufferLength; 35 | static uint8_t rxAddress; 36 | 37 | static uint8_t txAddress; 38 | static uint8_t txBuffer[]; 39 | static uint8_t txBufferIndex; 40 | static uint8_t txBufferLength; 41 | 42 | static uint8_t transmitting; 43 | static void (*user_onRequest)(void); 44 | static void (*user_onReceive)(int); 45 | static void onRequestService(uint8_t); 46 | static void onReceiveService(uint8_t, uint8_t*, int); 47 | public: 48 | TwoWire(); 49 | void begin(); 50 | void begin(uint8_t addr, uint8_t mask = 0xFF); 51 | void begin(int, int); 52 | void end(); 53 | void setClock(uint32_t); 54 | void beginTransmission(uint8_t); 55 | void beginTransmission(int); 56 | uint8_t endTransmission(void); 57 | uint8_t endTransmission(uint8_t); 58 | uint8_t requestFrom(uint8_t, uint8_t); 59 | uint8_t requestFrom(uint8_t, uint8_t, uint8_t); 60 | uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t); 61 | uint8_t requestFrom(int, int); 62 | uint8_t requestFrom(int, int, int); 63 | virtual size_t write(uint8_t); 64 | virtual size_t write(const uint8_t *, size_t); 65 | virtual int available(void); 66 | virtual int read(void); 67 | virtual int peek(void); 68 | virtual void flush(void); 69 | uint8_t getLastAddress(); 70 | void onReceive( void (*)(int) ); 71 | void onRequest( void (*)(void) ); 72 | 73 | inline size_t write(unsigned long n) { return write((uint8_t)n); } 74 | inline size_t write(long n) { return write((uint8_t)n); } 75 | inline size_t write(unsigned int n) { return write((uint8_t)n); } 76 | inline size_t write(int n) { return write((uint8_t)n); } 77 | using Print::write; 78 | }; 79 | 80 | extern TwoWire Wire; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Arduino_I2C_BPF_RX/twi_.c: -------------------------------------------------------------------------------- 1 | /* 2 | twi.c - TWI/I2C library for Wiring & Arduino 3 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | You should have received a copy of the GNU Lesser General Public 13 | License along with this library; if not, write to the Free Software 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 15 | Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | //#include "twi_.h" 25 | #include "Arduino.h" // for digitalWrite 26 | 27 | #ifndef cbi 28 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 29 | #endif 30 | 31 | #ifndef sbi 32 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 33 | #endif 34 | 35 | #include "pins_arduino.h" 36 | #include "twi_.h" 37 | 38 | static volatile uint8_t twi_state; 39 | static volatile uint8_t twi_slarw; 40 | static volatile uint8_t twi_sendStop; // should the transaction end with a stop 41 | static volatile uint8_t twi_inRepStart; // in the middle of a repeated start 42 | 43 | static void (*twi_onSlaveTransmit)(uint8_t); 44 | static void (*twi_onSlaveReceive)(uint8_t, uint8_t*, int); 45 | 46 | static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; 47 | static volatile uint8_t twi_masterBufferIndex; 48 | static volatile uint8_t twi_masterBufferLength; 49 | 50 | static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; 51 | static volatile uint8_t twi_txBufferIndex; 52 | static volatile uint8_t twi_txBufferLength; 53 | 54 | static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; 55 | static volatile uint8_t twi_rxBufferIndex; 56 | 57 | static volatile uint8_t twi_error; 58 | 59 | /* 60 | * Function twi_init 61 | * Desc readys twi pins and sets twi bitrate 62 | * Input none 63 | * Output none 64 | */ 65 | void twi_init(void) 66 | { 67 | // initialize state 68 | twi_state = TWI_READY; 69 | twi_sendStop = true; // default value 70 | twi_inRepStart = false; 71 | 72 | // activate internal pullups for twi. 73 | digitalWrite(SDA, 1); 74 | digitalWrite(SCL, 1); 75 | 76 | // initialize twi prescaler and bit rate 77 | cbi(TWSR, TWPS0); 78 | cbi(TWSR, TWPS1); 79 | TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; 80 | 81 | /* twi bit rate formula from atmega128 manual pg 204 82 | SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) 83 | note: TWBR should be 10 or higher for master mode 84 | It is 72 for a 16mhz Wiring board with 100kHz TWI */ 85 | 86 | // enable twi module, acks, and twi interrupt 87 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); 88 | } 89 | 90 | /* 91 | * Function twi_disable 92 | * Desc disables twi pins 93 | * Input none 94 | * Output none 95 | */ 96 | void twi_disable(void) 97 | { 98 | // disable twi module, acks, and twi interrupt 99 | TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA)); 100 | 101 | // deactivate internal pullups for twi. 102 | digitalWrite(SDA, 0); 103 | digitalWrite(SCL, 0); 104 | } 105 | 106 | /* 107 | * Function twi_slaveInit 108 | * Desc sets slave address and enables interrupt 109 | * Input none 110 | * Output none 111 | */ 112 | void twi_setAddress(uint8_t address, uint8_t mask) 113 | { 114 | // set twi slave address (skip over TWGCE bit) 115 | TWAR = address << 1; 116 | TWAMR = mask << 1; 117 | } 118 | 119 | /* 120 | * Function twi_setClock 121 | * Desc sets twi bit rate 122 | * Input Clock Frequency 123 | * Output none 124 | */ 125 | void twi_setFrequency(uint32_t frequency) 126 | { 127 | TWBR = ((F_CPU / frequency) - 16) / 2; 128 | 129 | /* twi bit rate formula from atmega128 manual pg 204 130 | SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) 131 | note: TWBR should be 10 or higher for master mode 132 | It is 72 for a 16mhz Wiring board with 100kHz TWI */ 133 | } 134 | 135 | /* 136 | * Function twi_readFrom 137 | * Desc attempts to become twi bus master and read a 138 | * series of bytes from a device on the bus 139 | * Input address: 7bit i2c device address 140 | * data: pointer to byte array 141 | * length: number of bytes to read into array 142 | * sendStop: Boolean indicating whether to send a stop at the end 143 | * Output number of bytes read 144 | */ 145 | uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) 146 | { 147 | uint8_t i; 148 | 149 | // ensure data will fit into buffer 150 | if(TWI_BUFFER_LENGTH < length){ 151 | return 0; 152 | } 153 | 154 | // wait until twi is ready, become master receiver 155 | while(TWI_READY != twi_state){ 156 | continue; 157 | } 158 | twi_state = TWI_MRX; 159 | twi_sendStop = sendStop; 160 | // reset error state (0xFF.. no error occured) 161 | twi_error = 0xFF; 162 | 163 | // initialize buffer iteration vars 164 | twi_masterBufferIndex = 0; 165 | twi_masterBufferLength = length-1; // This is not intuitive, read on... 166 | // On receive, the previously configured ACK/NACK setting is transmitted in 167 | // response to the received byte before the interrupt is signalled. 168 | // Therefor we must actually set NACK when the _next_ to last byte is 169 | // received, causing that NACK to be sent in response to receiving the last 170 | // expected byte of data. 171 | 172 | // build sla+w, slave device address + w bit 173 | twi_slarw = TW_READ; 174 | twi_slarw |= address << 1; 175 | 176 | if (true == twi_inRepStart) { 177 | // if we're in the repeated start state, then we've already sent the start, 178 | // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. 179 | // We need to remove ourselves from the repeated start state before we enable interrupts, 180 | // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning 181 | // up. Also, don't enable the START interrupt. There may be one pending from the 182 | // repeated start that we sent ourselves, and that would really confuse things. 183 | twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR 184 | do { 185 | TWDR = twi_slarw; 186 | } while(TWCR & _BV(TWWC)); 187 | TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START 188 | } 189 | else 190 | // send start condition 191 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); 192 | 193 | // wait for read operation to complete 194 | while(TWI_MRX == twi_state){ 195 | continue; 196 | } 197 | 198 | if (twi_masterBufferIndex < length) 199 | length = twi_masterBufferIndex; 200 | 201 | // copy twi buffer to data 202 | for(i = 0; i < length; ++i){ 203 | data[i] = twi_masterBuffer[i]; 204 | } 205 | 206 | return length; 207 | } 208 | 209 | /* 210 | * Function twi_writeTo 211 | * Desc attempts to become twi bus master and write a 212 | * series of bytes to a device on the bus 213 | * Input address: 7bit i2c device address 214 | * data: pointer to byte array 215 | * length: number of bytes in array 216 | * wait: boolean indicating to wait for write or not 217 | * sendStop: boolean indicating whether or not to send a stop at the end 218 | * Output 0 .. success 219 | * 1 .. length to long for buffer 220 | * 2 .. address send, NACK received 221 | * 3 .. data send, NACK received 222 | * 4 .. other twi error (lost bus arbitration, bus error, ..) 223 | */ 224 | uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) 225 | { 226 | uint8_t i; 227 | 228 | // ensure data will fit into buffer 229 | if(TWI_BUFFER_LENGTH < length){ 230 | return 1; 231 | } 232 | 233 | // wait until twi is ready, become master transmitter 234 | while(TWI_READY != twi_state){ 235 | continue; 236 | } 237 | twi_state = TWI_MTX; 238 | twi_sendStop = sendStop; 239 | // reset error state (0xFF.. no error occured) 240 | twi_error = 0xFF; 241 | 242 | // initialize buffer iteration vars 243 | twi_masterBufferIndex = 0; 244 | twi_masterBufferLength = length; 245 | 246 | // copy data to twi buffer 247 | for(i = 0; i < length; ++i){ 248 | twi_masterBuffer[i] = data[i]; 249 | } 250 | 251 | // build sla+w, slave device address + w bit 252 | twi_slarw = TW_WRITE; 253 | twi_slarw |= address << 1; 254 | 255 | // if we're in a repeated start, then we've already sent the START 256 | // in the ISR. Don't do it again. 257 | // 258 | if (true == twi_inRepStart) { 259 | // if we're in the repeated start state, then we've already sent the start, 260 | // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. 261 | // We need to remove ourselves from the repeated start state before we enable interrupts, 262 | // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning 263 | // up. Also, don't enable the START interrupt. There may be one pending from the 264 | // repeated start that we sent outselves, and that would really confuse things. 265 | twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR 266 | do { 267 | TWDR = twi_slarw; 268 | } while(TWCR & _BV(TWWC)); 269 | TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START 270 | } 271 | else 272 | // send start condition 273 | TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs 274 | 275 | // wait for write operation to complete 276 | while(wait && (TWI_MTX == twi_state)){ 277 | continue; 278 | } 279 | 280 | if (twi_error == 0xFF) 281 | return 0; // success 282 | else if (twi_error == TW_MT_SLA_NACK) 283 | return 2; // error: address send, nack received 284 | else if (twi_error == TW_MT_DATA_NACK) 285 | return 3; // error: data send, nack received 286 | else 287 | return 4; // other twi error 288 | } 289 | 290 | /* 291 | * Function twi_transmit 292 | * Desc fills slave tx buffer with data 293 | * must be called in slave tx event callback 294 | * Input data: pointer to byte array 295 | * length: number of bytes in array 296 | * Output 1 length too long for buffer 297 | * 2 not slave transmitter 298 | * 0 ok 299 | */ 300 | uint8_t twi_transmit(const uint8_t* data, uint8_t length) 301 | { 302 | uint8_t i; 303 | 304 | // ensure data will fit into buffer 305 | if(TWI_BUFFER_LENGTH < (twi_txBufferLength+length)){ 306 | return 1; 307 | } 308 | 309 | // ensure we are currently a slave transmitter 310 | if(TWI_STX != twi_state){ 311 | return 2; 312 | } 313 | 314 | // set length and copy data into tx buffer 315 | for(i = 0; i < length; ++i){ 316 | twi_txBuffer[twi_txBufferLength+i] = data[i]; 317 | } 318 | twi_txBufferLength += length; 319 | 320 | return 0; 321 | } 322 | 323 | /* 324 | * Function twi_attachSlaveRxEvent 325 | * Desc sets function called before a slave read operation 326 | * Input function: callback function to use 327 | * Output none 328 | */ 329 | void twi_attachSlaveRxEvent( void (*function)(uint8_t, uint8_t*, int) ) 330 | { 331 | twi_onSlaveReceive = function; 332 | } 333 | 334 | /* 335 | * Function twi_attachSlaveTxEvent 336 | * Desc sets function called before a slave write operation 337 | * Input function: callback function to use 338 | * Output none 339 | */ 340 | void twi_attachSlaveTxEvent( void (*function)(uint8_t) ) 341 | { 342 | twi_onSlaveTransmit = function; 343 | } 344 | 345 | /* 346 | * Function twi_reply 347 | * Desc sends byte or readys receive line 348 | * Input ack: byte indicating to ack or to nack 349 | * Output none 350 | */ 351 | void twi_reply(uint8_t ack) 352 | { 353 | // transmit master read ready signal, with or without ack 354 | if(ack){ 355 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); 356 | }else{ 357 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); 358 | } 359 | } 360 | 361 | /* 362 | * Function twi_stop 363 | * Desc relinquishes bus master status 364 | * Input none 365 | * Output none 366 | */ 367 | void twi_stop(void) 368 | { 369 | // send stop condition 370 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); 371 | 372 | // wait for stop condition to be exectued on bus 373 | // TWINT is not set after a stop condition! 374 | while(TWCR & _BV(TWSTO)){ 375 | continue; 376 | } 377 | 378 | // update twi state 379 | twi_state = TWI_READY; 380 | } 381 | 382 | /* 383 | * Function twi_releaseBus 384 | * Desc releases bus control 385 | * Input none 386 | * Output none 387 | */ 388 | void twi_releaseBus(void) 389 | { 390 | // release bus 391 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); 392 | 393 | // update twi state 394 | twi_state = TWI_READY; 395 | } 396 | 397 | ISR(TWI_vect) 398 | { 399 | switch(TW_STATUS){ 400 | // All Master 401 | case TW_START: // sent start condition 402 | case TW_REP_START: // sent repeated start condition 403 | // copy device address and r/w bit to output register and ack 404 | TWDR = twi_slarw; 405 | twi_reply(1); 406 | break; 407 | 408 | // Master Transmitter 409 | case TW_MT_SLA_ACK: // slave receiver acked address 410 | case TW_MT_DATA_ACK: // slave receiver acked data 411 | // if there is data to send, send it, otherwise stop 412 | if(twi_masterBufferIndex < twi_masterBufferLength){ 413 | // copy data to output register and ack 414 | TWDR = twi_masterBuffer[twi_masterBufferIndex++]; 415 | twi_reply(1); 416 | }else{ 417 | if (twi_sendStop) 418 | twi_stop(); 419 | else { 420 | twi_inRepStart = true; // we're gonna send the START 421 | // don't enable the interrupt. We'll generate the start, but we 422 | // avoid handling the interrupt until we're in the next transaction, 423 | // at the point where we would normally issue the start. 424 | TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; 425 | twi_state = TWI_READY; 426 | } 427 | } 428 | break; 429 | case TW_MT_SLA_NACK: // address sent, nack received 430 | twi_error = TW_MT_SLA_NACK; 431 | twi_stop(); 432 | break; 433 | case TW_MT_DATA_NACK: // data sent, nack received 434 | twi_error = TW_MT_DATA_NACK; 435 | twi_stop(); 436 | break; 437 | case TW_MT_ARB_LOST: // lost bus arbitration 438 | twi_error = TW_MT_ARB_LOST; 439 | twi_releaseBus(); 440 | break; 441 | 442 | // Master Receiver 443 | case TW_MR_DATA_ACK: // data received, ack sent 444 | // put byte into buffer 445 | twi_masterBuffer[twi_masterBufferIndex++] = TWDR; 446 | case TW_MR_SLA_ACK: // address sent, ack received 447 | // ack if more bytes are expected, otherwise nack 448 | if(twi_masterBufferIndex < twi_masterBufferLength){ 449 | twi_reply(1); 450 | }else{ 451 | twi_reply(0); 452 | } 453 | break; 454 | case TW_MR_DATA_NACK: // data received, nack sent 455 | // put final byte into buffer 456 | twi_masterBuffer[twi_masterBufferIndex++] = TWDR; 457 | if (twi_sendStop) 458 | twi_stop(); 459 | else { 460 | twi_inRepStart = true; // we're gonna send the START 461 | // don't enable the interrupt. We'll generate the start, but we 462 | // avoid handling the interrupt until we're in the next transaction, 463 | // at the point where we would normally issue the start. 464 | TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; 465 | twi_state = TWI_READY; 466 | } 467 | break; 468 | case TW_MR_SLA_NACK: // address sent, nack received 469 | twi_stop(); 470 | break; 471 | // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case 472 | 473 | // Slave Receiver 474 | case TW_SR_SLA_ACK: // addressed, returned ack 475 | case TW_SR_GCALL_ACK: // addressed generally, returned ack 476 | case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack 477 | case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack 478 | // enter slave receiver mode 479 | twi_state = TWI_SRX; 480 | twi_slarw = TWDR; 481 | // indicate that rx buffer can be overwritten and ack 482 | twi_rxBufferIndex = 0; 483 | twi_reply(1); 484 | break; 485 | case TW_SR_DATA_ACK: // data received, returned ack 486 | case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack 487 | // if there is still room in the rx buffer 488 | if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ 489 | // put byte in buffer and ack 490 | twi_rxBuffer[twi_rxBufferIndex++] = TWDR; 491 | twi_reply(1); 492 | }else{ 493 | // otherwise nack 494 | twi_reply(0); 495 | } 496 | break; 497 | case TW_SR_STOP: // stop or repeated start condition received 498 | // ack future responses and leave slave receiver state 499 | twi_releaseBus(); 500 | // put a null char after data if there's room 501 | if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ 502 | twi_rxBuffer[twi_rxBufferIndex] = '\0'; 503 | } 504 | // callback to user defined callback 505 | twi_onSlaveReceive(twi_slarw >> 1, twi_rxBuffer, twi_rxBufferIndex); 506 | // since we submit rx buffer to "wire" library, we can reset it 507 | twi_rxBufferIndex = 0; 508 | break; 509 | case TW_SR_DATA_NACK: // data received, returned nack 510 | case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack 511 | // nack back at master 512 | twi_reply(0); 513 | break; 514 | 515 | // Slave Transmitter 516 | case TW_ST_SLA_ACK: // addressed, returned ack 517 | case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack 518 | // enter slave transmitter mode 519 | twi_state = TWI_STX; 520 | // ready the tx buffer index for iteration 521 | twi_txBufferIndex = 0; 522 | // set tx buffer length to be zero, to verify if user changes it 523 | twi_txBufferLength = 0; 524 | twi_slarw = TWDR; 525 | // request for txBuffer to be filled and length to be set 526 | // note: user must call twi_transmit(bytes, length) to do this 527 | twi_onSlaveTransmit(twi_slarw >> 1); 528 | // if they didn't change buffer & length, initialize it 529 | if(0 == twi_txBufferLength){ 530 | twi_txBufferLength = 1; 531 | twi_txBuffer[0] = 0x00; 532 | } 533 | // transmit first byte from buffer, fall 534 | case TW_ST_DATA_ACK: // byte sent, ack returned 535 | // copy data to output register 536 | TWDR = twi_txBuffer[twi_txBufferIndex++]; 537 | // if there is more to send, ack, otherwise nack 538 | if(twi_txBufferIndex < twi_txBufferLength){ 539 | twi_reply(1); 540 | }else{ 541 | twi_reply(0); 542 | } 543 | break; 544 | case TW_ST_DATA_NACK: // received nack, we are done 545 | case TW_ST_LAST_DATA: // received ack, but we are done already! 546 | // ack future responses 547 | twi_reply(1); 548 | // leave slave receiver state 549 | twi_state = TWI_READY; 550 | break; 551 | 552 | // All 553 | case TW_NO_INFO: // no state information 554 | break; 555 | case TW_BUS_ERROR: // bus error, illegal stop/start 556 | twi_error = TW_BUS_ERROR; 557 | twi_stop(); 558 | break; 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /Arduino_I2C_BPF_RX/twi_.h: -------------------------------------------------------------------------------- 1 | /* 2 | twi.h - TWI/I2C library for Wiring & Arduino 3 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | You should have received a copy of the GNU Lesser General Public 13 | License along with this library; if not, write to the Free Software 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 15 | */ 16 | 17 | #ifndef twi_h 18 | #define twi_h 19 | 20 | #include 21 | 22 | //#define ATMEGA8 23 | 24 | #ifndef TWI_FREQ 25 | #define TWI_FREQ 100000L 26 | #endif 27 | 28 | #ifndef TWI_BUFFER_LENGTH 29 | #define TWI_BUFFER_LENGTH 32 30 | #endif 31 | 32 | #define TWI_READY 0 33 | #define TWI_MRX 1 34 | #define TWI_MTX 2 35 | #define TWI_SRX 3 36 | #define TWI_STX 4 37 | 38 | void twi_init(void); 39 | void twi_disable(void); 40 | void twi_setAddress(uint8_t, uint8_t); 41 | void twi_setFrequency(uint32_t); 42 | uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t); 43 | uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t); 44 | uint8_t twi_transmit(const uint8_t*, uint8_t); 45 | void twi_attachSlaveRxEvent( void (*)(uint8_t, uint8_t*, int) ); 46 | void twi_attachSlaveTxEvent( void (*)(uint8_t) ); 47 | void twi_reply(uint8_t); 48 | void twi_stop(void); 49 | void twi_releaseBus(void); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Arduino_uSDR_Pico_FFT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Arduino_uSDR_Pico_FFT.png -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/Arduino_uSDX_Pico_FFT.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Arduino_uSDX_Pico.ino 4 | * uSDX_PICO running in Raspberry Pi Pico RP2040 with TFT Display compiled in Arduino IDE 5 | * 6 | * Created: May 2022 7 | * Author: Klaus Fensterseifer 8 | * https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 9 | * 10 | * 11 | 12 | 13 | >>Use Boards Manager: Arduino Mbed OS RP2040 Boards 14 | >>Do not use EarlePhilhower library (it is just conflitant with Mbed) 15 | 16 | >>Lib used: TFT_eSPI by Bodmer 17 | 18 | 19 | >>On Ubuntu, to allow program Pico direct from Arduino IDE, run once: 20 | ~/.arduino15/packages/arduino/hardware/mbed_rp2040/4.0.2$ sudo ./post_install.sh 21 | >>Obs.: Compiled file Arduino_uSDX_Pico_FFT.ino.uf2 generated at /tmp/arduino-sketch-... 22 | 23 | 24 | 25 | >>Mods in the Library files to fit to the project: 26 | ================================================ 27 | 28 | 29 | -------------------------------------------------------------- 30 | -------------------------------------------------------------- 31 | >>TFT_eSPI LIBRARY: 32 | ---------------------------------------------------- 33 | >>Change Arduino/libraries/TFT_eSPI/User_Setup_Select.h 34 | >>Comment: 35 | //#include // Default setup is root library folder 36 | >>UnComment: 37 | #include // Setup file for RP2040 with SPI ILI9341 38 | 39 | ---------------------------------------------------- 40 | >>On User_Setups/Setup60_RP2040_ILI9341.h 41 | >>Uncomment: 42 | #define ILI9341_DRIVER 43 | #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 44 | >>Choose the SPI pins (SPI1): 45 | // For the Pico use these #define lines 46 | #define TFT_MISO 12 //0 RX 47 | #define TFT_MOSI 11 //3 TX 48 | #define TFT_SCLK 10 //2 49 | #define TFT_CS 13 //20 // Chip select control pin 50 | #define TFT_DC 4 //18 // Data Command control pin 51 | #define TFT_RST 5 //19 // Reset pin (could connect to Arduino RESET pin) 52 | //#define TFT_BL // LED back-light 53 | //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 54 | 55 | >>Leave the fonts available: 56 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 57 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 58 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 59 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 60 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 61 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 62 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 63 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 64 | 65 | #define SMOOTH_FONT 66 | 67 | >>Choose SPI 1 68 | #define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if SPI1 pins used 69 | 70 | 71 | 72 | -------------------------------------------------------------- 73 | >>In case your ILI9341 looks 90 degree view: 74 | For ILI9341 + RP2040, try change TFT_eSPI/TFT_Drivers/ILI9341_Defines.h 75 | #define TFT_WIDTH 320 //240 76 | #define TFT_HEIGHT 240 //320 77 | >>(this was reported as a issue to TFT_eSPI github project: https://github.com/Bodmer/TFT_eSPI/issues/1725) 78 | >>(there are also options to set: #if defined (ILI9341_DRIVER) || defined (ILI9341_2_DRIVER)) 79 | >>In combination, you can try to change display_tft.h #define ROTATION_SETUP 0 to 1 80 | 81 | 82 | -------------------------------------------------------------- 83 | -------------------------------------------------------------- 84 | >>The following mods will correct this beginner usual error: 85 | Compilation error: 'Wire1' was not declared in this scope 86 | 87 | >>For Wire/I2C, look the pins at 88 | .arduino15/packages/arduino/hardware/mbed_rp2040/4.0.2/variants/RASPBERRY_PI_PICO/pins_arduino.h 89 | >>(.arduino15 is a hidden directory, set Windows/Linux to show hidden files/directories) 90 | >>change the pins for I2C0 and include the pins for I2C1: 91 | // Wire 92 | #define PIN_WIRE_SDA (16u) //I2C0 93 | #define PIN_WIRE_SCL (17u) //I2C0 94 | #define PIN_WIRE_SDA1 (18u) //included I2C1 95 | #define PIN_WIRE_SCL1 (19u) //included I2C1 96 | 97 | #define WIRE_HOWMANY (2) //included I2C1 default was 1 98 | #define I2C_SDA (digitalPinToPinName(PIN_WIRE_SDA)) //I2C0 99 | #define I2C_SCL (digitalPinToPinName(PIN_WIRE_SCL)) //I2C0 100 | #define I2C_SDA1 (digitalPinToPinName(PIN_WIRE_SDA1)) //included I2C1 101 | #define I2C_SCL1 (digitalPinToPinName(PIN_WIRE_SCL1)) //included I2C1 102 | 103 | 104 | -------------------------------------------------------------- 105 | >>Check on hmi.cpp: 106 | #define HMI_MULFREQ 1 // Factor between HMI and actual frequency 107 | // Set to 1, 2 or 4 for certain types of mixer 108 | 109 | -------------------------------------------------------------- 110 | >>Check on Si5351.cpp to set the correct Si5351 internal frequency: 111 | #define SI_XTAL_FREQ 25001414UL // Replace with measured crystal frequency of XTAL for CL = 10pF (default) 112 | 113 | 114 | -------------------------------------------------------------- 115 | >>Check ENCODER selection at hmi.cpp (change if necessary) 116 | #define ENCODER_TYPE ENCODER_FALL //choose what encoder is used 117 | #define ENCODER_DIRECTION ENCODER_CW_A_FALL_B_HIGH //direction related to B signal level 118 | 119 | -------------------------------------------------------------- 120 | >>Check the correct RX I and Q inputs at dsp.h 121 | #define EXCHANGE_I_Q 1 //include or comment this #define in case the LSB/USB and the lower/upper frequency of waterfall display are reverted - hardware dependent 122 | 123 | -------------------------------------------------------------- 124 | >>Choose one TX method at uSDR.h 125 | #define TX_METHOD I_Q_QSE // uSDR_Pico original project generating I and Q signal to a QSE mixer 126 | //#define TX_METHOD PHASE_AMPLITUDE // DO NOT USE - is not ready - used for Class E RF amplifier - see description at: uSDX_TX_PhaseAmpl.cpp 127 | 128 | -------------------------------------------------------------- 129 | >> Set if using Arduino Pro Mini instead of PCF8574 to control the filter relays at relay.h 130 | #define I2C_Arduino_Pro_Mini 1 //=1 when I2C BPF and Atten is commanded with Arduino Pro Mini (allow SWR reading) 131 | 132 | -------------------------------------------------------------- 133 | >> I made a #define PY2KLA_setup 1 at uSDR.h to set my configuration on other files (you can search for PY2KLA_setup on all files to look for configurations) 134 | Comment out this #define and set your own configuration 135 | 136 | 137 | >> Have fun 138 | >> PY2KLA/PY4LL KLaus F. 139 | 140 | 141 | */ 142 | 143 | 144 | #include "uSDR.h" 145 | #include "hmi.h" 146 | 147 | 148 | 149 | 150 | 151 | //--------------------------------------------------------------------------- 152 | //--------------------------------------------------------------------------- 153 | void setup() { 154 | 155 | //RP2040 initialize the GPIOs as inputs with pulldown as default, and this is like PTT active 156 | //it needs a strong pullup = 1K to 3v3 on pin GPIO15 (pin 20) to force high level during initialization 157 | gpio_pull_up(GP_PTT); // PTT pullup (it takes about 1s to reach this point after power up / reset) 158 | gpio_set_dir(GP_PTT, GPIO_IN); // PTT input (just to confirm) - true for out, false for in 159 | 160 | 161 | // initialize digital pin LED_BUILTIN as an output. 162 | //pinMode(LED_BUILTIN, OUTPUT); 163 | gpio_init_mask(1< Serialx = Serial1 //UART0 /dev/ttyUSB0 168 | //if you choose Serialx = Serial on uSDR.h - it will use Pico's USB and save the use of USB to serial converter and leave 2 spare pins 169 | Serialx.begin(115200); 170 | 171 | uint16_t tim = millis(); 172 | 173 | //special jobs while waiting initial display print 174 | uSDR_setup0(); //write something into display while waiting for the serial and DFLASH read 175 | hmi_init0(); //it could take some time to read all DFLASH data 176 | 177 | // some delay required for Serial to open 178 | while((millis() - tim) < 5000) //try for 5s to connect to serial 179 | { 180 | //digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 181 | gpio_set_mask(1< 83 | #include 84 | }; 85 | #include "Dflash.h" 86 | #include "hmi.h" 87 | #include "pico/multicore.h" 88 | 89 | 90 | //#define DFLASH_debug 10 91 | 92 | #ifdef DFLASH_debug 93 | //defines used to print the DFlash variable to help debug 94 | #define PRT_LN(x) Serialx.println(x) //print the states 95 | #define PRT(x) Serialx.print(x) //print the states 96 | #else 97 | #define PRT_LN(x) //do not print the states 98 | #define PRT(x) //do not print the states 99 | #endif 100 | 101 | 102 | // 10 years in microseconds 103 | //#define MULTICORE_LOCKOUT_TIMEOUT_us (uint64_t)(10 * 365 * 24 * 60 * 60 * 1000 * 1000) 104 | // 10s 105 | #define MULTICORE_LOCKOUT_TIMEOUT_us (uint64_t)(10 * 1000 * 1000) 106 | 107 | 108 | 109 | 110 | /* 111 | bool Dflash_empty = false; 112 | uint16_t npage; //actual page 113 | uint16_t nblock; //last data flash block 114 | */ 115 | 116 | 117 | int16_t last_block; //last block with data on the DFLASH - int to use value -1 as DFLASH empty 118 | uint16_t DFLASH_in_use = 0; //used to stop core1 for wirting on DFLASH memory 119 | 120 | 121 | 122 | //*********************************************************************** 123 | // 124 | // Read the block num from the data flash memory 125 | // 126 | //*********************************************************************** 127 | uint16_t Dflash_read_block(uint16_t block_num, uint8_t *data_bl, uint16_t data_siz) 128 | { 129 | uint16_t ndata; 130 | uint32_t addr; 131 | uint8_t data_block[DATA_BLOCK_SIZE]; 132 | uint8_t chksum; 133 | uint8_t count_FFs = 0; 134 | uint32_t freq; 135 | 136 | if(data_siz > DATA_BLOCK_SIZE-1) // -1 to leave 1 byte for chksum 137 | { 138 | PRT_LN("error read data_siz > DATA_BLOCK_SIZE"); 139 | //return false; 140 | data_siz = DATA_BLOCK_SIZE-1; 141 | } 142 | 143 | 144 | // The ARM cores have the entire address space of the flash memory-mapped. 145 | // Read the flash value directly as if it were in RAM 146 | // The RAM is included in the address space (RAM space = XIP_BASE) 147 | 148 | 149 | addr = DFLASH_ADDR_READ(block_num * DATA_BLOCK_SIZE); 150 | PRT_LN("Reading nblock = " + String(block_num) + " addr = " + String((block_num * DATA_BLOCK_SIZE)) + " addr in page = " + String(addr)); 151 | chksum = 0; 152 | for(ndata = 0; ndata < DATA_BLOCK_SIZE; ndata++) 153 | { 154 | data_block[ndata] = *((const uint8_t *)(addr + ndata)); 155 | PRT(" " + String(data_block[ndata])); 156 | if(ndata < data_siz) 157 | { 158 | chksum += data_block[ndata]; 159 | } 160 | if(data_block[ndata] == 0xff) 161 | { 162 | count_FFs++; 163 | } 164 | } 165 | PRT_LN(" "); 166 | 167 | if(count_FFs == DATA_BLOCK_SIZE) // (data_block[0] == 0xFF) //found empty block 168 | { 169 | PRT_LN("--- found empty block"); 170 | return 0; 171 | } 172 | else if(chksum != *((const uint8_t *)(addr + data_siz))) //chksum on block next byte 173 | { 174 | PRT_LN("--- found block with wrong chksum"); 175 | return 2; 176 | } 177 | else 178 | { 179 | for(ndata = 0; ndata < data_siz; ndata++) 180 | { 181 | data_bl[ndata] = data_block[ndata]; 182 | } 183 | data_bl[ndata] = chksum; 184 | 185 | #ifdef DFLASH_debug 186 | freq = data_bl[HMI_NMENUS_DFLASH+0]; 187 | freq <<= 8; 188 | freq += data_bl[HMI_NMENUS_DFLASH+1]; 189 | freq <<= 8; 190 | freq += data_bl[HMI_NMENUS_DFLASH+2]; 191 | freq <<= 8; 192 | freq += data_bl[HMI_NMENUS_DFLASH+3]; 193 | PRT_LN("--- found block ok band = " + String(data_bl[HMI_S_BPF]) + " freq = " + String(freq)); 194 | #endif 195 | 196 | return 1; //block ok 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | 204 | //*********************************************************************** 205 | // 206 | // Init routine - Load HMI data from DFLASH 207 | // 208 | // Load default values to band variables 209 | // Read one block 210 | // if empty, use the default values or the ones already read, stop 211 | // if there is valid data, put in the correct band variable 212 | // repeat on next block 213 | // Last block read will be the actual 214 | // 215 | //*********************************************************************** 216 | void Init_HMI_data(uint8_t *actual_bnd) 217 | { 218 | uint8_t data_block[DATA_BLOCK_SIZE]; 219 | uint16_t i, j, data_index; 220 | uint8_t last_band; 221 | uint16_t ret_read; 222 | uint16_t count_block = 0; 223 | 224 | //band_vars[HMI_NUM_OPT_BPF][BAND_VARS_SIZE]; 225 | // read all block saving the last data for each band 226 | for(i=0; i < MAX_NBLOCK; i++) 227 | { 228 | ret_read = Dflash_read_block(i, data_block, BAND_VARS_SIZE_DFLASH); 229 | if(ret_read == 1) //block ok 230 | { 231 | last_block = i; //keep the last_block index to use on next writing to the DFLASH 232 | count_block++; 233 | //Serialx.print("\nRead block from DFLASH = OK "); 234 | // the last band data read is the newest data and it will be in the vars for use in menu to switch bands 235 | last_band = data_block[HMI_S_BPF]; //last band read 236 | for(j = 0; j < HMI_NMENUS_DFLASH; j++) 237 | { 238 | band_vars[last_band][j] = data_block[j]; //put the data on the right band position to use in menus 239 | //Serialx.print(" " + String(band_vars[hmi_band][ndata])); 240 | } 241 | for(; j < HMI_NMENUS; j++) 242 | { 243 | band_vars[last_band][j] = 0; //fill not saved menu with zeros 244 | //Serialx.print(" " + String(band_vars[hmi_band][ndata])); 245 | } 246 | for(j = 0; j < 4; j++) 247 | { 248 | band_vars[last_band][HMI_NMENUS+j] = data_block[HMI_NMENUS_DFLASH+j]; //copy freq saved 249 | //Serialx.print(" " + String(band_vars[hmi_band][ndata])); 250 | } 251 | //Serialx.println("\n"); 252 | } 253 | else if(ret_read == 0) //empty block found = end of data on DFLASH found 254 | { 255 | if(count_block>0) 256 | { 257 | *actual_bnd = last_band; //last band saved will be the actual band 258 | //Serialx.print(" " + String(band_vars[hmi_band][ndata])); 259 | } 260 | else 261 | { 262 | *actual_bnd = 2; //no data in DFLASH, use default band vars 263 | last_block = i-1; // -1 means empty DFLASH 264 | } 265 | //Serialx.println("\nRead menu configuration from DFLASH = NOT OK Using Default Values"); 266 | break; //stop reading fromDFLASH 267 | } 268 | else if(ret_read == 2) //wrong chksum 269 | { 270 | // ignores the block 271 | // does not stop to search for empty blcok 272 | } 273 | } 274 | PRT_LN("INIT ok last_block = " + String(last_block) + " actual_bnd = " + String(*actual_bnd)); 275 | 276 | //calculate the page for last_block+1 277 | uint16_t npage = (last_block+1) / MAX_NBLOCK_IN_PAGE; 278 | 279 | //calculte the block+1 position inside of the page 280 | uint16_t block_in_page = last_block - (npage * MAX_NBLOCK_IN_PAGE); 281 | 282 | PRT_LN(" npage = " + String(npage) + " last block inpage = " + String(block_in_page)); 283 | } 284 | 285 | 286 | //*********************************************************************** 287 | // 288 | // If change the band, use the next band var as actual 289 | // 290 | // 291 | //*********************************************************************** 292 | 293 | 294 | 295 | //*********************************************************************** 296 | // 297 | // 298 | // 299 | // 300 | //*********************************************************************** 301 | void Dflash_erase_sector(void) 302 | { 303 | //erase all sector 304 | PRT_LN("DFLASH sector full -> start erase"); 305 | 306 | DFLASH_in_use = 1; //signal to core1 to stop interrupts while writing to the DFLASH 307 | sleep_us(500); // 1/16kHz = 62.5us 308 | 309 | const bool locked = multicore_lockout_start_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to lockout = pause 310 | 311 | if (locked) 312 | { 313 | PRT_LN("multicore_lock"); 314 | 315 | uint32_t ints = save_and_disable_interrupts(); 316 | flash_range_erase(DFLASH_ADDR_ERASE, FLASH_SECTOR_SIZE); //size Must be a multiple of 4096 bytes (one sector). 317 | restore_interrupts (ints); 318 | bool unlocked; 319 | 320 | //do { 321 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 322 | //} while(!unlocked); 323 | 324 | if(!unlocked) 325 | { 326 | PRT_LN("multicore_lock NOT unlock"); 327 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 328 | if(!unlocked) 329 | { 330 | PRT_LN("multicore_lock NOT unlock"); 331 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 332 | } 333 | } 334 | else 335 | { 336 | PRT_LN("multicore_lock unlock OK"); 337 | } 338 | 339 | } 340 | else 341 | { 342 | PRT_LN("multicore NOT lock"); 343 | } 344 | 345 | PRT_LN("DFLASH sector full -> finish erase"); 346 | 347 | } 348 | 349 | 350 | 351 | 352 | //*********************************************************************** 353 | // 354 | // 355 | // 356 | // 357 | //*********************************************************************** 358 | void Dflash_write_page(uint16_t npage_wr, uint8_t *pg_wr) 359 | { 360 | uint32_t page_addr_write; 361 | 362 | page_addr_write = DFLASH_ADDR_WRITE(npage_wr); 363 | 364 | PRT_LN("Write npage_wr = " + String(npage_wr) + " last_block+1 = " + String(last_block+1) + " page_addr_write = " + String(page_addr_write)); 365 | 366 | 367 | PRT_LN("OverWrite writing start"); 368 | 369 | 370 | DFLASH_in_use = 1; //signal to core1 to stop interrupts while writing to the DFLASH 371 | sleep_us(500); // 1/16kHz = 62.5us 372 | 373 | const bool locked = multicore_lockout_start_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to lockout = pause 374 | 375 | if (locked) 376 | { 377 | PRT_LN("multicore_lock"); 378 | 379 | uint32_t interrupts = save_and_disable_interrupts(); 380 | flash_range_program(page_addr_write, pg_wr, FLASH_PAGE_SIZE); 381 | restore_interrupts(interrupts); 382 | bool unlocked; 383 | 384 | //do { 385 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 386 | //} while(!unlocked); 387 | 388 | if(!unlocked) 389 | { 390 | PRT_LN("multicore_lock NOT unlock"); 391 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 392 | if(!unlocked) 393 | { 394 | PRT_LN("multicore_lock NOT unlock"); 395 | unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT_us); //wait MULTICORE_LOCKOUT_TIMEOUT_us for Core1 to release from lockout 396 | } 397 | } 398 | else 399 | { 400 | PRT_LN("multicore_lock unlock OK"); 401 | } 402 | 403 | } 404 | else 405 | { 406 | PRT_LN("multicore NOT lock"); 407 | } 408 | 409 | DFLASH_in_use = 0; //release core1 after writing to the DFLASH 410 | 411 | PRT_LN("OverWrite writing end"); 412 | } 413 | 414 | 415 | 416 | 417 | 418 | //*********************************************************************** 419 | // 420 | // Writes block to the data flash memory 421 | // 422 | // calculate the page for last_block+1 423 | // 424 | // Check if the last block has the same data, if it is the same, do not save 425 | // 426 | // if the sector is full 427 | // erase all sector 428 | // save all band variables with the actual as last one 429 | // (save all band block at once in the first page) 430 | // last_block = BAND_VARS_SIZE-1 (last position) 431 | // else 432 | // read the page (256 bytes) of the last_block+1 433 | // fill with the block data on last_block+1 position 434 | // write to the DFLASH 435 | // last_block = last_block+1 436 | // 437 | // 438 | //*********************************************************************** 439 | bool Dflash_write_block(uint8_t *data_bl) 440 | { 441 | uint16_t ndata; 442 | uint16_t next_block_pos_in_page; 443 | uint32_t last_block_addr_read; 444 | uint32_t page_addr_read; 445 | uint8_t *ap_bl; 446 | uint8_t pg[FLASH_PAGE_SIZE]; 447 | uint16_t npage; //next block's page 448 | uint16_t i,j,k; 449 | uint16_t block_in_page; 450 | uint16_t data_index; 451 | uint32_t freq; 452 | uint8_t chksum; 453 | 454 | //check if it is the same as the last already saved 455 | last_block_addr_read = DFLASH_ADDR_READ(last_block * DATA_BLOCK_SIZE); 456 | for(data_index = 0; data_index < BAND_VARS_SIZE; data_index++) //check band and freq 457 | { 458 | if(data_bl[data_index] != *((const uint8_t *)(last_block_addr_read + data_index))) //band_vars[last_block][data_index]) 459 | { 460 | break; 461 | } 462 | } 463 | if(data_index < BAND_VARS_SIZE) //actual data is different from last band in DFLASH 464 | { 465 | PRT_LN("Last block different -> writing the new one"); 466 | 467 | if(last_block+1 >= FLASH_SECTOR_SIZE) //DFLASH sector full 468 | { 469 | Dflash_erase_sector(); 470 | 471 | //fill the page with the band_vars[HMI_NUM_OPT_BPF][BAND_VARS_SIZE] 472 | k = 0; 473 | for(i=0; i 21 | 22 | #define MAXFACTORS 32 23 | /* e.g. an fft of length 128 has 4 factors 24 | as far as kissfft is concerned 25 | 4*4*4*2 26 | */ 27 | 28 | struct kiss_fft_state{ 29 | int nfft; 30 | int inverse; 31 | int factors[2*MAXFACTORS]; 32 | kiss_fft_cpx twiddles[1]; 33 | }; 34 | 35 | /* 36 | Explanation of macros dealing with complex math: 37 | 38 | C_MUL(m,a,b) : m = a*b 39 | C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise 40 | C_SUB( res, a,b) : res = a - b 41 | C_SUBFROM( res , a) : res -= a 42 | C_ADDTO( res , a) : res += a 43 | * */ 44 | #ifdef FIXED_POINT 45 | #if (FIXED_POINT==32) 46 | # define FRACBITS 31 47 | # define SAMPPROD int64_t 48 | #define SAMP_MAX 2147483647 49 | #else 50 | # define FRACBITS 15 51 | # define SAMPPROD int32_t 52 | #define SAMP_MAX 32767 53 | #endif 54 | 55 | #define SAMP_MIN -SAMP_MAX 56 | 57 | #if defined(CHECK_OVERFLOW) 58 | # define CHECK_OVERFLOW_OP(a,op,b) \ 59 | if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ 60 | fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } 61 | #endif 62 | 63 | 64 | # define smul(a,b) ( (SAMPPROD)(a)*(b) ) 65 | # define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) 66 | 67 | # define S_MUL(a,b) sround( smul(a,b) ) 68 | 69 | # define C_MUL(m,a,b) \ 70 | do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ 71 | (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) 72 | 73 | # define DIVSCALAR(x,k) \ 74 | (x) = sround( smul( x, SAMP_MAX/k ) ) 75 | 76 | # define C_FIXDIV(c,div) \ 77 | do { DIVSCALAR( (c).r , div); \ 78 | DIVSCALAR( (c).i , div); }while (0) 79 | 80 | # define C_MULBYSCALAR( c, s ) \ 81 | do{ (c).r = sround( smul( (c).r , s ) ) ;\ 82 | (c).i = sround( smul( (c).i , s ) ) ; }while(0) 83 | 84 | #else /* not FIXED_POINT*/ 85 | 86 | # define S_MUL(a,b) ( (a)*(b) ) 87 | #define C_MUL(m,a,b) \ 88 | do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ 89 | (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) 90 | # define C_FIXDIV(c,div) /* NOOP */ 91 | # define C_MULBYSCALAR( c, s ) \ 92 | do{ (c).r *= (s);\ 93 | (c).i *= (s); }while(0) 94 | #endif 95 | 96 | #ifndef CHECK_OVERFLOW_OP 97 | # define CHECK_OVERFLOW_OP(a,op,b) /* noop */ 98 | #endif 99 | 100 | #define C_ADD( res, a,b)\ 101 | do { \ 102 | CHECK_OVERFLOW_OP((a).r,+,(b).r)\ 103 | CHECK_OVERFLOW_OP((a).i,+,(b).i)\ 104 | (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ 105 | }while(0) 106 | #define C_SUB( res, a,b)\ 107 | do { \ 108 | CHECK_OVERFLOW_OP((a).r,-,(b).r)\ 109 | CHECK_OVERFLOW_OP((a).i,-,(b).i)\ 110 | (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ 111 | }while(0) 112 | #define C_ADDTO( res , a)\ 113 | do { \ 114 | CHECK_OVERFLOW_OP((res).r,+,(a).r)\ 115 | CHECK_OVERFLOW_OP((res).i,+,(a).i)\ 116 | (res).r += (a).r; (res).i += (a).i;\ 117 | }while(0) 118 | 119 | #define C_SUBFROM( res , a)\ 120 | do {\ 121 | CHECK_OVERFLOW_OP((res).r,-,(a).r)\ 122 | CHECK_OVERFLOW_OP((res).i,-,(a).i)\ 123 | (res).r -= (a).r; (res).i -= (a).i; \ 124 | }while(0) 125 | 126 | 127 | #ifdef FIXED_POINT 128 | # define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) 129 | # define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) 130 | # define HALF_OF(x) ((x)>>1) 131 | #elif defined(USE_SIMD) 132 | # define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) 133 | # define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) 134 | # define HALF_OF(x) ((x)*_mm_set1_ps(.5)) 135 | #else 136 | # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) 137 | # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) 138 | # define HALF_OF(x) ((x)*.5) 139 | #endif 140 | 141 | #define kf_cexp(x,phase) \ 142 | do{ \ 143 | (x)->r = KISS_FFT_COS(phase);\ 144 | (x)->i = KISS_FFT_SIN(phase);\ 145 | }while(0) 146 | 147 | 148 | /* a debugging function */ 149 | #define pcpx(c)\ 150 | fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) 151 | 152 | 153 | #ifdef KISS_FFT_USE_ALLOCA 154 | // define this to allow use of alloca instead of malloc for temporary buffers 155 | // Temporary buffers are used in two case: 156 | // 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 157 | // 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. 158 | #include 159 | #define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) 160 | #define KISS_FFT_TMP_FREE(ptr) 161 | #else 162 | #define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) 163 | #define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) 164 | #endif 165 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/display_tft.h: -------------------------------------------------------------------------------- 1 | #ifndef __DISPLAY_TFT_H__ 2 | #define __DISPLAY_TFT_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | 9 | 10 | #define ROTATION_SETUP 2 // 0, 1, 2 or 3 11 | 12 | 13 | #if ROTATION_SETUP == 0 || ROTATION_SETUP == 2 14 | //ILI9341 15 | #define display_WIDTH TFT_WIDTH //TFT_HEIGHT=240 16 | #define display_HEIGHT TFT_HEIGHT //TFT_WIDTH=320 17 | #endif 18 | 19 | #if ROTATION_SETUP == 1 || ROTATION_SETUP == 3 20 | //ILI9341 21 | #define display_WIDTH TFT_HEIGHT //TFT_WIDTH=320 22 | #define display_HEIGHT TFT_WIDTH //TFT_HEIGHT=240 23 | #endif 24 | 25 | /* 26 | #define FONT &FreeMonoBold9pt7b 27 | #define X_CHAR 11 28 | #define Y_CHAR 16 29 | */ 30 | #define FONT3 &FreeMonoBold24pt7b 31 | //#define FONT &FreeMono24pt7b 32 | //#define FONT &FreeSans24pt7b 33 | //#define FONT &FreeSerif24pt7b 34 | #define X_CHAR3 28 35 | #define Y_CHAR3 42 36 | #define SIZE3 1 37 | 38 | #define FONT2 &FreeMonoBold18pt7b 39 | //#define FONT2 &FreeMono18pt7b 40 | #define X_CHAR2 21 41 | #define Y_CHAR2 32 42 | #define SIZE2 1 43 | 44 | #define FONT1 &FreeMonoBold12pt7b 45 | #define X_CHAR1 14 46 | #define Y_CHAR1 22 47 | #define SIZE1 1 48 | 49 | 50 | 51 | 52 | 53 | // Default color definitions 54 | #define TFT_BLACK 0x0000 /* 0, 0, 0 */ 55 | #define TFT_NAVY 0x000F /* 0, 0, 128 */ 56 | #define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ 57 | #define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ 58 | #define TFT_MAROON 0x7800 /* 128, 0, 0 */ 59 | #define TFT_PURPLE 0x780F /* 128, 0, 128 */ 60 | #define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ 61 | #define TFT_LIGHTGREY 0xD69A /* 211, 211, 211 */ 62 | #define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ 63 | #define TFT_BLUE 0x001F /* 0, 0, 255 */ 64 | #define TFT_GREEN 0x07E0 /* 0, 255, 0 */ 65 | #define TFT_CYAN 0x07FF /* 0, 255, 255 */ 66 | #define TFT_RED 0xF800 /* 255, 0, 0 */ 67 | #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ 68 | #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ 69 | #define TFT_WHITE 0xFFFF /* 255, 255, 255 */ 70 | #define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ 71 | #define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ 72 | #define TFT_PINK 0xFE19 /* 255, 192, 203 */ 73 | #define TFT_BROWN 0x9A60 /* 150, 75, 0 */ 74 | #define TFT_GOLD 0xFEA0 /* 255, 215, 0 */ 75 | #define TFT_SILVER 0xC618 /* 192, 192, 192 */ 76 | #define TFT_SKYBLUE 0x867D /* 135, 206, 235 */ 77 | #define TFT_VIOLET 0x915C /* 180, 46, 226 */ 78 | 79 | /* 80 | uint16_t red = tft.color565(255, 0, 0); 81 | uint16_t green = tft.color565(0, 255, 0); 82 | uint16_t blue = tft.color565(0, 0, 255); 83 | uint16_t yellow = tft.color565(255, 255, 0); 84 | */ 85 | 86 | // scope graph 87 | //#define AUD_GRAPH_NUM_COLS 100 88 | #define AUD_GRAPH_MIN -25 89 | #define AUD_GRAPH_MAX 25 90 | #define X_MIN_AUD_GRAPH 215 91 | #define Y_MIN_AUD_GRAPH 30 92 | #define Y_MAX_AUD_GRAPH (Y_MIN_AUD_GRAPH + AUD_GRAPH_MAX - AUD_GRAPH_MIN) 93 | 94 | 95 | 96 | // waterfall = FFT graph 97 | #define GRAPH_NUM_LINES (48u) 98 | #define GRAPH_NUM_COLS (FFT_NSAMP) 99 | #define Y_MIN_DRAW (display_HEIGHT - GRAPH_NUM_LINES) 100 | 101 | #define WATERFALL_IN_BLOCK 1 // all lines in the waterfall move with the freq change (not only the new line) 102 | 103 | 104 | extern uint8_t vet_graf_fft[GRAPH_NUM_LINES][GRAPH_NUM_COLS]; // [NL][NCOL] 105 | //extern uint16_t vet_graf_fft_pos; 106 | 107 | // Use hardware SPI 108 | extern TFT_eSPI tft; 109 | 110 | 111 | 112 | //void tft_setup(void); 113 | //void tft_writexy(uint8_t x, uint8_t y, uint8_t *s); 114 | void tft_writexy_(uint16_t font, uint16_t color, uint16_t color_back, uint16_t x, uint16_t y, uint8_t *s); 115 | void tft_writexy_plus(uint16_t font, uint16_t color, uint16_t color_back, uint16_t x, uint16_t x_plus, uint16_t y, uint16_t y_plus, uint8_t *s); 116 | void tft_cursor(uint16_t font, uint16_t color, uint8_t x, uint8_t y); 117 | void tft_cursor_plus(uint16_t font, uint16_t color, uint8_t x, uint8_t x_plus, uint8_t y, uint8_t y_plus); 118 | uint16_t tft_color565(uint16_t r, uint16_t g, uint16_t b); 119 | 120 | // Smeter barr graph definitions 121 | #define MAX_Smeter_table 11 // S1, S2.. S9, S9+ S9++ = 11 steps 122 | void Smeter_bargraph(int16_t index_new); 123 | void TxPower_bargraph(int16_t index_new); 124 | 125 | void display_fft_graf(uint16_t freq); 126 | void display_fft_graf_top(void); 127 | void display_tft_setup0(void); 128 | void display_tft_setup(void); 129 | void display_tft_countdown(bool show, uint16_t val); 130 | void display_tft_loop(void); 131 | 132 | void display_aud_graf(void); 133 | 134 | 135 | 136 | #ifdef __cplusplus 137 | } 138 | #endif 139 | #endif 140 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/dsp.h: -------------------------------------------------------------------------------- 1 | #ifndef __DSP_H__ 2 | #define __DSP_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* 9 | * dsp.h 10 | * 11 | * Created: Mar 2021 12 | * Author: Arjan te Marvelde 13 | * 14 | * See dsp.c for more information 15 | */ 16 | 17 | 18 | 19 | #define FSAMP 480000UL // freq AD sample / 3 channels = 160kHz 20 | #define FSAMP_AUDIO 16000U // audio freq sample 32kHz=critical time 21 | #define ADC_CLOCK_DIV ((uint16_t)(48000000UL/FSAMP)) //48Mhz / 480Khz = 100 22 | #define FRES 500u //Hz resolucao de frequencias desejado para cada bin 23 | #define FFT_NSAMP ((((uint16_t)((FSAMP / 3u) / FRES))+1u) & (~(uint16_t)1u)) // must be even 160k / 500 = 320 24 | //FFT max freq = (FSAMP/3) / 2 25 | #define FFT_NUMFREQ (FFT_NSAMP/2) 26 | 27 | 28 | 29 | /* 30 | * DAC_RANGE defines PWM cycle, determining DAC resolution and PWM frequency. 31 | * DAC resolution = Vcc / DAC_RANGE 32 | * PWM frequency = Fsys / DAC_RANGE 33 | * A value of 250 means 125MHz/250=500kHz 34 | * ADC is 12 bit, so resolution is by definition 4096 35 | * To eliminate undefined behavior we clip off the upper 4 sample bits. 36 | */ 37 | #define DAC_RANGE 255u 38 | #define DAC_BIAS (DAC_RANGE/2u) 39 | #define ADC_RANGE 4095u 40 | #define ADC_BIAS (ADC_RANGE/2u) 41 | 42 | 43 | #ifdef PY2KLA_setup 44 | #define EXCHANGE_I_Q 1 //include or remove this #define in case the LSB/USB and the lower/upper frequency of waterfall display are reverted - hardware I and Q pin dependent 45 | #endif 46 | 47 | 48 | #define PHASE_AMPLITUDE 11 49 | #define I_Q_QSE 22 50 | // 51 | //Here you can choose one of the two methods for uSDR_Pico transmission 52 | // 53 | #define TX_METHOD I_Q_QSE // uSDR_Pico original project generating I and Q signal to a QSE mixer 54 | //#define TX_METHOD PHASE_AMPLITUDE // DO NOT USE - is not ready - used for Class E RF amplifier - see description at: uSDX_TX_PhaseAmpl.cpp 55 | 56 | #define IQ_TX_ATTENUATION 2 //practical value after testing min value without saturate the IQ output for TX 57 | 58 | 59 | #define LOW_PASS_16KHZ_AVERAGE_SUM 11 60 | #define LOW_PASS_16KHZ_FIR 55 61 | // choose with type of filter used for I and Q after 160kHz sampling to deliever to the 16kHz audio process 62 | //#define LOW_PASS_16KHZ LOW_PASS_16KHZ_AVERAGE_SUM 63 | #define LOW_PASS_16KHZ LOW_PASS_16KHZ_FIR 64 | 65 | 66 | 67 | 68 | //extern volatile uint16_t adc_audio_count; 69 | //extern volatile uint16_t adc_waterfall_count; 70 | extern volatile int16_t adc_result[3]; // 71 | 72 | #define MAX_SMETER_DISPLAY_TIME (150 *16) // X ms / (1/16kHz) = number of 16kHz ints to get X ms 73 | extern volatile int16_t max_a_sample; 74 | extern volatile int16_t display_a_sample; 75 | extern volatile int16_t smeter_display_time; 76 | 77 | void dsp_setagc(int agc); 78 | void dsp_setmode(int mode); 79 | void dsp_setvox(int vox); 80 | int dsp_getmode(void); 81 | 82 | //extern volatile uint16_t adc_audio_ready; 83 | extern volatile uint16_t tim_count; 84 | //extern volatile uint16_t fft_samples_ready; 85 | //extern volatile uint16_t fft_samp_pos; //number of samples saved for FFT 86 | extern volatile uint16_t fft_samples_ready; 87 | extern volatile uint16_t fft_display_graf_new; 88 | 89 | #define AUD_GRAPH_NUM_COLS 100 90 | 91 | #define AUD_NUM_VAR (6u) // number of variables on buffer for low freq = audio graphic 92 | #define AUD_NUM_SAMP (AUD_GRAPH_NUM_COLS*3u) // number of variables on buffer for low freq = audio graphic 93 | #define AUD_SAMP_I 0u 94 | #define AUD_SAMP_Q 1u 95 | #define AUD_SAMP_MIC 2u 96 | #define AUD_SAMP_A 3u 97 | #define AUD_SAMP_PEAK 4u 98 | #define AUD_SAMP_GAIN 5u 99 | #define AUD_STATE_SAMP_IN 0 100 | #define AUD_STATE_SAMP_RDY 10 101 | //#define AUD_STATE_SAMP_GRAPH 20 102 | //#define AUD_STATE_SAMP_DONE 30 103 | extern volatile int16_t aud_samp[AUD_NUM_VAR][AUD_NUM_SAMP]; //samples buffer for FFT and waterfall only 0-1 used for I and Q (3=MIC) [NL][NCOL] 104 | extern volatile uint16_t aud_samples_state; 105 | 106 | #define PEAK_AVG_SHIFT 2 //affects agc speed 107 | extern volatile int32_t peak_avg_shifted; // signal level detector after AGC = average of positive values 108 | 109 | #define AGC_GAIN_SHIFT 6 //shift corresponding to AGC_GAIN_MAX 110 | #define AGC_GAIN_MAX (1u<(y)?(x):(y)) // Get max value 92 | #endif 93 | 94 | 95 | 96 | #define band0_hmi_freq_default 1820000L 97 | #define band1_hmi_freq_default 3700000L 98 | #define band2_hmi_freq_default 7050000L 99 | #define band3_hmi_freq_default 14200000L 100 | #define band4_hmi_freq_default 28400000L 101 | 102 | #define b0_0 (uint8_t)((band0_hmi_freq_default >> 24)&0xff) 103 | #define b0_1 (uint8_t)((band0_hmi_freq_default >> 16)&0xff) 104 | #define b0_2 (uint8_t)((band0_hmi_freq_default >> 8)&0xff) 105 | #define b0_3 (uint8_t)(band0_hmi_freq_default&0xff) 106 | 107 | #define b1_0 (uint8_t)((band1_hmi_freq_default >> 24)&0xff) 108 | #define b1_1 (uint8_t)((band1_hmi_freq_default >> 16)&0xff) 109 | #define b1_2 (uint8_t)((band1_hmi_freq_default >> 8)&0xff) 110 | #define b1_3 (uint8_t)(band1_hmi_freq_default&0xff) 111 | 112 | #define b2_0 (uint8_t)((band2_hmi_freq_default >> 24)&0xff) 113 | #define b2_1 (uint8_t)((band2_hmi_freq_default >> 16)&0xff) 114 | #define b2_2 (uint8_t)((band2_hmi_freq_default >> 8)&0xff) 115 | #define b2_3 (uint8_t)(band2_hmi_freq_default&0xff) 116 | 117 | #define b3_0 (uint8_t)((band3_hmi_freq_default >> 24)&0xff) 118 | #define b3_1 (uint8_t)((band3_hmi_freq_default >> 16)&0xff) 119 | #define b3_2 (uint8_t)((band3_hmi_freq_default >> 8)&0xff) 120 | #define b3_3 (uint8_t)(band3_hmi_freq_default&0xff) 121 | 122 | #define b4_0 (uint8_t)((band4_hmi_freq_default >> 24)&0xff) 123 | #define b4_1 (uint8_t)((band4_hmi_freq_default >> 16)&0xff) 124 | #define b4_2 (uint8_t)((band4_hmi_freq_default >> 8)&0xff) 125 | #define b4_3 (uint8_t)(band4_hmi_freq_default&0xff) 126 | 127 | 128 | #define GP_PTT 15 129 | 130 | //extern uint8_t hmi_sub[HMI_NMENUS]; // Stored option selection per state 131 | extern uint32_t hmi_freq; 132 | extern uint8_t hmi_band; 133 | extern bool tx_enabled; 134 | extern bool tx_enable_changed; 135 | extern bool ptt_internal_active; //PTT output = true for vox, mon and mem 136 | extern bool ptt_external_active; 137 | extern bool ptt_vox_active; 138 | extern bool ptt_mon_active; 139 | extern bool ptt_aud_active; 140 | 141 | 142 | 143 | #define AUDIO_BUF_MAX 160000 //160k bytes(memory used) * (1 / 16khz(sample freq)) = 10s 144 | extern uint8_t audio_buf[AUDIO_BUF_MAX]; 145 | extern uint32_t audio_rec_pos; 146 | extern uint32_t audio_play_pos; 147 | 148 | #define AUDIO_STOPPED 0 149 | #define AUDIO_START 1 150 | #define AUDIO_RUNNING 2 151 | 152 | extern uint16_t Aud_Rec_Tx; 153 | extern uint16_t Aud_Rec_Rx; 154 | extern uint16_t Aud_Play_Tx; 155 | extern uint16_t Aud_Play_Spk; 156 | 157 | 158 | void Setup_Band(uint8_t band); 159 | void hmi_init0(void); 160 | void hmi_init(void); 161 | void hmi_evaluate(void); 162 | 163 | 164 | //#define TST_MAX_SMETER_SWR 1 165 | 166 | 167 | 168 | #ifdef __cplusplus 169 | } 170 | #endif 171 | #endif 172 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/kiss_fft.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2010, Mark Borgerding 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 16 | #include "_kiss_fft_guts.h" 17 | /* The guts header contains all the multiplication and addition macros that are defined for 18 | fixed or floating point complex numbers. It also delares the kf_ internal functions. 19 | */ 20 | 21 | static void kf_bfly2( 22 | kiss_fft_cpx * Fout, 23 | const size_t fstride, 24 | const kiss_fft_cfg st, 25 | int m 26 | ) 27 | { 28 | kiss_fft_cpx * Fout2; 29 | kiss_fft_cpx * tw1 = st->twiddles; 30 | kiss_fft_cpx t; 31 | Fout2 = Fout + m; 32 | do{ 33 | C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); 34 | 35 | C_MUL (t, *Fout2 , *tw1); 36 | tw1 += fstride; 37 | C_SUB( *Fout2 , *Fout , t ); 38 | C_ADDTO( *Fout , t ); 39 | ++Fout2; 40 | ++Fout; 41 | }while (--m); 42 | } 43 | 44 | static void kf_bfly4( 45 | kiss_fft_cpx * Fout, 46 | const size_t fstride, 47 | const kiss_fft_cfg st, 48 | const size_t m 49 | ) 50 | { 51 | kiss_fft_cpx *tw1,*tw2,*tw3; 52 | kiss_fft_cpx scratch[6]; 53 | size_t k=m; 54 | const size_t m2=2*m; 55 | const size_t m3=3*m; 56 | 57 | 58 | tw3 = tw2 = tw1 = st->twiddles; 59 | 60 | do { 61 | C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); 62 | 63 | C_MUL(scratch[0],Fout[m] , *tw1 ); 64 | C_MUL(scratch[1],Fout[m2] , *tw2 ); 65 | C_MUL(scratch[2],Fout[m3] , *tw3 ); 66 | 67 | C_SUB( scratch[5] , *Fout, scratch[1] ); 68 | C_ADDTO(*Fout, scratch[1]); 69 | C_ADD( scratch[3] , scratch[0] , scratch[2] ); 70 | C_SUB( scratch[4] , scratch[0] , scratch[2] ); 71 | C_SUB( Fout[m2], *Fout, scratch[3] ); 72 | tw1 += fstride; 73 | tw2 += fstride*2; 74 | tw3 += fstride*3; 75 | C_ADDTO( *Fout , scratch[3] ); 76 | 77 | if(st->inverse) { 78 | Fout[m].r = scratch[5].r - scratch[4].i; 79 | Fout[m].i = scratch[5].i + scratch[4].r; 80 | Fout[m3].r = scratch[5].r + scratch[4].i; 81 | Fout[m3].i = scratch[5].i - scratch[4].r; 82 | }else{ 83 | Fout[m].r = scratch[5].r + scratch[4].i; 84 | Fout[m].i = scratch[5].i - scratch[4].r; 85 | Fout[m3].r = scratch[5].r - scratch[4].i; 86 | Fout[m3].i = scratch[5].i + scratch[4].r; 87 | } 88 | ++Fout; 89 | }while(--k); 90 | } 91 | 92 | static void kf_bfly3( 93 | kiss_fft_cpx * Fout, 94 | const size_t fstride, 95 | const kiss_fft_cfg st, 96 | size_t m 97 | ) 98 | { 99 | size_t k=m; 100 | const size_t m2 = 2*m; 101 | kiss_fft_cpx *tw1,*tw2; 102 | kiss_fft_cpx scratch[5]; 103 | kiss_fft_cpx epi3; 104 | epi3 = st->twiddles[fstride*m]; 105 | 106 | tw1=tw2=st->twiddles; 107 | 108 | do{ 109 | C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); 110 | 111 | C_MUL(scratch[1],Fout[m] , *tw1); 112 | C_MUL(scratch[2],Fout[m2] , *tw2); 113 | 114 | C_ADD(scratch[3],scratch[1],scratch[2]); 115 | C_SUB(scratch[0],scratch[1],scratch[2]); 116 | tw1 += fstride; 117 | tw2 += fstride*2; 118 | 119 | Fout[m].r = Fout->r - HALF_OF(scratch[3].r); 120 | Fout[m].i = Fout->i - HALF_OF(scratch[3].i); 121 | 122 | C_MULBYSCALAR( scratch[0] , epi3.i ); 123 | 124 | C_ADDTO(*Fout,scratch[3]); 125 | 126 | Fout[m2].r = Fout[m].r + scratch[0].i; 127 | Fout[m2].i = Fout[m].i - scratch[0].r; 128 | 129 | Fout[m].r -= scratch[0].i; 130 | Fout[m].i += scratch[0].r; 131 | 132 | ++Fout; 133 | }while(--k); 134 | } 135 | 136 | static void kf_bfly5( 137 | kiss_fft_cpx * Fout, 138 | const size_t fstride, 139 | const kiss_fft_cfg st, 140 | int m 141 | ) 142 | { 143 | kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; 144 | int u; 145 | kiss_fft_cpx scratch[13]; 146 | kiss_fft_cpx * twiddles = st->twiddles; 147 | kiss_fft_cpx *tw; 148 | kiss_fft_cpx ya,yb; 149 | ya = twiddles[fstride*m]; 150 | yb = twiddles[fstride*2*m]; 151 | 152 | Fout0=Fout; 153 | Fout1=Fout0+m; 154 | Fout2=Fout0+2*m; 155 | Fout3=Fout0+3*m; 156 | Fout4=Fout0+4*m; 157 | 158 | tw=st->twiddles; 159 | for ( u=0; ur += scratch[7].r + scratch[8].r; 174 | Fout0->i += scratch[7].i + scratch[8].i; 175 | 176 | scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); 177 | scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); 178 | 179 | scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); 180 | scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); 181 | 182 | C_SUB(*Fout1,scratch[5],scratch[6]); 183 | C_ADD(*Fout4,scratch[5],scratch[6]); 184 | 185 | scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); 186 | scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); 187 | scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); 188 | scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); 189 | 190 | C_ADD(*Fout2,scratch[11],scratch[12]); 191 | C_SUB(*Fout3,scratch[11],scratch[12]); 192 | 193 | ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; 194 | } 195 | } 196 | 197 | /* perform the butterfly for one stage of a mixed radix FFT */ 198 | static void kf_bfly_generic( 199 | kiss_fft_cpx * Fout, 200 | const size_t fstride, 201 | const kiss_fft_cfg st, 202 | int m, 203 | int p 204 | ) 205 | { 206 | int u,k,q1,q; 207 | kiss_fft_cpx * twiddles = st->twiddles; 208 | kiss_fft_cpx t; 209 | int Norig = st->nfft; 210 | 211 | kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); 212 | 213 | for ( u=0; u=Norig) twidx-=Norig; 228 | C_MUL(t,scratch[q] , twiddles[twidx] ); 229 | C_ADDTO( Fout[ k ] ,t); 230 | } 231 | k += m; 232 | } 233 | } 234 | KISS_FFT_TMP_FREE(scratch); 235 | } 236 | 237 | static 238 | void kf_work( 239 | kiss_fft_cpx * Fout, 240 | const kiss_fft_cpx * f, 241 | const size_t fstride, 242 | int in_stride, 243 | int * factors, 244 | const kiss_fft_cfg st 245 | ) 246 | { 247 | kiss_fft_cpx * Fout_beg=Fout; 248 | const int p=*factors++; /* the radix */ 249 | const int m=*factors++; /* stage's fft length/p */ 250 | const kiss_fft_cpx * Fout_end = Fout + p*m; 251 | 252 | #ifdef _OPENMP 253 | // use openmp extensions at the 254 | // top-level (not recursive) 255 | if (fstride==1 && p<=5) 256 | { 257 | int k; 258 | 259 | // execute the p different work units in different threads 260 | # pragma omp parallel for 261 | for (k=0;k floor_sqrt) 324 | p = n; /* no more factors, skip to end */ 325 | } 326 | n /= p; 327 | *facbuf++ = p; 328 | *facbuf++ = n; 329 | } while (n > 1); 330 | } 331 | 332 | /* 333 | * 334 | * User-callable function to allocate all necessary storage space for the fft. 335 | * 336 | * The return value is a contiguous block of memory, allocated with malloc. As such, 337 | * It can be freed with free(), rather than a kiss_fft-specific function. 338 | * */ 339 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) 340 | { 341 | kiss_fft_cfg st=NULL; 342 | size_t memneeded = sizeof(struct kiss_fft_state) 343 | + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ 344 | 345 | if ( lenmem==NULL ) { 346 | st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); 347 | }else{ 348 | if (mem != NULL && *lenmem >= memneeded) 349 | st = (kiss_fft_cfg)mem; 350 | *lenmem = memneeded; 351 | } 352 | if (st) { 353 | int i; 354 | st->nfft=nfft; 355 | st->inverse = inverse_fft; 356 | 357 | for (i=0;iinverse) 361 | phase *= -1; 362 | kf_cexp(st->twiddles+i, phase ); 363 | } 364 | 365 | kf_factor(nfft,st->factors); 366 | } 367 | return st; 368 | } 369 | 370 | 371 | void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) 372 | { 373 | if (fin == fout) { 374 | //NOTE: this is not really an in-place FFT algorithm. 375 | //It just performs an out-of-place FFT into a temp buffer 376 | kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); 377 | kf_work(tmpbuf,fin,1,in_stride, st->factors,st); 378 | memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); 379 | KISS_FFT_TMP_FREE(tmpbuf); 380 | }else{ 381 | kf_work( fout, fin, 1,in_stride, st->factors,st ); 382 | } 383 | } 384 | 385 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) 386 | { 387 | kiss_fft_stride(cfg,fin,fout,1); 388 | } 389 | 390 | 391 | void kiss_fft_cleanup(void) 392 | { 393 | // nothing needed any more 394 | } 395 | 396 | int kiss_fft_next_fast_size(int n) 397 | { 398 | while(1) { 399 | int m=n; 400 | while ( (m%2) == 0 ) m/=2; 401 | while ( (m%3) == 0 ) m/=3; 402 | while ( (m%5) == 0 ) m/=5; 403 | if (m<=1) 404 | break; /* n is completely factorable by twos, threes, and fives */ 405 | n++; 406 | } 407 | return n; 408 | } 409 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/kiss_fft.h: -------------------------------------------------------------------------------- 1 | #ifndef KISS_FFT_H 2 | #define KISS_FFT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /* 14 | ATTENTION! 15 | If you would like a : 16 | -- a utility that will handle the caching of fft objects 17 | -- real-only (no imaginary time component ) FFT 18 | -- a multi-dimensional FFT 19 | -- a command-line utility to perform ffts 20 | -- a command-line utility to perform fast-convolution filtering 21 | 22 | Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c 23 | in the tools/ directory. 24 | */ 25 | 26 | #define FIXED_POINT 16 //to use kiss_fft_scalar = int16 27 | 28 | 29 | 30 | #ifdef USE_SIMD 31 | # include 32 | # define kiss_fft_scalar __m128 33 | #define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) 34 | #define KISS_FFT_FREE _mm_free 35 | #else 36 | #define KISS_FFT_MALLOC malloc 37 | #define KISS_FFT_FREE free 38 | #endif 39 | 40 | 41 | #ifdef FIXED_POINT 42 | #include 43 | # if (FIXED_POINT == 32) 44 | # define kiss_fft_scalar int32_t 45 | # else 46 | # define kiss_fft_scalar int16_t 47 | # endif 48 | #else 49 | # ifndef kiss_fft_scalar 50 | /* default is float */ 51 | # define kiss_fft_scalar float 52 | # endif 53 | #endif 54 | 55 | typedef struct { 56 | kiss_fft_scalar r; 57 | kiss_fft_scalar i; 58 | }kiss_fft_cpx; 59 | 60 | typedef struct kiss_fft_state* kiss_fft_cfg; 61 | 62 | /* 63 | * kiss_fft_alloc 64 | * 65 | * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. 66 | * 67 | * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); 68 | * 69 | * The return value from fft_alloc is a cfg buffer used internally 70 | * by the fft routine or NULL. 71 | * 72 | * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. 73 | * The returned value should be free()d when done to avoid memory leaks. 74 | * 75 | * The state can be placed in a user supplied buffer 'mem': 76 | * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, 77 | * then the function places the cfg in mem and the size used in *lenmem 78 | * and returns mem. 79 | * 80 | * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), 81 | * then the function returns NULL and places the minimum cfg 82 | * buffer size in *lenmem. 83 | * */ 84 | 85 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); 86 | 87 | /* 88 | * kiss_fft(cfg,in_out_buf) 89 | * 90 | * Perform an FFT on a complex input buffer. 91 | * for a forward FFT, 92 | * fin should be f[0] , f[1] , ... ,f[nfft-1] 93 | * fout will be F[0] , F[1] , ... ,F[nfft-1] 94 | * Note that each element is complex and can be accessed like 95 | f[k].r and f[k].i 96 | * */ 97 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); 98 | 99 | /* 100 | A more generic version of the above function. It reads its input from every Nth sample. 101 | * */ 102 | void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); 103 | 104 | /* If kiss_fft_alloc allocated a buffer, it is one contiguous 105 | buffer and can be simply free()d when no longer needed*/ 106 | #define kiss_fft_free free 107 | 108 | /* 109 | Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up 110 | your compiler output to call this before you exit. 111 | */ 112 | void kiss_fft_cleanup(void); 113 | 114 | 115 | /* 116 | * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) 117 | */ 118 | int kiss_fft_next_fast_size(int n); 119 | 120 | /* for real ffts, we need an even size */ 121 | #define kiss_fftr_next_fast_size_real(n) \ 122 | (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) 123 | 124 | #ifdef __cplusplus 125 | } 126 | #endif 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/kiss_fftr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2004, Mark Borgerding 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | #include "kiss_fftr.h" 16 | #include "_kiss_fft_guts.h" 17 | 18 | struct kiss_fftr_state{ 19 | kiss_fft_cfg substate; 20 | kiss_fft_cpx * tmpbuf; 21 | kiss_fft_cpx * super_twiddles; 22 | #ifdef USE_SIMD 23 | void * pad; 24 | #endif 25 | }; 26 | 27 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) 28 | { 29 | int i; 30 | kiss_fftr_cfg st = NULL; 31 | size_t subsize, memneeded; 32 | 33 | if (nfft & 1) { 34 | fprintf(stderr,"Real FFT optimization must be even.\n"); 35 | return NULL; 36 | } 37 | nfft >>= 1; 38 | 39 | kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); 40 | memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); 41 | 42 | if (lenmem == NULL) { 43 | st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); 44 | } else { 45 | if (*lenmem >= memneeded) 46 | st = (kiss_fftr_cfg) mem; 47 | *lenmem = memneeded; 48 | } 49 | if (!st) 50 | return NULL; 51 | 52 | st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ 53 | st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); 54 | st->super_twiddles = st->tmpbuf + nfft; 55 | kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); 56 | 57 | for (i = 0; i < nfft/2; ++i) { 58 | double phase = 59 | -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); 60 | if (inverse_fft) 61 | phase *= -1; 62 | kf_cexp (st->super_twiddles+i,phase); 63 | } 64 | return st; 65 | } 66 | 67 | void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) 68 | { 69 | /* input buffer timedata is stored row-wise */ 70 | int k,ncfft; 71 | kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; 72 | 73 | if ( st->substate->inverse) { 74 | fprintf(stderr,"kiss fft usage error: improper alloc\n"); 75 | exit(1); 76 | } 77 | 78 | ncfft = st->substate->nfft; 79 | 80 | /*perform the parallel fft of two real signals packed in real,imag*/ 81 | kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); 82 | /* The real part of the DC element of the frequency spectrum in st->tmpbuf 83 | * contains the sum of the even-numbered elements of the input time sequence 84 | * The imag part is the sum of the odd-numbered elements 85 | * 86 | * The sum of tdc.r and tdc.i is the sum of the input time sequence. 87 | * yielding DC of input time sequence 88 | * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... 89 | * yielding Nyquist bin of input time sequence 90 | */ 91 | 92 | tdc.r = st->tmpbuf[0].r; 93 | tdc.i = st->tmpbuf[0].i; 94 | C_FIXDIV(tdc,2); 95 | CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); 96 | CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); 97 | freqdata[0].r = tdc.r + tdc.i; 98 | freqdata[ncfft].r = tdc.r - tdc.i; 99 | #ifdef USE_SIMD 100 | freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); 101 | #else 102 | freqdata[ncfft].i = freqdata[0].i = 0; 103 | #endif 104 | 105 | for ( k=1;k <= ncfft/2 ; ++k ) { 106 | fpk = st->tmpbuf[k]; 107 | fpnk.r = st->tmpbuf[ncfft-k].r; 108 | fpnk.i = - st->tmpbuf[ncfft-k].i; 109 | C_FIXDIV(fpk,2); 110 | C_FIXDIV(fpnk,2); 111 | 112 | C_ADD( f1k, fpk , fpnk ); 113 | C_SUB( f2k, fpk , fpnk ); 114 | C_MUL( tw , f2k , st->super_twiddles[k-1]); 115 | 116 | freqdata[k].r = HALF_OF(f1k.r + tw.r); 117 | freqdata[k].i = HALF_OF(f1k.i + tw.i); 118 | freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); 119 | freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); 120 | } 121 | } 122 | 123 | void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) 124 | { 125 | /* input buffer timedata is stored row-wise */ 126 | int k, ncfft; 127 | 128 | if (st->substate->inverse == 0) { 129 | fprintf (stderr, "kiss fft usage error: improper alloc\n"); 130 | exit (1); 131 | } 132 | 133 | ncfft = st->substate->nfft; 134 | 135 | st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; 136 | st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; 137 | C_FIXDIV(st->tmpbuf[0],2); 138 | 139 | for (k = 1; k <= ncfft / 2; ++k) { 140 | kiss_fft_cpx fk, fnkc, fek, fok, tmp; 141 | fk = freqdata[k]; 142 | fnkc.r = freqdata[ncfft - k].r; 143 | fnkc.i = -freqdata[ncfft - k].i; 144 | C_FIXDIV( fk , 2 ); 145 | C_FIXDIV( fnkc , 2 ); 146 | 147 | C_ADD (fek, fk, fnkc); 148 | C_SUB (tmp, fk, fnkc); 149 | C_MUL (fok, tmp, st->super_twiddles[k-1]); 150 | C_ADD (st->tmpbuf[k], fek, fok); 151 | C_SUB (st->tmpbuf[ncfft - k], fek, fok); 152 | #ifdef USE_SIMD 153 | st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); 154 | #else 155 | st->tmpbuf[ncfft - k].i *= -1; 156 | #endif 157 | } 158 | kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); 159 | } 160 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/kiss_fftr.h: -------------------------------------------------------------------------------- 1 | #ifndef KISS_FTR_H 2 | #define KISS_FTR_H 3 | 4 | #include "kiss_fft.h" 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | 10 | /* 11 | 12 | Real optimized version can save about 45% cpu time vs. complex fft of a real seq. 13 | 14 | 15 | 16 | */ 17 | 18 | typedef struct kiss_fftr_state *kiss_fftr_cfg; 19 | 20 | 21 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); 22 | /* 23 | nfft must be even 24 | 25 | If you don't care to allocate space, use mem = lenmem = NULL 26 | */ 27 | 28 | 29 | void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); 30 | /* 31 | input timedata has nfft scalar points 32 | output freqdata has nfft/2+1 complex points 33 | */ 34 | 35 | void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); 36 | /* 37 | input freqdata has nfft/2+1 complex points 38 | output timedata has nfft scalar points 39 | */ 40 | 41 | #define kiss_fftr_free free 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | #endif 47 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/monitor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * monitor.c 3 | * 4 | * Created: Mar 2021 5 | * Author: Arjan te Marvelde 6 | * May2022: adapted by Klaus Fensterseifer 7 | * https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 8 | * 9 | * Command shell on stdin/stdout. 10 | * Collects characters and parses commandstring. 11 | * Additional commands can easily be added. 12 | */ 13 | /* 14 | #include 15 | #include 16 | #include 17 | #include "pico/stdlib.h" 18 | */ 19 | #include "Arduino.h" 20 | /* 21 | #include "SPI.h" 22 | #include "TFT_eSPI.h" 23 | //#include "display.h" 24 | //#include "kiss_fftr.h" 25 | //#include "adc_fft.h" 26 | #include "dma.h" 27 | #include "pwm.h" 28 | #include "adc.h" 29 | #include "irq.h" 30 | #include "time.h" 31 | #include "multicore.h" 32 | */ 33 | 34 | //#include "lcd.h" 35 | #include "si5351.h" 36 | #include "dsp.h" 37 | #include "hmi.h" 38 | #include "relay.h" 39 | #include "monitor.h" 40 | #include "uSDR.h" 41 | 42 | 43 | #define CR 13 44 | #define LF 10 45 | #define SP 32 46 | #define CMD_LEN 80 47 | #define CMD_ARGS 16 48 | 49 | char mon_cmd[CMD_LEN+1]; // Command string buffer 50 | char *argv[CMD_ARGS]; // Argument pointers 51 | int nargs; // Nr of arguments 52 | 53 | typedef struct 54 | { 55 | const char *cmdstr; // Command string 56 | int cmdlen; // Command string length 57 | void (*cmd)(void); // Command executive 58 | const char *cmdsyn; // Command syntax 59 | const char *help; // Command help text 60 | } shell_t; 61 | 62 | 63 | 64 | 65 | 66 | /*** Initialisation, called at startup ***/ 67 | void mon_init() 68 | { 69 | // stdio_init_all(); // Initialize Standard IO 70 | mon_cmd[CMD_LEN] = '\0'; // Termination to be sure 71 | Serialx.print("\n"); 72 | Serialx.print("===================\n"); 73 | Serialx.print("**** ARJAN-5 ****\n"); 74 | Serialx.print(" 5 Band SSB/AM/CW \n"); 75 | Serialx.print(" HF Transceiver \n"); 76 | Serialx.print("======= by ========\n"); 77 | Serialx.print(" Arjan te Marvelde \n"); 78 | Serialx.print("Klaus Fensterseifer\n"); 79 | Serialx.print("=======2023========\n"); 80 | Serialx.print("Pico> "); // prompt 81 | } 82 | 83 | 84 | 85 | /*** ------------------------------------------------------------- ***/ 86 | /*** Below the definitions of the shell commands, add where needed ***/ 87 | /*** ------------------------------------------------------------- ***/ 88 | 89 | /* 90 | * Dumps a defined range of Si5351 registers 91 | */ 92 | uint8_t si5351_reg[200]; 93 | void mon_si(void) 94 | { 95 | int base=0, nreg=200, i; 96 | 97 | for (i=0; i=2) 145 | { 146 | ret = atoi(argv[2]); 147 | relay_setband((uint8_t)ret); 148 | } 149 | } 150 | ret = relay_getband(); 151 | if (ret<0) 152 | Serialx.println("I2C read error"); 153 | else 154 | Serialx.println(ret, HEX); 155 | } 156 | 157 | /* 158 | * Relay read or write 159 | */ 160 | void mon_rx(void) 161 | { 162 | int ret; 163 | 164 | if (*argv[1]=='w') 165 | { 166 | if (nargs>=2) 167 | { 168 | ret = atoi(argv[2]); 169 | relay_setattn((uint8_t)ret); 170 | } 171 | } 172 | ret = relay_getattn(); 173 | if (ret<0) 174 | Serialx.println("I2C read error"); 175 | else 176 | Serialx.println(ret, HEX); 177 | 178 | } 179 | 180 | /* 181 | * Command shell table, organize the command functions above 182 | */ 183 | #define NCMD 6 184 | shell_t shell[NCMD]= 185 | { 186 | {"si", 2, &mon_si, "si ", "Dumps Si5351 registers"}, 187 | {"lt", 2, &mon_lt, "lt (no parameters)", "LCD test, dumps characterset on LCD"}, 188 | {"pt", 2, &mon_pt, "pt (no parameters)", "Toggles PTT status"}, 189 | {"bp", 2, &mon_bp, "bp {r|w} ", "Read or Write BPF relays"}, 190 | {"rx", 2, &mon_rx, "rx {r|w} ", "Read or Write RX relays"} 191 | }; 192 | 193 | 194 | 195 | /*** ---------------------------------------- ***/ 196 | /*** Commandstring parser and monitor process ***/ 197 | /*** ---------------------------------------- ***/ 198 | 199 | /* 200 | * Command line parser 201 | */ 202 | void mon_parse(char* s) 203 | { 204 | char *p; 205 | int i; 206 | 207 | p = s; // Set to start of string 208 | nargs = 0; 209 | while (*p!='\0') // Assume stringlength >0 210 | { 211 | while (*p==' ') p++; // Skip whitespace 212 | if (*p=='\0') break; // String might end in spaces 213 | argv[nargs++] = p; // Store first valid char loc after whitespace 214 | while ((*p!=' ')&&(*p!='\0')) p++; // Skip non-whitespace 215 | } 216 | if (nargs==0) return; // No command or parameter 217 | for (i=0; i 0) 242 | { 243 | int c = Serialx.read(); 244 | switch (c) 245 | { 246 | case CR: // CR : need to parse command string 247 | Serialx.print('\n'); // Echo character, assume terminal appends CR 248 | mon_cmd[i] = '\0'; // Terminate command string 249 | if (i>0) // something to parse? 250 | mon_parse(mon_cmd); // --> process command 251 | i=0; // reset index 252 | Serialx.print("Pico> "); // prompt 253 | break; 254 | case LF: 255 | break; // Ignore, assume CR as terminator 256 | default: 257 | if ((c<32)||(c>=128)) break; // Only allow alfanumeric 258 | Serialx.print((char)c); // Echo character 259 | mon_cmd[i] = (char)c; // store in command string 260 | if (i 24 | #include 25 | #include "pico/stdlib.h" 26 | #include "hardware/i2c.h" 27 | */ 28 | #include "Arduino.h" 29 | /* 30 | #include "SPI.h" 31 | #include "TFT_eSPI.h" 32 | //#include "display.h" 33 | //#include "kiss_fftr.h" 34 | //#include "adc_fft.h" 35 | #include "dma.h" 36 | #include "pwm.h" 37 | #include "adc.h" 38 | #include "irq.h" 39 | #include "time.h" 40 | #include "multicore.h" 41 | */ 42 | 43 | #include "relay.h" 44 | #include "uSDR.h" 45 | 46 | 47 | 48 | 49 | void relay_setband(uint8_t val) 50 | { 51 | uint8_t data[2]; 52 | //int ret; 53 | 54 | data[0] = val&0x1f; 55 | // if (i2c_write_blocking(i2c1, I2C_BPF, data, 1, false) < 0) 56 | if (i2c_write_timeout_us(i2c1, I2C_BPF, data, 1, false, I2C_TIMEOUT_us) < 0) 57 | // i2c_write_blocking(i2c1, I2C_BPF, data, 1, false); 58 | i2c_write_timeout_us(i2c1, I2C_BPF, data, 1, false, I2C_TIMEOUT_us); 59 | } 60 | 61 | int relay_getband(void) 62 | { 63 | uint8_t data[2]; 64 | int ret; 65 | 66 | ret = i2c_read_blocking(i2c1, I2C_BPF, data, 1, false); 67 | if (ret>=0) 68 | ret=data[0]; 69 | return(ret); 70 | } 71 | 72 | 73 | 74 | void relay_setattn(uint8_t val) 75 | { 76 | uint8_t data[2]; 77 | 78 | data[0] = val&0x07; 79 | //Serialx.println("relay_setattn i2c_write_blocking start"); 80 | //int i2c_write_blocking (i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop). 81 | //if (i2c_write_blocking(i2c1, I2C_RX, data, 1, false) < 0) // true to keep master control of bus 82 | if (i2c_write_timeout_us(i2c1, I2C_RX, data, 1, false, I2C_TIMEOUT_us) < 0) // true to keep master control of bus 83 | { 84 | //Serialx.println("relay_setattn i2c_write_blocking if <0"); // false, we're done writing 85 | i2c_write_timeout_us(i2c1, I2C_RX, data, 1, false, I2C_TIMEOUT_us); 86 | } 87 | //Serialx.println("relay_setattn i2c_write_blocking end"); // false, we're done writing 88 | } 89 | 90 | int relay_getattn(void) 91 | { 92 | uint8_t data[2]; 93 | int ret; 94 | 95 | ret = i2c_read_blocking(i2c1, I2C_RX, data, 1, false); 96 | if (ret>=0) 97 | ret=data[0]; 98 | return(ret); 99 | } 100 | 101 | void relay_init(void) 102 | { 103 | /* done at hmi_init() 104 | relay_setattn(REL_PRE_10); 105 | sleep_ms(1); 106 | relay_setband(REL_BPF12); 107 | */ 108 | } 109 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/relay.h: -------------------------------------------------------------------------------- 1 | #ifndef __RELAY_H__ 2 | #define __RELAY_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* 9 | * relay.h 10 | * 11 | * Created: Nov 2021 12 | * Author: Arjan te Marvelde 13 | * 14 | * See relay.c for more information 15 | */ 16 | 17 | 18 | #ifdef PY2KLA_setup 19 | #define I2C_Arduino_Pro_Mini 1 //=1 when I2C BPF and Atten is commanded with Arduino Pro Mini (allow SWR reading) 20 | #else 21 | #define I2C_Arduino_Pro_Mini 0 //=0 when I2C BPF and Atten is commanded with PCF8574 22 | #endif 23 | 24 | /* I2C address and pins */ 25 | #define I2C_BPF 0x20 26 | #define I2C_RX 0x21 27 | #define I2C_SWR 0x22 // read 3 bytes = SWR, FOR and REF 28 | 29 | //#define I2C_WAIT_us (uint64_t)500000 absolute_time_t 30 | #define I2C_TIMEOUT_us 10000 31 | 32 | 33 | #define REL_LPF2 0x01 34 | #define REL_BPF6 0x02 35 | #define REL_BPF12 0x04 36 | #define REL_BPF24 0x08 37 | #define REL_BPF40 0x10 38 | 39 | #define REL_ATT_30 0x03 40 | #define REL_ATT_20 0x01 41 | #define REL_ATT_10 0x02 42 | #define REL_ATT_00 0x00 43 | #define REL_PRE_10 0x04 44 | 45 | extern void relay_setband(uint8_t val); 46 | extern void relay_setattn(uint8_t val); 47 | extern int relay_getband(void); 48 | extern int relay_getattn(void); 49 | extern void relay_init(void); 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | #endif 55 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/si5351.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * si5351.c 3 | * 4 | * Created: Jan 2020 5 | * Author: Arjan 6 | * May2022: adapted by Klaus Fensterseifer 7 | * https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 8 | 9 | Si5351 principle: 10 | ================= 11 | +-------+ +-------+ +------+ 12 | - Fxtal --> | * MSN | -- Fvco --> | / MSi | --> | / Ri | -- Fout --> 13 | +-------+ +-------+ +------+ 14 | 15 | ---Derivation of Fout--- 16 | MSN determines: Fvco = Fxtal * (MSN) , where MSN = a + b/c 17 | MSi and Ri determine: Fout = Fvco / (Ri*MSi) , where MSi = a + b/c (different a, b and c) 18 | 19 | ---Derivation of register values, for MSN and MSi--- 20 | P1 = 128*a + Floor(128*b/c) - 512 21 | P2 = 128*b - c*Floor(128*b/c) (P2 = 0 for MSi integer mode, or calculated for MSN tuning) 22 | P3 = c (P3 = 1 for MSi integer mode, or P3 = 1000000 for MSN tuning) 23 | 24 | This VFO implementation assumes PLLA is used for clk0 and clk1, PLLB is used for clk2 25 | 26 | Algorithm to get from frequency to synthesizer settings: 27 | (this assumes that the current settings are consistent, i.e. must be initialized at startup) 28 | | calculate new MSN from the desired Fout, based on current Ri and MSi 29 | | if MSN is still inside [600/Fxtal, 900/Fxtal] 30 | | then 31 | | just write the MSN parameter registers 32 | | else 33 | | re-calculate MSi, Ri and MSN from desired Fout 34 | | write the MSi and Ri parameter registers, including phase offset (MSi equals phase offset for 90 deg, use INV to shift 180deg more) 35 | | write the MSN parameter registers 36 | | reset PLL 37 | 38 | Ri=128 for Fout <1 MHz 39 | Ri= 32 for Fout 1-6 MHz 40 | Ri= 1 for Fout >6 MHz 41 | 42 | Some boundary values: 43 | Ri MSi Lo MHz Hi MHz 44 | 1 4 150.000000 225.000000 45 | 1 126 4.761905 7.142857 46 | 32 4 4.687500 7.031250 47 | 32 126 0.148810 0.223214 48 | 128 4 1.171875 1.757813 49 | 128 126 0.037202 0.055804 50 | 51 | MSi: target for mid-band, i.e. Fvco=750MHz 52 | MSi = 750MHz/(Fout*Ri) 53 | MSi &= 0xfe // Make it even 54 | 55 | MSN = MSi*Ri*Fout/Fxtal (should be between 24 and 36) 56 | 57 | Only use MSi even-integers, i.e. d=[4, 6, 8..126], e=0 and f=100000, and set INT bits in reg 22, 23. 58 | Quadrature Phase offsets (i.e. delay): 59 | - Offset for MS0 (reg 165) must be 0 (cosine), 60 | - Offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sine), 61 | - Set INV bit (reg 17) to add 180 deg. 62 | NOTE: Phase offsets only work when Ri = 1, this means minimum Fout is 4.762MHz at Fvco = 600MHz. Additional flip/flop dividers are needed to get 80m band frequencies, or Fvco must be tuned below spec. 63 | 64 | 65 | 66 | Control Si5351 (see AN619): 67 | =========================== 68 | ----+---------+---------+---------+---------+---------+---------+---------+---------+ 69 | @ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 70 | ----+---------+---------+---------+---------+---------+---------+---------+---------+ 71 | 0 |SYS_INIT | LOL_B | LOL_A | LOS | Reserved | REVID[1:0] | 72 | 1 |SYS_INIT | LOS_B | LOL_A | LOS | Reserved | 73 | | _STKY| _STKY| _STKY| _STKY| Reserved | 74 | 2 |SYS_INIT | LOS_B | LOL_A | LOS | Reserved | 75 | | _MASK| _MASK| _MASK| _MASK| Reserved | 76 | 3 | Reserved | CLK2_EN | CLK1_EN | CLK0_EN | 77 | ==== 78 | 9 | Reserved |OEB_MASK2|OEB_MASK1|OEB_MASK0| 79 | ==== 80 | 15 | CLKIN_DIV[2:0] | 0 | 0 | PLLB_SRC| PLLA_SRC| 0 | 0 | 81 | 16 | CLK0_PDN| MS0_INT | MS0_SRC | CLK0_INV| CLK0_SRC[1:0] | CLK0_IDRV[1:0] | 82 | 17 | CLK1_PDN| MS1_INT | MS1_SRC | CLK1_INV| CLK1_SRC[1:0] | CLK1_IDRV[1:0] | 83 | 18 | CLK2_PDN| MS2_INT | MS2_SRC | CLK2_INV| CLK2_SRC[1:0] | CLK2_IDRV[1:0] | 84 | ==== 85 | 22 | Reserved| FBA_INT | Reserved | 86 | 23 | Reserved| FBB_INT | Reserved | 87 | 24 | Reserved | CLK2_DIS_STATE | CLK1_DIS_STATE | CLK0_DIS_STATE | 88 | ==== 89 | 26 | MSNA_P3[15:8] | 90 | 27 | MSNA_P3[7:0] | 91 | 28 | Reserved | MSNA_P1[17:16] | 92 | 29 | MSNA_P1[15:8] | 93 | 30 | MSNA_P1[7:0] | 94 | 31 | MSNA_P3[19:16] | MSNA_P2[19:16] | 95 | 32 | MSNA_P2[15:8] | 96 | 33 | MSNA_P2[7:0] | 97 | 98 | 34: Same pattern for PLLB 99 | 100 | 42 | MS0_P3[15:8] | 101 | 43 | MS0_P3[7:0] | 102 | 44 | Reserved| R0_DIV[2:0] | MS0_DIVBY4[1:0] | MS0_P1[17:16] | 103 | 45 | MS0_P1[15:8] | 104 | 46 | MS0_P1[7:0] | 105 | 47 | MS0_P3[19:16] | MS0_P2[19:16] | 106 | 48 | MS0_P2[15:8] | 107 | 49 | MS0_P2[7:0] | 108 | 109 | 50: Same pattern for CLK1 110 | 111 | 58: Same pattern for CLK2 112 | 113 | ==== 114 | 165 | Reserved| CLK0_PHOFF[6:0] | 115 | 166 | Reserved| CLK1_PHOFF[6:0] | 116 | 167 | Reserved| CLK2_PHOFF[6:0] | 117 | ==== 118 | 177 | PLLB_RST| Reserved| PLLA_RST| Reserved | 119 | ==== 120 | 183 | XTAL_CL | Reserved | 121 | ==== 122 | 123 | */ 124 | 125 | #include "Arduino.h" 126 | #include "uSDR.h" 127 | #include "dsp.h" 128 | #include "si5351.h" 129 | 130 | 131 | 132 | #define I2C_VFO 0x60 // I2C address 133 | 134 | 135 | #if TX_METHOD == I_Q_QSE 136 | #define i2c_write_blocking_ i2c_write_blocking 137 | #define i2c_read_blocking_ i2c_read_blocking 138 | #endif 139 | #if TX_METHOD == PHASE_AMPLITUDE 140 | #include "uSDX_SI5351.h" 141 | 142 | #define i2c_write_blocking_ uSDX_i2c_write_blocking 143 | #define i2c_read_blocking_ uSDX_i2c_read_blocking 144 | #endif 145 | 146 | 147 | // SI5351 register address definitions 148 | #define SI_CLK_OE 3 149 | #define SI_CLK0_CTL 16 150 | #define SI_CLK1_CTL 17 151 | #define SI_CLK2_CTL 18 152 | #define SI_SYNTH_PLLA 26 153 | #define SI_SYNTH_PLLB 34 154 | #define SI_SYNTH_MS0 42 155 | #define SI_SYNTH_MS1 50 156 | #define SI_SYNTH_MS2 58 157 | #define SI_SS_EN 149 158 | #define SI_CLK0_PHOFF 165 159 | #define SI_CLK1_PHOFF 166 160 | #define SI_CLK2_PHOFF 167 161 | #define SI_PLL_RESET 177 162 | #define SI_XTAL_LOAD 183 163 | 164 | // CLK_OE register 3 values 165 | //#define SI_CLK0_ENABLE 0b00000001 // Enable clock 0 output 166 | //#define SI_CLK1_ENABLE 0b00000010 // Enable clock 1 output 167 | //#define SI_CLK2_ENABLE 0b00000100 // Enable clock 2 output 168 | 169 | // CLKi_CTL register 16, 17, 18 values 170 | // Normally 0x4f for clk 0 and 1, 0x6f for clk 2 171 | #define SI_CLK_INT 0b01000000 // Set integer mode 172 | #define SI_CLK_PLL 0b00100000 // Select PLL B as MS source (default 0 = PLL A) 173 | #define SI_CLK_INV 0b00010000 // Invert output (i.e. phase + 180deg) 174 | #define SI_CLK_SRC 0b00001100 // Select output source: 11=MS, 00=XTAL direct 175 | #define SI_CLK_DRV 0b00000011 // Select output drive, increasingly: 2-4-6-8 mA (best risetime, use max = 11) 176 | 177 | // PLL_RESET register 177 values 178 | #define SI_PLLB_RST 0b10000000 // Reset PLL B 179 | #define SI_PLLA_RST 0b00100000 // Reset PLL A 180 | 181 | 182 | #ifdef PY2KLA_setup 183 | #define SI_XTAL_FREQ (25000000UL-375UL) // Replace with measured crystal frequency of XTAL for CL = 10pF (default) 0.0 184 | #else 185 | #define SI_XTAL_FREQ (25000000UL-250UL) // Replace with measured crystal frequency of XTAL for CL = 10pF (default) 186 | #endif 187 | #define SI_MSN_LO ((0.6e9)/SI_XTAL_FREQ) 188 | #define SI_MSN_HI ((0.9e9)/SI_XTAL_FREQ) 189 | #define SI_PLL_C 1000000UL // Parameter c for PLL-A and -B setting 190 | 191 | 192 | 193 | vfo_t vfo[2]; // 0: clk0 and clk1 1: clk2 194 | 195 | /* read contents of SI5351 registers, from reg to reg+len-1, output in data array */ 196 | int si_getreg(uint8_t *data, uint8_t reg, uint8_t len) 197 | { 198 | int ret; 199 | 200 | ret = i2c_write_blocking_(i2c0, I2C_VFO, ®, 1, true); 201 | if (ret<0) printf ("I2C write error\n"); 202 | ret = i2c_read_blocking_(i2c0, I2C_VFO, data, len, false); 203 | if (ret<0) printf ("I2C read error\n"); 204 | return(len); 205 | } 206 | 207 | 208 | // Set up MSN PLL divider for vfo[i], assuming MSN has been set in vfo[i] 209 | // Optimize for speed, this may be called with short intervals 210 | // See also SiLabs AN619 section 3.2 211 | void si_setmsn(uint8_t i) 212 | { 213 | uint8_t data[16]; // I2C trx buffer 214 | uint32_t P1, P2; // MSN parameters 215 | uint32_t A; 216 | uint32_t B; 217 | 218 | i=(i>0?1:0); 219 | /* 220 | P1 = 128*a + Floor(128*b/c) - 512 221 | P2 = 128*b - c*Floor(128*b/c) 222 | P3 = c (P3 = 1000000 for MSN tuning) 223 | */ 224 | A = (uint32_t)(floor(vfo[i].msn)); // A is integer part of MSN 225 | B = (uint32_t)((vfo[i].msn - (float)A) * SI_PLL_C); // B is C * fraction part of MSN (C is a constant) 226 | P2 = (uint32_t)(floor((float)(128 * B) / (float)SI_PLL_C)); 227 | P1 = (uint32_t)(128 * A + P2 - 512); 228 | P2 = (uint32_t)(128 * B - SI_PLL_C * P2); 229 | 230 | // transfer registers 231 | data[0] = (i==0?SI_SYNTH_PLLA:SI_SYNTH_PLLB); 232 | data[1] = (SI_PLL_C & 0x0000FF00) >> 8; 233 | data[2] = (SI_PLL_C & 0x000000FF); 234 | data[3] = (P1 & 0x00030000) >> 16; 235 | data[4] = (P1 & 0x0000FF00) >> 8; 236 | data[5] = (P1 & 0x000000FF); 237 | data[6] = ((SI_PLL_C & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16); 238 | data[7] = (P2 & 0x0000FF00) >> 8; 239 | data[8] = (P2 & 0x000000FF); 240 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 241 | } 242 | 243 | // Set up registers with MS and R divider for vfo[i], assuming values have been set in vfo[i] 244 | // In this implementation we only use integer mode, i.e. b=0 and P3=1 245 | // See also SiLabs AN619 section 4.1 246 | void si_setmsi(uint8_t i) 247 | { 248 | uint8_t data[16]; // I2C trx buffer 249 | uint32_t P1; 250 | uint8_t R; 251 | 252 | i=(i>0?1:0); 253 | /* 254 | P1 = 128*a + Floor(128*b/c) - 512 255 | P2 = 128*b - c*Floor(128*b/c) (P2 = 0 for MSi integer mode) 256 | P3 = c (P3 = 1 for MSi integer mode) 257 | */ 258 | P1 = (uint32_t)(128*(uint32_t)floor(vfo[i].msi) - 512); 259 | R = vfo[i].ri; 260 | R = (R&0xf0) ? ((R&0xc0)?((R&0x80)?7:6):(R&0x20)?5:4) : ((R&0x0c)?((R&0x08)?3:2):(R&0x02)?1:0); // quick log2(r) 261 | 262 | data[0] = (i==0?SI_SYNTH_MS0:SI_SYNTH_MS2); 263 | data[1] = 0x00; 264 | data[2] = 0x01; 265 | data[3] = ((P1 & 0x00030000) >> 16) | (R << 4 ); 266 | data[4] = (P1 & 0x0000FF00) >> 8; 267 | data[5] = (P1 & 0x000000FF); 268 | data[6] = 0x00; 269 | data[7] = 0x00; 270 | data[8] = 0x00; 271 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 272 | 273 | // If vfo[0] also set clk 1 274 | if (i==0) 275 | { 276 | data[0] = SI_SYNTH_MS1; // Same data in synthesizer 277 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 278 | 279 | if (vfo[0].phase&1) // Phase is either 90 or 270 deg? 280 | { 281 | data[0] = SI_CLK1_PHOFF; 282 | data[1] = vfo[0].msi; 283 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 284 | } 285 | else // Phase is 0 or 180 deg 286 | { 287 | data[0] = SI_CLK1_PHOFF; 288 | data[1] = 0; 289 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 290 | } 291 | if (vfo[0].phase&2) // Phase is 180 or 270 deg? 292 | { 293 | data[0] = SI_CLK1_CTL; 294 | data[1] = 0x5d; // CLK1: INT, PLLA, INV, MS, 8mA 295 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 296 | } 297 | } 298 | 299 | // Reset associated PLL 300 | data[0] = SI_PLL_RESET; 301 | data[1] = (i==1)?0x80:0x20; 302 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 303 | } 304 | 305 | 306 | // For each vfo, calculate required MSN setting, MSN = MSi*Ri*Fout/Fxtal 307 | // If in range, just set MSN registers 308 | // If not in range, recalculate MSi and Ri and also MSN 309 | // Set MSN, MSi and Ri registers (implicitly resets PLL) 310 | void si_evaluate(void) 311 | { 312 | float msn; 313 | 314 | if (vfo[0].flag) 315 | { 316 | msn = (float)(vfo[0].msi); // Re-calculate MSN 317 | msn = msn * (float)(vfo[0].ri); 318 | msn = msn * (float)(vfo[0].freq) / SI_XTAL_FREQ; 319 | if ((msn>=SI_MSN_LO)&&(msn= 3000000)&&(vfo[0].freq < 6000000)) // Low end of Ri=1 range 328 | vfo[0].msi = (uint8_t)126; // Maximum MSi on Fvco=(4x126)MHz 329 | else 330 | vfo[0].msi = (uint8_t)(750000000UL / (vfo[0].freq * vfo[0].ri)) & 0xfe; // Calculate MSi on Fvco=750MHz 331 | msn = (float)(vfo[0].msi); // Re-calculate MSN 332 | msn = msn * (float)(vfo[0].ri); 333 | msn = msn * (float)(vfo[0].freq) / SI_XTAL_FREQ; 334 | vfo[0].msn = msn; 335 | si_setmsn(0); 336 | si_setmsi(0); 337 | } 338 | vfo[0].flag = 0; 339 | } 340 | if (vfo[1].flag) 341 | { 342 | vfo[1].flag = 0; 343 | } 344 | } 345 | 346 | 347 | // Initialize the Si5351 VFO registers 348 | void si_init(void) 349 | { 350 | uint8_t data[16]; // I2C trx buffer 351 | 352 | // Hard initialize Synth registers: 7.074MHz, CLK1 90 deg ahead, PLLA for CLK 0&1, PLLB for CLK2 353 | // Ri=1, 354 | // MSi=68, P1=8192, P2=0, P3=1 355 | // MSN=27.2 P1=2969, P2=600000, P3=1000000 356 | vfo[0].freq = 10000000; 357 | vfo[0].flag = 0; 358 | vfo[0].phase = 1; 359 | vfo[0].ri = 1; 360 | vfo[0].msi = 68; 361 | vfo[0].msn = 27.2; 362 | vfo[1].freq = 10000000; 363 | vfo[1].flag = 0; 364 | vfo[1].phase = 0; 365 | vfo[1].ri = 1; 366 | vfo[1].msi = 68; 367 | vfo[1].msn = 27.2; 368 | 369 | // PLLA: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240 370 | data[0] = SI_SYNTH_PLLA; 371 | data[1] = 0x42; // MSNA_P3[15:8] 372 | data[2] = 0x40; // MSNA_P3[7:0] 373 | data[3] = 0x00; // 0b000000 , MSNA_P1[17:16] 374 | data[4] = 0x0b; // MSNA_P1[15:8] 375 | data[5] = 0x99; // MSNA_P1[7:0] 376 | data[6] = 0xf9; // MSNA_P3[19:16] , MSNA_P2[19:16] 377 | data[7] = 0x27; // MSNA_P2[15:8] 378 | data[8] = 0xc0; // MSNA_P2[7:0] 379 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 380 | 381 | 382 | // PLLB: MSN P1=0x00000b99, P2=0x000927c0, P3=0x000f4240 383 | data[0] = SI_SYNTH_PLLB; // Same content 384 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 385 | 386 | // MS0 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 387 | data[0] = SI_SYNTH_MS0; 388 | data[1] = 0x00; // MS0_P3[15:8] 389 | data[2] = 0x01; // MS0_P3[7:0] 390 | data[3] = 0x00; // 0b0, R0_DIV[2:0] , MS0_DIVBY4[1:0] , MS0_P1[17:16] 391 | data[4] = 0x20; // MS0_P1[15:8] 392 | data[5] = 0x00; // MS0_P1[7:0] 393 | data[6] = 0x00; // MS0_P3[19:16] , MS0_P2[19:16] 394 | data[7] = 0x00; // MS0_P2[15:8] 395 | data[8] = 0x00; // MS0_P2[7:0] 396 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 397 | 398 | // MS1 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 399 | data[0] = SI_SYNTH_MS1; // Same content 400 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 401 | 402 | // MS2 P1=0x00002000, P2=0x00000000, P3=0x00000001, R=1 403 | data[0] = SI_SYNTH_MS2; // Same content 404 | i2c_write_blocking_(i2c0, I2C_VFO, data, 9, false); 405 | 406 | // Phase offsets for 3 clocks 407 | data[0] = SI_CLK0_PHOFF; 408 | data[1] = 0x00; // CLK0: phase 0 deg 409 | data[2] = 0x44; // CLK1: phase 90 deg (=MSi) 410 | data[3] = 0x00; // CLK2: phase 0 deg 411 | i2c_write_blocking_(i2c0, I2C_VFO, data, 4, false); 412 | 413 | // Output port settings for 3 clocks 414 | data[0] = SI_CLK0_CTL; 415 | data[1] = 0x4d; // CLK0: INT, PLLA, nonINV, MS, 4mA 416 | data[2] = 0x4d; // CLK1: INT, PLLA, nonINV, MS, 4mA 417 | data[3] = 0x6f; // CLK2: INT, PLLB, nonINV, MS, 8mA 418 | i2c_write_blocking_(i2c0, I2C_VFO, data, 4, false); 419 | 420 | // Disable spread spectrum (startup state is undefined) 421 | data[0] = SI_SS_EN; 422 | data[1] = 0x00; 423 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 424 | 425 | // Reset both PLL 426 | data[0] = SI_PLL_RESET; 427 | data[1] = 0xa0; 428 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 429 | 430 | // Enable all outputs 431 | data[0] = SI_CLK_OE; //reg address 432 | #ifdef PY2KLA_setup 433 | data[1] = 0xfe; // enable clk0 434 | #else 435 | data[1] = 0x00; //0 = enable all 436 | #endif 437 | i2c_write_blocking_(i2c0, I2C_VFO, data, 2, false); 438 | } 439 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/si5351.h: -------------------------------------------------------------------------------- 1 | #ifndef _SI5351_H 2 | #define _SI5351_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* 9 | * si5351.h 10 | * 11 | * Created: March 2021 12 | * Author: Arjan 13 | * 14 | * See si5351.c for more information 15 | * 16 | */ 17 | 18 | 19 | 20 | typedef struct 21 | { 22 | uint32_t freq; // type can hold up to 4GHz 23 | uint8_t flag; // flag != 0 when update needed 24 | uint8_t phase; // in quarter waves (0, 1, 2, 3) 25 | uint8_t ri; // Ri (1 .. 128) 26 | uint8_t msi; // MSi parameter a (4, 6, 8 .. 126) 27 | float msn; // MSN (24.0 .. 35.9999) 28 | } vfo_t; 29 | extern vfo_t vfo[2]; // Table contains all control data for three clk outputs, but 0 and 1 are coupled in vfo[0] 30 | 31 | 32 | int si_getreg(uint8_t *data, uint8_t reg, uint8_t len); 33 | void si_init(void); 34 | void si_evaluate(void); 35 | 36 | 37 | //#define SI_GETFREQ(i) ((((i)>=0)&&((i)<2))?vfo[(i)].freq:0) 38 | //#define SI_INCFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)<(150000000-(d)))) { vfo[(i)].freq += (d); vfo[(i)].flag = 1;} 39 | //#define SI_DECFREQ(i, d) if ((((i)>=0)&&((i)<2))&&((vfo[(i)].freq)>(d))) { (vfo[(i)].freq) -= (d); vfo[(i)].flag = 1;} 40 | #define SI_SETFREQ(i, f) if ((((i)>=0)&&((i)<2))&&((f)<150000000)) { vfo[(i)].freq = (f); vfo[(i)].flag = 1;} 41 | #define SI_SETPHASE(i, p) if (((i)>=0)&&((i)<2)) {vfo[(i)].phase = ((uint8_t)p)&3; vfo[(i)].flag = 1;} 42 | 43 | 44 | 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif /* _SI5351_H */ 50 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/uSDR.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * uSDR.c 3 | * 4 | * Created: Mar 2021 5 | * Author: Arjan te Marvelde 6 | * May2022: adapted by Klaus Fensterseifer 7 | * https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 8 | * 9 | * The application main loop 10 | * This initializes the units that do the actual work, and then loops in the background. 11 | * Other units are: 12 | * - dsp.c, containing all signal processing in RX and TX branches. This part runs on the second processor core. 13 | * - si5351.c, containing all controls for setting up the si5351 clock generator. 14 | * - lcd.c, contains all functions to put something on the LCD 15 | * - hmi.c, contains all functions that handle user inputs 16 | */ 17 | 18 | #include "Arduino.h" 19 | 20 | #include 21 | #include "uSDR.h" 22 | #include "hmi.h" 23 | #include "dsp.h" 24 | #include "si5351.h" 25 | #include "monitor.h" 26 | #include "relay.h" 27 | #include "TFT_eSPI.h" 28 | #include "display_tft.h" 29 | #include "Dflash.h" 30 | 31 | 32 | 33 | 34 | 35 | uint16_t tim_loc; // local time 36 | 37 | void uSDR_setup0(void) //main 38 | { 39 | display_tft_setup0(); 40 | 41 | } 42 | 43 | 44 | 45 | void uSDR_setup(void) //main 46 | { 47 | //gpio_init_mask(1<<14); 48 | //gpio_set_dir(14, GPIO_OUT); 49 | 50 | 51 | //Serialx.println("uSDR_setup Wire.begin"); 52 | /* 53 | * i2c0 is used for the si5351 interface 54 | * i2c1 is used for the LCD and all other interfaces 55 | */ 56 | #if TX_METHOD == I_Q_QSE //original project 57 | Wire.begin(); //i2c0 master to Si5351 58 | //Wire.setClock(200000); // Set i2c0 clock speed (default=100k) 59 | #endif 60 | Wire1.begin(); //i2c1 used for switching band and atten/LNA 61 | //Wire1.setTimeout(1000); // sets maximum milliseconds to wait for stream data, default is 1 second 62 | 63 | 64 | /* Initialize units */ 65 | //Serialx.println("uSDR_setup mon_init"); 66 | mon_init(); // Monitor shell on stdio 67 | //Serialx.println("uSDR_setup si_init"); 68 | si_init(); // VFO control unit 69 | //Serialx.println("uSDR_setup relay_init"); 70 | relay_init(); 71 | Dflash_setup(); 72 | //Serialx.println("uSDR_setup display_tft_setup"); 73 | display_tft_setup(); //moved to setup0 to write into display from the beggining 74 | //Serialx.println("uSDR_setup hmi_init"); 75 | hmi_init(); // HMI user inputs 76 | //Serialx.println("uSDR_setup dsp_init"); 77 | dsp_init(); // Signal processing unit 78 | 79 | tim_loc = tim_count; // local time for main loop 80 | //digitalWrite(14, LOW); 81 | } 82 | 83 | 84 | 85 | 86 | void uSDR_loop(void) 87 | { 88 | 89 | if((uint16_t)(tim_count - tim_loc) >= (uint16_t)LOOP_MS) //run the tasks every 100ms (LOOP_MS = 100) 90 | { 91 | hmi_evaluate(); // Refresh HMI 92 | si_evaluate(); // Refresh VFO settings 93 | mon_evaluate(); // Check monitor input 94 | dsp_loop(); //spend more time here for FFT and graphic 95 | display_tft_loop(); // Refresh display 96 | //it takes 50ms for the tasks (most in hmi_evaluate() to plot the waterfall) 97 | 98 | tim_loc += LOOP_MS; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Arduino_uSDX_Pico_FFT/uSDR.h: -------------------------------------------------------------------------------- 1 | #ifndef __uSDR_H__ 2 | #define __uSDR_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | 9 | //#define PY2KLA_setup 1 //setup for PY2KLA hardware (comment this line for default setup) 10 | #define SW_VERSION "Dec24" //software version 11 | 12 | //choose the serial to be used (names come from MBed library, look at "pins_arduino.h" and comments at .ino file) 13 | //#define Serialx SerialUSB //USB virtual serial /dev/ttyACM0 14 | #define Serialx Serial1 //UART0 /dev/ttyUSB0 15 | 16 | #define LOOP_MS 100 //100 miliseconds 17 | 18 | 19 | void uSDR_setup0(void); 20 | void uSDR_setup(void); 21 | void uSDR_loop(void); 22 | 23 | 24 | 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /Aux/Components/LM386_amplifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Aux/Components/LM386_amplifier.png -------------------------------------------------------------------------------- /Aux/Components/MP2315_DCDC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Aux/Components/MP2315_DCDC.png -------------------------------------------------------------------------------- /Aux/Components/RP2040_YD2040_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Aux/Components/RP2040_YD2040_comparison.png -------------------------------------------------------------------------------- /Aux/Components/TEA2025_pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Aux/Components/TEA2025_pcb.png -------------------------------------------------------------------------------- /Aux/TFilter_Engineerjs.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Aux/TFilter_Engineerjs.zip -------------------------------------------------------------------------------- /Encoder_selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Encoder_selection.png -------------------------------------------------------------------------------- /FFT_LCD_pico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/FFT_LCD_pico.png -------------------------------------------------------------------------------- /FFT_LCD_pico_MOD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/FFT_LCD_pico_MOD.png -------------------------------------------------------------------------------- /FFT_LCD_pico_MOD2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/FFT_LCD_pico_MOD2.png -------------------------------------------------------------------------------- /HFD3_characteristics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/HFD3_characteristics.png -------------------------------------------------------------------------------- /PCB/SWR/SWR_B_Mask.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/SWR/SWR_B_Mask.pdf -------------------------------------------------------------------------------- /PCB/SWR/SWR_F_Mask.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/SWR/SWR_F_Mask.pdf -------------------------------------------------------------------------------- /PCB/SWR/SWR_KiCad.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/SWR/SWR_KiCad.zip -------------------------------------------------------------------------------- /PCB/SWR/SWR_PCB.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/SWR/SWR_PCB.pdf -------------------------------------------------------------------------------- /PCB/SWR/SWR_SCH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/SWR/SWR_SCH.pdf -------------------------------------------------------------------------------- /PCB/uSDR_Pico_BPF_RX_Kicad.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/uSDR_Pico_BPF_RX_Kicad.zip -------------------------------------------------------------------------------- /PCB/uSDR_Pico_BPF_RX_SCH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/uSDR_Pico_BPF_RX_SCH.pdf -------------------------------------------------------------------------------- /PCB/uSDR_Pico_FFT_Kicad.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/uSDR_Pico_FFT_Kicad.zip -------------------------------------------------------------------------------- /PCB/uSDR_Pico_FFT_SCH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/PCB/uSDR_Pico_FFT_SCH.pdf -------------------------------------------------------------------------------- /Pict1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pict1.png -------------------------------------------------------------------------------- /Pictures/Arjan_5_BPF_RX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Arjan_5_BPF_RX.png -------------------------------------------------------------------------------- /Pictures/Arjan_5_block_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Arjan_5_block_diagram.png -------------------------------------------------------------------------------- /Pictures/Cardboard_box_prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Cardboard_box_prototype.png -------------------------------------------------------------------------------- /Pictures/CwDecoder_display_area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/CwDecoder_display_area.png -------------------------------------------------------------------------------- /Pictures/Display_SWR_Power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Display_SWR_Power.png -------------------------------------------------------------------------------- /Pictures/Mod_include_RC_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Mod_include_RC_audio.png -------------------------------------------------------------------------------- /Pictures/Mod_include_diode_Vcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Mod_include_diode_Vcc.png -------------------------------------------------------------------------------- /Pictures/PTTout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/PTTout.png -------------------------------------------------------------------------------- /Pictures/Prototype_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/Prototype_2.png -------------------------------------------------------------------------------- /Pictures/uSDR_Pico_FFT_PCB_botton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/uSDR_Pico_FFT_PCB_botton.png -------------------------------------------------------------------------------- /Pictures/uSDR_Pico_FFT_PCB_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Pictures/uSDR_Pico_FFT_PCB_top.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ARJAN-5 2 | ## 5 Band SSB/AM/CW HF Transceiver 3 | ## by Klaus Fensterseifer - PY2KLA 4 | ### (based on Arjan te Marvelde / uSDR-pico) 5 | 6 | ![uSDR-PICO FFT](Pict1.png) 7 | 8 | This project is a QSD/QSE Software Defined HF Transceiver (SDR), 5 Band, Low Power, based on Arjan te Marvelde / uSDR-pico, from https://github.com/ArjanteMarvelde/uSDR-pico.
9 | I started this project based on Arjan's version https://github.com/ArjanteMarvelde/uSDR-pico/blob/main/package/CODEv2.zip from 2021 with documentation at https://github.com/ArjanteMarvelde/uSDR-pico/blob/main/doc/uSDR%20-%20v2.02.pdf . 10 | 11 | My intention was to include a Waterfall or Panadapter to the Arjan's uSDR-Pico project. For this, I included an ILI9341 240x320 2.4" TFT display, without touch, and also, changed the software to generate the Waterfall. 12 | 13 | Initially, I used Visual Studio, like the original project, but after some considerations, I ported all code to Arduino IDE. So, to compile and run this code you need the Arduino IDE installed for a Raspberry Pi Pico project (see "Arduino IDE setup and notes:" below). 14 | 15 | I also, chose not to change the original software as much as possible, and focused on the Waterfall implementation, mostly in the dsp.c. 16 | 17 | The Arjan-5 and uSDR-Pico **DO NOT USE** the uSDX Amplitude/Phase for transmission like the PE1NNZ uSDX project. 18 | 19 | I used the word "uSDX" instead of "uSDR" to name some files. This was a mistake. My intention was to follow Arjan's project with the same names.
20 | 21 | ## Basic connections for initial tests 22 | ![Main Block Diagram](FFT_LCD_pico.png) 23 |
24 | 25 | Initial tests video: https://youtu.be/0zGAnkRjizE
26 | AGC and Visual Scope video: https://youtu.be/BiaS002xZfw
27 | Transmission test video: https://drive.google.com/file/d/1Rr4CVPphtcBfMTgRN5z8F7xxByQ_1ybG/view
28 | 29 | There are also some messages related to this project at:
30 | https://groups.io/g/ucx
31 | Subject: uSDR-pico on GitHUB
32 | Initial msg: #15923 · May 26 2022
33 |
34 | 35 | ## Repository folders 36 | **Arduino_I2C_BPF_RX** - Code to control the Band Pass Filter (BPF) board. It runs at Arduino Pro Mini, compiled with Arduino IDE. The Arduino Pro Mini is used replacing the uSDR-Pico I2C interfaces PCF8574's to allow extra features: SWR reading and others (future). It uses the same I2C protocol, as uSDR-Pico.
37 | **Arduino_uSDX_Pico_FFT** - Main code for Ajan-5, it runs at Raspberry Pi Pico, compiled with Arduino IDE (look "Arduino IDE setup and notes" below).
38 | **Aux** - Files used as reference.
39 | **PCB** - Schematic and PCB Layout on Kicad format.
40 | **Pictures** - Pictures in general used in this Readme file.
41 | **uSDX_TX** - Code to test RF modulation TX using Amplitude/Phase, the same method used at the PE1NNZ uSDX project (https://github.com/threeme3/usdx) running at RP2040, only for transmission.
42 |
43 |
44 | 45 | ## Main Board Block Diagram 46 | - Arjan-5 uses the same modules connections as uSDR-pico. 47 | 48 | ![Main Block Diagram](Pictures/Arjan_5_block_diagram.png) 49 |
50 | 51 | ## Band Pass Filters, Attenuators and Low Noise Amplifier Board Block Diagram 52 | ![BPF RX Block Diagram](Pictures/Arjan_5_BPF_RX.png) 53 |
54 | 55 | ## Main Board Top and Botton 56 | 57 | ![Main Board Top](Pictures/uSDR_Pico_FFT_PCB_top.png) 58 |
59 | ![Main Board Botton](Pictures/uSDR_Pico_FFT_PCB_botton.png) 60 |
61 | Obs.: Don't mind the red wires on the PCB, they are only test for separated 5V power supply. 62 |
63 | 64 | ## First Prototype with TX and RX 65 | 66 | ![Prototype](Pictures/Cardboard_box_prototype.png) 67 | 68 | ## Second Prototype (all in a metallic box) 69 | 70 | ![Prototype2](Pictures/Prototype_2.png) 71 | 72 | 73 | 74 |
75 | 76 | ## Microcontroller RP2040 notes: 77 | - Core0 and Core1 are too much connected and affect each other. This made me lose some painful hours... 78 | - There are only 3 ADC ports available. 79 | - There are some reports at internet about the low quality of the RP2040 ADC readings (https://pico-adc.markomo.me/). 80 | 81 | 82 | 83 | 84 | ## Hardware changes from the original SDR-Pico and notes: 85 | - I chose to make a main board with the Pico, Display, QSD/QSE and 5V power supply, and another board with the relays, filters and attenuators. 86 | - The main change from SDR-Pico is the inclusion of ILI9341 on Pico free pins, using SPI1, and removing the LCD display. 87 | - We need more frequency range on RX to show at the Waterfall, the RX amplifier must amplify at least 80kHz of signal band. The opamp must be changed to one with wider bandwidth. 88 | - I will use an Arduino Pro Mini to work as two I2C slaves, replacing the PCF8574's on the BPF RX board. This will give more ADCs to read the SWR values. 89 | - You can see the schematic diagram at [uSDR_Pico_FFT_SCH.pdf](PCB/uSDR_Pico_FFT_SCH.pdf) and [uSDR_Pico_BPF_RX_SCH.pdf](PCB/uSDR_Pico_BPF_RX_SCH.pdf). 90 | - I noticed that changing the signal in one ADC input, changed the other inputs signal through the resistors for setting half Vref. To solve this, I changed the circuit to have a separate resistor divider for each ADC input. 91 | 92 | ![Hardware Modification](FFT_LCD_pico_MOD.png) 93 |
94 | 95 | - Use input/output filters for ADC Aliasing considerations (see below). 96 | - Obs.: at the initial test video, I used only the RC output filter shown in the schematic, and for input filter, only what is already inside of the Softrock RXTX Ensemble. 97 |
98 | 99 | ## Hardware changes from initial version of my Schematic and PCB: 100 | - Include a reversion VCC connector protection = series diode 101 | ![Diode_VCC](Pictures/Mod_include_diode_Vcc.png) 102 | 103 | - Include an extra RC to the audio output filter 104 | ![RC_audio](Pictures/Mod_include_RC_audio.png) 105 | 106 | - The PTT output signal generated on GPIO15 (pin 20) used to switch the filters and the Power Amplifier needs: 107 | A transistor to convert from 3v3 to 12V.
108 | Include a 1K resistor between GPIO15 (pin 20) and 3V3 (pin 36).
109 | Increase the value of C44 from 1n to 100n.
110 | Reduce the value of R25 to 10R. 111 | ![Main Block Diagram](Pictures/PTTout.png) 112 |
113 | 114 | ## Software Notes
115 | ### Arduino IDE setup and notes: 116 | - I am using Arduino IDE version 2.0.1 in Linux/Ubuntu 117 | - Lib used: TFT_eSPI by Bodmer 118 | - **IMPORTANT: Use the comments at beginning of .ino file to "adjust" the library files to the project.** 119 | - Boards Manager: Arduino Mbed OS RP2040 Boards. My version is 4.0.2 (Every time I update it, I will need to "adjust" the library files again). 120 | - Do not include EarlePhilhower library (it is conflitant with Mbed) 121 | - Board: "RaspberryPiPico" > Arduino Mbed OS RP2040 Boards > RaspberryPiPico 122 | - The code files have cpp type, but the code itself is in C (cpp type is used to help in some compiler issues). 123 | 124 | 125 | ### To implement the Waterfall I considered this: 126 | 127 | - There are 3 ADC inputs: I, Q and MIC (if we remove the VOX function, we could remove the MIC ADC during reception, this will increase the ADC frequency for I and Q, improving the frequencies we can see at the display - for now I will keep it like the original). 128 | - The max ADC frequency is 500kHz, I have changed it to 480kHz (close to the original) to make the divisions "rounded". 129 | - The ADC for audio reception has frequency of 16kHz (close to the original). I have tested higher frequencies, but the time became critical, without so much benefit. 130 | - The max ADC frequency for each sample = 480kHz / 3 = 160kHz (because there is only one internal ADC used to read the 3 inputs in sequence). 131 | - With 160kHz of samples, we can see 80kHz range after the FFT, but applying Hilbert to get the lower and the upper band, we get two bands of 80kHz, above and below the center frequency. 132 | - There is no time to process each sample at 160kHz and generate the "live" audio, so I use this method: 133 | Set the DMA to receive 10 samples of each ADC input (10 x 3 = 30) and generate an interrupt. 134 | So, we get 16kHz interrupts with 10 x 3 samples to deal.
135 | For audio, we need only one sample at each interruption of 16kHz. At this point, there is a low pass filter to remove any frequency above 8kHz.
136 | For FFT, we need all samples (raw samples), so they are copied to a FFT buffer for later use. 137 | - There is also no time to process the samples and run the audio receiver part at 16kHz, so I chose to split it. The interrupt and buffer/filter part is done at Core1, and the audio original reception is in the Core0. 138 | - Every 16kHz interrupt, after filtering the I, Q and MIC, these samples are passed to Core0 to follow the audio reception tasks. 139 | - For the Waterfall, when we have received 320 I and Q samples, it stops filling the buffer and indicates to the Core1 main loop to process FFT/Hilbert for a new graphic line. 140 | - The original processes run at Core0, every 100ms. 141 | - There is a digital low pass filter FIR implemented at the code (like the original) that will give the passband we want for audio. 142 | This filter was calculated with the help of this site: http://t-filter.engineerjs.com/ 143 | The dificulty is that the number of filter taps can not be high (there is no much time to process it), so the filter must be chosen carefully. 144 | - Please consider that this Waterfall is not perfect, I had to let go of some rules to make it. 145 | - Software Block diagram at "Arduino_uSDR_Pico_FFT.png": 146 | 147 | ![Block diagram](Arduino_uSDR_Pico_FFT.png) 148 | 149 | 150 | ### ADC Aliasing filter considerations: 151 | **Input:** We sample each signal I, Q and MIC at 160kHz, so it is necessary to have a hardware low pass filter for 80kHz on each input (anti-aliasing filter). If the input filter is set to lower than 80kHz, the Waterfall will show less than +-80kHz of signals. If the input filter is set to higher then 80kHz, the audio and the Waterfall could peek some signals greater than 80kHz and treat them as lower than 80kHz (this is the ADC aliasing problem).
152 | **Output:** We deliver an audio signal at 16kHz sample frequency, so we need a hardware low pass filter for less than 8kHz at the output. The sample frequency will be present and needs to be removed as it is also an audio frequency. 153 | 154 | 155 |
156 |
157 | 158 | ## Operational Notes
159 | ### Keys description
160 | **Normal operation:**
161 | Encoder = to change the frequency at the cursor position
162 | Left key = move the cursor to left
163 | Right key = move the cursor to right
164 | Enter key = We can adjust the Waterfall gain with the Encoder while pressing the Enter key.
165 | Escape key = to enter on menu mode
166 | **Menu mode:**
167 | Escape key = to go back to normal operation
168 | Left key = to move between the menu items
169 | Right key = to move between the menu items
170 | Encoder = to change menu item value
171 | Enter key = to confirm the menu item value
172 |
173 |
174 |
175 | 176 | 177 | 178 | 179 | ## Application Menu Description
180 | 181 | The menu uses the first line on display to show the status and the options.

182 | 183 | ### Menu TUNE:
184 | Menu position used operate the radio on normal condition. It shows the actual values for the menus: Mode, VOX, AGC and Pre.
185 | 186 | ### Menu Mode:
187 | Options: "USB","LSB","AM","CW"
188 | Define the modulation mode used for transmition and reception.
189 | 190 | ### Menu AGC:
191 | Options: "NoAGC","Slow","Fast"
192 | Define the Automatic Gain Control mode of actuation. (It needs improvement)
193 | 194 | ### Menu Pre:
195 | Options: "-30dB","-20dB","-10dB","0dB","+10dB"
196 | Define the use of the attenuators and pre-amplifier on reception. It uses the relay board to switch between the options.
197 | 198 | ### Menu VOX:
199 | Options: "NoVOX","VOX-L","VOX-M","VOX-H"
200 | Defines de use of the VOX feature. (It needs improvement)
201 | 202 | ### Menu Band:
203 | Options: "<2.5","2-6","5-12","10-24","20-40" MHz
204 | Defines the filter used for RX and TX, and consequently the frequency band. It uses the relay board to switch between the options.
205 | 206 | ### Menu Memory:
207 | Options: "Save"
208 | It saves the actual band setup on flash memory, so when we come back to this band after power on, it will start with the setup saved. The last band saved will be the selected band used after power on.
209 | 210 | ### Menu Audio:
211 | Options: "Rec from TX", "Rec from RX", "Play to TX", "Play to Speaker"
212 | It enables to store 10s max. of audio from the RX or TX (= microphone), and also play it to speaker or to TX.
213 | It starts to save/play when pressing < Enter >, and will stop after 10s or when pressing < Escape >.
214 | 215 |
216 |
217 |
218 | 219 | ## Last changes and notes:
220 | 221 | ### Nov25 2024 222 | - Improving the CW Morse Decoder. It is still under test (well... all features are always under test).
223 | 224 | ### Oct28 2024 225 | - Including a CW Morse Decoder. It will try to interpret the CW sound and translate it to letters on display. To enable the decoder you just need to select CW on MODE menu and tune to the highest sound level CW reception. It is still not working properlly, under test (I also notice some issues with the CW filter).
226 | 227 | ![Display SWR Power](Pictures/CwDecoder_display_area.png) 228 |
229 | 230 | ### Oct23 2024 231 | - New changes related to RX S-Meter and TX Power/SWR on display. The TX Power/SWR is valid only when using Arduino Pro Mini to control the filter relays. Arduino Pro Mini allows to measure the Forward and Reverse values from SWR board. The Pico has no extra ADC for this. I included the values read from SWR board on display.
232 | 233 | ![Display SWR Power](Pictures/Display_SWR_Power.png) 234 |
235 | 236 | - There is a
237 | #define I2C_Arduino_Pro_Mini 1 //=1 when I2C BPF and Atten is commanded with Arduino Pro Mini (allow SWR reading)
238 | on relay.h to define if the Arduino Pro Mini is part of the project.
239 | - There is a table swr_pow[] on hmi.cpp to make the correlation from the ADC forward power measured and the value shown on the display.
240 | - There is a table Smeter_table_level[] on hmi.cpp to make the correlation from the ADC I and Q audio level measured and the value shown on the display.
241 | - Corrections on Arduino Pro Mini I2C routines.
242 | - I included here a short description of the menus: "Application Menu Description".
243 | - Just to remember, there is a "Discussions" folder on the Github Project site (look above) to allow comments about the project, as the ucx groups.io forum could not be the right place for it.
244 | (https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/discussions)
245 | 246 | ### Ago20 2024 247 | - It will move the Waterfall in block (all lines) when changing the frequency (instead of moving only the last line received).
248 | 249 | ### Ago03 2024 250 | - S Meter corrections.
251 | 252 | ### Jul29 2024 253 | - S Meter implementation.
254 | Starting tests with a S Meter and Bar Graph.
255 | There is a table (Smeter_table_level[]) to make the correspondence between the audio level and the S meter level.
256 | The values on the code are **not calibrated**. To get the audio level on the display, to make your own calibration, uncomment this line : //#define SMETER_TEST 10 on "hmi.h".
257 | 258 | ### Jan05 2024 259 | - Correcting internal PTT activation from Monitor and Vox.
260 | There are some necessary hardware changes to be made:
261 | Include a 1K resistor between GPIO15 (pin 20) and 3V3 (pin 36).
262 | Increase the value of C44 from 1n to 100n.
263 | Reduce the value of R25 to 10R. 264 | - New functions on menu **to record and play the audio** from reception(RX) or transmission(TX) (max. 10s) 265 | 266 | ### Nov12 2023 267 | - Correcting Arjan's name. 268 | - Switching the PTT pin when TX in VOX mode. 269 | 270 | ### Oct13 2023 271 | - Now, each band has its own setup, including last frequency used. When changing bands, it will remember the last menu options for each band. 272 | - Included new menu option to save the band setup on Data Flash (non volatile memory), including the frequency. The menu Save will save the actual band and frequency to DFlash. The last band saved will be the one selected after power on. 273 | - Changed the transceiver name to ARJAN-5 (I hope everyone agrees... the name uSDR Pico brings to misunderstandings in my opinion). 274 | 275 | ### Jul19 2023 276 | - Changing uSDR_Pico_BPF_RX_SCH.pdf and PCB due to Relay HFD3 characteristics. 277 | 278 | ![PCB assembly top](HFD3_characteristics.png) 279 | 280 | - Changing the components value on schematic to be the same as my assembly. 281 | 282 | - Including my Kicad PCBs layout as ZIP file (I can not give much support on this, use at your risk). 283 | 284 | 285 | ### Jul16 2023 286 | - Changing the 16kHz low pass filter from average sum of samples to a strong lp FIR. Due to I could hear strong stations away multiples of 16kHz. 287 | - Including the Band selection on screen in blue (ex. B0 = Band 0). 288 | - Adjusting the position on screen for receive level and manual gain (ex. R30x12). 289 | Obs.: to use manual gain adjust, press Enter button and turn the frequncy knob. 290 | 291 | ### Jun2 2023 292 | - Testing an 80KHz Low Pass Filter for I and Q inputs because I can hear some frequency distant strong stations. 293 | - New schematics and PCB for LPF and RX at the same board. 294 | - Added software for Arduino Pro Mini to work as two I2C slaves, replacing the PCF8574's. 295 | - There are some mirror signal at the Waterfall, I am looking for the reason. 296 | - I really like the quality of the audio received. 297 | 298 | ### Apr30 2023 299 | - Included an adjust to the gain in the Waterfall, it helps to reduce the noise in the Waterfall. Press and keep the Enter button, and turn the frequency knob. This adjust also affects the audio AGC. 300 | 301 | ### Apr29 2023 302 | - Minor changes, improve comments at .ino file. Trying to get the new PCB running and getting back to software improvement. 303 | 304 | ### Ago17 2022 305 | - Included sketch folder uSDX_TX to test the Phase and Amplitude TX method. 306 | 307 | ### Ago07 2022 308 | - PTT input corrected. Now it uses the falling or rising to turn off and on the PTT. 309 | - Including options for encoder at hmi.cpp: ENCODER_TYPE ENCODER_DIRECTION 310 | 311 | ![Encoder type](Encoder_selection.png) 312 | 313 | 314 | ### Ago05 2022 315 | - Now the frequency changes at each encoder step (I am using EC11 encoder and it changed the frequency at every second step) 316 | - Plot to the Waterfall improved to spend less time 317 | - Included separate audio filters for CW and AM 318 | - Included side tone for CW TX 319 | - Writing to display and programming Si5351 only when necessary (finally) 320 | - PTT debounce reduced to allow CW TX (** it needs a 100nF capacitor from PTT pin to ground **) 321 | 322 | 323 | ![Hardware Modification](FFT_LCD_pico_MOD2.png) 324 |
325 | 326 | 327 | ### Jul20 2022 328 | - Waterfall: Changed to fall instead of going up 329 | - Waterfall: Frequency scale moving with main frequency 330 | - Waterfall: Shadow indicating the reception zone 331 | - AGC attack faster 332 | - It shows the reception signal level implemented from audio output and gain 333 | 334 | ### Jun24 2022 335 | - Few display corrections: central triangle, mode text overwriting. 336 | 337 | ### Jun13 2022 338 | - 3V3 noise sensitivity test:
339 | I followed the tips in this post 340 | https://forums.raspberrypi.com/viewtopic.php?t=330208
341 | It says that a power supply with less noise to Vref is the best for reducing the ADC noise.
342 | I used a 3V3 linear regulator instead of the Pico on board DC-DC 3v3.
343 | I connected it externally, in pins VSYS, GND, 3v3_EN and 3V3.
344 | Connecting 3v3_EN to Gnd disables the on board DC-DC 3v3.
345 | I used a TPS79933 + caps from VSYS to 3V3 pin.
346 | I can connect and disconnect it easily.
347 | The first impression is that this does not improve the sensibility.
348 | I have found a limit position from RF generator to Softrock receiver to start showing something at the Waterfall. 349 | And both DC-DC and TPS look the same to me. 350 | 351 | ![Reg 3v3](VREF_TPS79933_AXX.png) 352 | 353 | ![Reg mounted](Reg_monted.jpg) 354 | 355 | 356 | ### Jun10 2022 357 | - AGC uncommented and adapted to work.
358 | The AGC is only used at the output audio, not for Waterfall. 359 | - A visual scope was implemented to allow visualization of some internal variables.
360 | The variables plotted are:
361 | - I, Q and MIC = ADC inputs
362 | - A = output audio
363 | - PEAK = average of absolute(A)
364 | This gives an input signal level for AGC (min in the middle of the scope height, max at the top)
365 | - GAIN = AGC result.
366 | If the PEAK is high for some time, it decreases the GAIN. With PEAK low, increases GAIN.
367 | Gain has 32 steps:
368 | 1 = min AGC gain (almost in the middle of the scope height)
369 | 32 = max AGC gain (limited to 25 at the top of scope)
370 | - Obs.: Scope limits: -25 < y < 25 0 < x < 100 (each 'x' dot time = 1/16kHz)
371 | - Obs.: Variables are scaled to fit in the scope height. 372 | - The signal level meter at the display does not change because it is fixed at the original code (the level depends on the software as well as the hardware).
373 | 374 | 375 | ## Wish list: 376 | - Include RIT (receiver incremental tuning) 377 | - Reduce the minimum step to change the frequency to 50Hz or less 378 | - Tests: reception/transmission modes, bands, menus, ... 379 | 380 | 381 | ## Copyright notice 382 | **The code and electronic designs as well as the implementations presented in this repository can be copied and modified freely, for non-commercial use. 383 | Use for commercial purposes is allowed as well, as long as a reference to this repository is included in the product.** 384 | 385 | 386 | -------------------------------------------------------------------------------- /Reg_monted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Reg_monted.jpg -------------------------------------------------------------------------------- /Senos FFT.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/Senos FFT.xlsx -------------------------------------------------------------------------------- /VREF_TPS79933_AXX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/VREF_TPS79933_AXX.png -------------------------------------------------------------------------------- /leitura fft samp.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj/bbbdcf4d1e6d82350a3d2fccf05ca8cfe45bc4e2/leitura fft samp.xlsx -------------------------------------------------------------------------------- /uSDX_TX/uSDX_I2C.cpp: -------------------------------------------------------------------------------- 1 | // Based on QCX-SSB.ino - https://github.com/threeme3/QCX-SSB 2 | // 3 | // Copyright 2019, 2020, 2021 Guido PE1NNZ 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 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. 6 | // 7 | // Adapted by: Klaus Fensterseifer PY2KLA 8 | // https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 9 | 10 | 11 | #include "Arduino.h" 12 | #include "uSDX_I2C.h" 13 | #include "uSDX_SI5351.h" 14 | #include "uSDX_TX_PhaseAmpl.h" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | // I2C communication starts with a START condition, multiple single byte-transfers (MSB first) followed by an ACK/NACK and stops with a STOP condition; 23 | // during data-transfer SDA may only change when SCL is LOW, during a START/STOP condition SCL is HIGH and SDA goes DOWN for a START and UP for a STOP. 24 | // https://www.ti.com/lit/an/slva704/slva704.pdf 25 | //*********************************************************************** 26 | // 27 | // class I2C 28 | // 29 | //*********************************************************************** 30 | 31 | 32 | //*********************************************************************** 33 | I2C::I2C(){ 34 | gpio_init_mask(1< 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 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. 6 | // 7 | // Adapted by: Klaus Fensterseifer PY2KLA 8 | // https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 9 | 10 | 11 | #include "Arduino.h" 12 | #include "uSDX_I2C.h" 13 | #include "uSDX_SI5351.h" 14 | #include "uSDX_TX_PhaseAmpl.h" 15 | 16 | 17 | 18 | 19 | 20 | #define _MSC 0x10000 21 | 22 | 23 | #define BB0(x) ((uint8_t)(x)) // Bash byte x of int32_t 24 | #define BB1(x) ((uint8_t)((x)>>8)) 25 | #define BB2(x) ((uint8_t)((x)>>16)) 26 | 27 | 28 | //int32_t vdf[20]; 29 | //int16_t ndf = 0; 30 | 31 | 32 | //*********************************************************************** 33 | // 34 | // class SI5351 35 | // 36 | //*********************************************************************** 37 | 38 | 39 | //*********************************************************************** 40 | void FAST SI5351::freq_calc_fast(int16_t df) // note: relies on cached variables: _msb128, _msa128min512, _div, _fout, fxtal 41 | { 42 | uint32_t msb128 = _msb128 + ((int64_t)(_div * (int32_t)df) * _MSC * 128) / fxtal; 43 | // uint32_t msb128 = _msb128 + (((int64_t)(_div * (int32_t)df) * _MSC) * 128L) / fxtal; 44 | //uint32_t msb128 = (((int64_t)(_div * (int32_t)df) * (int64_t)_MSC) * (int64_t)128) / (int64_t)fxtal; 45 | //vdf[ndf&0x0f] = msb128; ndf++; ndf = ndf&0x000f; 46 | //msb128 += _msb128; 47 | /* 48 | vdf[0] = _msb128; 49 | vdf[1] = _div; 50 | vdf[2] = df; 51 | vdf[3] = _MSC; 52 | vdf[4] = fxtal; 53 | vdf[5] = 0; 54 | vdf[6] = msb128; 55 | vdf[7] = 0; 56 | vdf[8] = 0; 57 | vdf[9] = 0; 58 | vdf[10] = 0; 59 | */ 60 | 61 | 62 | uint16_t msp1 = _msa128min512 + msb128 / _MSC; // = 128 * _msa + msb128 / _MSC - 512; 63 | uint16_t msp2 = msb128; // = msb128 % _MSC; assuming MSC is covering exact uint16_t so the mod operation can dissapear (and the upper BB2 byte) // = msb128 - msb128/_MSC * _MSC; 64 | 65 | //pll_regs[0] = BB1(msc); // 3 regs are constant 66 | //pll_regs[1] = BB0(msc); 67 | //pll_regs[2] = BB2(msp1); 68 | //pll_regs[3] = BB1(msp1); 69 | pll_regs[4] = BB0(msp1); 70 | pll_regs[5] = ((_MSC&0xF0000)>>(16-4))/*|BB2(msp2)*/; // top nibble MUST be same as top nibble of _MSC ! assuming that BB2(msp2) is always 0 -> so reg is constant 71 | pll_regs[6] = BB1(msp2); 72 | pll_regs[7] = BB0(msp2); 73 | } 74 | 75 | //*********************************************************************** 76 | void SI5351::SendPLLRegisterBulk(){ 77 | i2c.start(); 78 | i2c.SendByte(SI5351_ADDR << 1); 79 | i2c.SendByte(26+0*8 + 4); // Write to PLLA 80 | //i2c.SendByte(26+1*8 + 4); // Write to PLLB 81 | i2c.SendByte(pll_regs[4]); 82 | i2c.SendByte(pll_regs[5]); 83 | i2c.SendByte(pll_regs[6]); 84 | i2c.SendByte(pll_regs[7]); 85 | i2c.stop(); 86 | } 87 | 88 | 89 | //*********************************************************************** 90 | void SI5351::SendRegister(uint8_t reg, uint8_t* data, uint8_t n){ 91 | i2c.start(); 92 | i2c.SendByte(SI5351_ADDR << 1); 93 | i2c.SendByte(reg); 94 | while (n--) i2c.SendByte(*data++); 95 | i2c.stop(); 96 | } 97 | //*********************************************************************** 98 | void SI5351::SendRegister(uint8_t reg, uint8_t val){ SendRegister(reg, &val, 1); } 99 | 100 | 101 | //*********************************************************************** 102 | // void SI5351::ms(int8_t n, uint32_t div_nom, uint32_t div_denom, uint8_t pll = PLLA, uint8_t _int = 0, uint16_t phase = 0, uint8_t rdiv = 0){ 103 | void SI5351::ms(int8_t n, uint32_t div_nom, uint32_t div_denom, uint8_t pll, uint8_t _int, uint16_t phase, uint8_t rdiv){ 104 | uint16_t msa; uint32_t msb, msc, msp1, msp2, msp3; 105 | msa = div_nom / div_denom; // integer part: msa must be in range 15..90 for PLL, 8+1/1048575..900 for MS 106 | if(msa == 4) _int = 1; // To satisfy the MSx_INT=1 requirement of AN619, section 4.1.3 which basically says that for MS divider a value of 4 and integer mode must be used 107 | msb = (_int) ? 0 : (((uint64_t)(div_nom % div_denom)*_MSC) / div_denom); // fractional part 108 | msc = (_int) ? 1 : _MSC; 109 | //lcd.setCursor(0, 0); lcd.print(n); lcd.print(":"); lcd.print(msa); lcd.print(" "); lcd.print(msb); lcd.print(" "); lcd.print(msc); lcd.print(F(" ")); delay(500); 110 | msp1 = 128*msa + 128*msb/msc - 512; 111 | msp2 = 128*msb - 128*msb/msc * msc; 112 | msp3 = msc; 113 | uint8_t ms_reg2 = BB2(msp1) | (rdiv<<4) | ((msa == 4)*0x0C); 114 | uint8_t ms_regs[8] = { BB1(msp3), BB0(msp3), ms_reg2, BB1(msp1), BB0(msp1), BB2(((msp3 & 0x0F0000)<<4) | msp2), BB1(msp2), BB0(msp2) }; 115 | 116 | SendRegister(n*8+42, ms_regs, 8); // Write to MSx 117 | if(n < 0){ 118 | SendRegister(n+16+8, 0x80|(0x40*_int)); // MSNx PLLn: 0x40=FBA_INT; 0x80=CLKn_PDN 119 | } else { 120 | //SendRegister(n+16, ((pll)*0x20)|0x0C|0|(0x40*_int)); // MSx CLKn: 0x0C=PLLA,0x2C=PLLB local msynth; 0=2mA; 0x40=MSx_INT; 0x80=CLKx_PDN 121 | SendRegister(n+16, ((pll)*0x20)|0x0C|3|(0x40*_int)); // MSx CLKn: 0x0C=PLLA,0x2C=PLLB local msynth; 3=8mA; 0x40=MSx_INT; 0x80=CLKx_PDN 122 | SendRegister(n+165, (!_int) * phase * msa / 90); // when using: make sure to configure MS in fractional-mode, perform reset afterwards 123 | } 124 | } 125 | 126 | //*********************************************************************** 127 | void SI5351::phase(int8_t n, uint32_t div_nom, uint32_t div_denom, uint16_t phase){ SendRegister(n+165, phase * (div_nom / div_denom) / 90); } // when using: make sure to configure MS in fractional-mode!, perform reset afterwards 128 | 129 | //*********************************************************************** 130 | void SI5351::reset(){ SendRegister(177, 0xA0); } // 0x20 reset PLLA; 0x80 reset PLLB 131 | 132 | //*********************************************************************** 133 | void SI5351::oe(uint8_t mask){ SendRegister(3, ~mask); } // output-enable mask: CLK2=4; CLK1=2; CLK0=1 134 | 135 | //*********************************************************************** 136 | void SI5351::freq(int32_t fout, uint16_t i, uint16_t q){ // Set a CLK0,1,2 to fout Hz with phase i, q (on PLLA) 137 | uint8_t rdiv = 0; // CLK pin sees fout/(2^rdiv) 138 | if(fout > 300000000){ i/=3; q/=3; fout/=3; } // for higher freqs, use 3rd harmonic 139 | if(fout < 500000){ rdiv = 7; fout *= 128; } // Divide by 128 for fout 4..500kHz 140 | uint16_t d; if(fout < 30000000) d = (16 * fxtal) / fout; else d = (32 * fxtal) / fout; // Integer part .. maybe 44? 141 | if(fout < 3500000) d = (7 * fxtal) / fout; // PLL at 189MHz to cover 160m (freq>1.48MHz) when using 27MHz crystal 142 | if(fout > 140000000) d = 4; // for f=140..300MHz; AN619; 4.1.3, this implies integer mode 143 | if(d % 2) d++; // even numbers preferred for divider (AN619 p.4 and p.6) 144 | if( (d * (fout - 5000) / fxtal) != (d * (fout + 5000) / fxtal) ) d += 2; // Test if multiplier remains same for freq deviation +/- 5kHz, if not use different divider to make same 145 | uint32_t fvcoa = d * fout; // Variable PLLA VCO frequency at integer multiple of fout at around 27MHz*16 = 432MHz 146 | // si5351 spectral purity considerations: https://groups.io/g/QRPLabs/message/42662 147 | 148 | ms(MSNA, fvcoa, fxtal); // PLLA in fractional mode 149 | //ms(MSNB, fvcoa, fxtal); 150 | ms(MS0, fvcoa, fout, PLLA, 0, i, rdiv); // Multisynth stage with integer divider but in frac mode due to phase setting 151 | ms(MS1, fvcoa, fout, PLLA, 0, q, rdiv); 152 | 153 | ms(MS2, fvcoa, fout, PLLA, 0, 0, rdiv); 154 | 155 | if(iqmsa != (((int8_t)i-(int8_t)q)*((int16_t)(fvcoa/fout))/90)){ iqmsa = ((int8_t)i-(int8_t)q)*((int16_t)(fvcoa/fout))/90; reset(); } 156 | oe(0b00000011); // output enable CLK0, CLK1 157 | 158 | _fout = fout; // cache 159 | _div = d; 160 | _msa128min512 = ((fvcoa / fxtal) * 128) - 512; 161 | _msb128=(((uint64_t)(fvcoa % fxtal)*_MSC)*128) / fxtal; 162 | //_mod = fvcoa % fxtal; 163 | } 164 | 165 | //*********************************************************************** 166 | void SI5351::freqb(uint32_t fout){ // Set a CLK2 to fout Hz (on PLLB) 167 | uint16_t d = (16 * fxtal) / fout; 168 | if(d % 2) d++; // even numbers preferred for divider (AN619 p.4 and p.6) 169 | uint32_t fvcoa = d * fout; // Variable PLLA VCO frequency at integer multiple of fout at around 27MHz*16 = 432MHz 170 | 171 | ms(MSNB, fvcoa, fxtal); 172 | ms(MS2, fvcoa, fout, PLLB, 0, 0, 0); 173 | } 174 | 175 | 176 | //*********************************************************************** 177 | uint8_t SI5351::RecvRegister(uint8_t reg){ 178 | i2c.start(); // Data write to set the register address 179 | i2c.SendByte(SI5351_ADDR << 1); 180 | i2c.SendByte(reg); 181 | i2c.stop(); 182 | i2c.start(); // Data read to retrieve the data from the set address 183 | i2c.SendByte((SI5351_ADDR << 1) | 1); 184 | uint8_t data = i2c.RecvByte(true); 185 | i2c.stop(); 186 | return data; 187 | } 188 | 189 | //*********************************************************************** 190 | void SI5351::powerDown(){ 191 | SendRegister(3, 0b11111111); // Disable all CLK outputs 192 | SendRegister(24, 0b00010000); // Disable state: CLK2 HIGH state, CLK0 & CLK1 LOW state when disabled; CLK2 needs to be in HIGH state to make sure that cap to gate is already charged, preventing "exponential pulse is caused by CLK2, which had been at 0v whilst it was disabled, suddenly generating a 5vpp waveform, which is “added to” the 0v filtered PWM output and causing the output fets to be driven with the full 5v pp.", see: https://forum.dl2man.de/viewtopic.php?t=146&p=1307#p1307 193 | SendRegister(25, 0b00000000); // Disable state: LOW state when disabled 194 | for(int addr = 16; addr != 24; addr++) SendRegister(addr, 0b10000000); // Conserve power when output is disabled 195 | SendRegister(187, 0); // Disable fanout (power-safe) 196 | // To initialise things as they should: 197 | SendRegister(149, 0); // Disable spread spectrum enable 198 | SendRegister(183, 0b11010010); // Internal CL = 10 pF (default) 199 | } 200 | -------------------------------------------------------------------------------- /uSDX_TX/uSDX_SI5351.h: -------------------------------------------------------------------------------- 1 | #ifndef __USDX_SI5351_H__ 2 | #define __USDX_SI5351_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | 9 | 10 | #define SI5351_ADDR 0x60 // SI5351A I2C address: 0x60 for SI5351A-B-GT, Si5351A-B04771-GT, MS5351M; 0x62 for SI5351A-B-04486-GT; 0x6F for SI5351A-B02075-GT; see here for other variants: https://www.silabs.com/TimingUtility/timing-download-document.aspx?OPN=Si5351A-B02075-GT&OPNRevision=0&FileType=PublicAddendum 11 | //#define F_XTAL 27005000UL // 27MHz SI5351 crystal 12 | #define F_XTAL 25005000UL // 25MHz SI5351 crystal 13 | 14 | 15 | //TX CLK2 16 | #define TX1RX0 0b11111011 17 | #define TX1RX1 0b11111000 18 | #define TX0RX1 0b11111100 19 | #define TX0RX0 0b11111111 20 | 21 | 22 | #define FAST __attribute__((optimize("Ofast"))) 23 | enum ms_t { PLLA=0, PLLB=1, MSNA=-2, MSNB=-1, MS0=0, MS1=1, MS2=2, MS3=3, MS4=4, MS5=5 }; 24 | #define SI_CLK_OE 3 25 | 26 | 27 | 28 | 29 | //*********************************************************************** 30 | // 31 | // 32 | //*********************************************************************** 33 | class SI5351 { 34 | public: 35 | volatile int32_t _fout; 36 | volatile uint8_t _div; // note: uint8_t asserts fout > 3.5MHz with R_DIV=1 37 | volatile uint16_t _msa128min512; 38 | volatile uint32_t _msb128; 39 | volatile uint8_t pll_regs[8]; 40 | volatile uint32_t fxtal = F_XTAL; 41 | int16_t iqmsa; // to detect a need for a PLL reset 42 | //I2C i2c; 43 | 44 | //*********************************************************************** 45 | void FAST freq_calc_fast(int16_t df); // note: relies on cached variables: _msb128, _msa128min512, _div, _fout, fxtal 46 | 47 | 48 | //*********************************************************************** 49 | void SendPLLRegisterBulk(); 50 | 51 | 52 | //*********************************************************************** 53 | void SendRegister(uint8_t reg, uint8_t* data, uint8_t n); 54 | 55 | //*********************************************************************** 56 | void SendRegister(uint8_t reg, uint8_t val); 57 | 58 | 59 | 60 | //*********************************************************************** 61 | void ms(int8_t n, uint32_t div_nom, uint32_t div_denom, uint8_t pll = PLLA, uint8_t _int = 0, uint16_t phase = 0, uint8_t rdiv = 0); 62 | 63 | 64 | //*********************************************************************** 65 | void phase(int8_t n, uint32_t div_nom, uint32_t div_denom, uint16_t phase); 66 | 67 | //*********************************************************************** 68 | void reset(); 69 | 70 | //*********************************************************************** 71 | void oe(uint8_t mask); 72 | 73 | //*********************************************************************** 74 | void freq(int32_t fout, uint16_t i, uint16_t q); 75 | 76 | //*********************************************************************** 77 | void freqb(uint32_t fout); 78 | 79 | 80 | //*********************************************************************** 81 | uint8_t RecvRegister(uint8_t reg); 82 | 83 | //*********************************************************************** 84 | void powerDown(); 85 | 86 | }; 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /uSDX_TX/uSDX_TX.ino: -------------------------------------------------------------------------------- 1 | // Based on QCX-SSB.ino - https://github.com/threeme3/QCX-SSB 2 | // 3 | // Copyright 2019, 2020, 2021 Guido PE1NNZ 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 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. 6 | // 7 | // Adapted by: Klaus Fensterseifer PY2KLA 8 | // https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 9 | 10 | 11 | 12 | // 13 | // uSDX_TX.ino 14 | // 15 | // Arduino sketch used to test Phase and Amplitude RF transmission method running at PICO (preparing to be included at uSDR_PICO_FFT project) 16 | // It uses the TX code from uSDX Guido's project converted to run in RPI PICO in uSDX_PICO_FFT project https://github.com/kaefe64/Arduino_uSDX_Pico_FFT_Proj 17 | // 18 | // - TX code from uSDX version "1.02x" 19 | // - same Arduino IDE and library setup as uSDX_PICO_FFT 20 | // - same uSDX_PICO_FFT connections: 21 | // Input: Microphone at GP28 = MIC 22 | // Input: PTT TX = LOW at GP15 = PTT 23 | // Output: Phase at Si5351 CLK2 24 | // Output: Amplitude at GP21 = I TX 25 | // Output: CW Side Tone at GP22 = Audio 26 | // - it needs the 1K pullup change on SCL SDA I2C in Si5351 board, similar to "Modifying SI 5351 Module:" at https://antrak.org.tr/blog/usdx-a-compact-sota-ssb-sdr-transceiver-with-arduino/ 27 | // - it needs to be connected by USB to PC with the Serial Monitor running 28 | // - it just tests the transmission, no display, no reception. 29 | // 30 | // Consider that there are some uSDX Guido's comments mixed with mine for PICO. 31 | // 32 | 33 | 34 | #include "uSDX_I2C.h" 35 | #include "uSDX_SI5351.h" 36 | #include "uSDX_TX_PhaseAmpl.h" 37 | 38 | 39 | 40 | 41 | //*********************************************************************** 42 | // 43 | // 44 | //*********************************************************************** 45 | void setup() 46 | { 47 | gpio_init_mask(1< Serialx = Serial1 //UART0 /dev/ttyUSB0 51 | Serial.begin(115200); 52 | 53 | 54 | 55 | uint16_t tim = millis(); 56 | 57 | // some delay required for Serial1 too 58 | for(int i=0; i<60; i++) //waits 6s to connect to serial 59 | { 60 | //digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 61 | gpio_set_mask(1<