├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bin └── pico-hf-oscillator-USBconsole.uf2 ├── build.sh ├── clean.sh ├── conswrapper.c ├── debug ├── logutils.c └── logutils.h ├── defines.h ├── doc ├── NEO-6_DataSheet_(GPS.G6-HW-09005).pdf ├── Raspberry-Pi-Pico-Pinout.png ├── pico-hf-oscillator.pdf ├── pico-hf-oscillator.png └── u-blox6-GPS-GLONASS-QZSS-V14_ReceiverDescrProtSpec_(GPS.G6-SW-12013)_Public.pdf ├── gpstime ├── GPStime.c └── GPStime.h ├── hfconsole ├── hfconsole.c └── hfconsole.h ├── hwdefs.h ├── lib ├── assert.c ├── assert.h ├── thirdparty │ ├── strnstr.c │ └── strnstr.h └── utility.h ├── pico_sdk_import.cmake ├── piodco ├── dco2.pio ├── piodco.c └── piodco.h ├── protos.h └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_BUILD_TYPE "Release") 6 | 7 | set(CMAKE_C_STANDARD 11) 8 | # set(CMAKE_CXX_STANDARD 17) 9 | 10 | # Initialise pico_sdk from installed location 11 | # (note this can come from environment, CMake cache etc) 12 | set(PICO_BOARD pico CACHE STRING "Board type") 13 | 14 | # Pull in Raspberry Pi Pico SDK (must be before project) 15 | include(pico_sdk_import.cmake) 16 | 17 | if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") 18 | message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") 19 | endif() 20 | 21 | project(pico-hf-oscillator-test C CXX ASM) 22 | 23 | # Initialise the Raspberry Pi Pico SDK 24 | pico_sdk_init() 25 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Og") 26 | add_executable(pico-hf-oscillator-test) 27 | 28 | pico_generate_pio_header(pico-hf-oscillator-test ${CMAKE_CURRENT_LIST_DIR}/piodco/dco2.pio) 29 | 30 | target_sources(pico-hf-oscillator-test PUBLIC 31 | ${CMAKE_CURRENT_LIST_DIR}/lib/assert.c 32 | ${CMAKE_CURRENT_LIST_DIR}/piodco/piodco.c 33 | ${CMAKE_CURRENT_LIST_DIR}/gpstime/GPStime.c 34 | ${CMAKE_CURRENT_LIST_DIR}/debug/logutils.c 35 | ${CMAKE_CURRENT_LIST_DIR}/test.c 36 | ${CMAKE_CURRENT_LIST_DIR}/conswrapper.c 37 | ${CMAKE_CURRENT_LIST_DIR}/hfconsole/hfconsole.c 38 | ) 39 | 40 | pico_set_program_name(pico-hf-oscillator-test "pico-hf-oscillator-test") 41 | pico_set_program_version(pico-hf-oscillator-test "0.9") 42 | 43 | pico_enable_stdio_uart(pico-hf-oscillator-test 0) 44 | pico_enable_stdio_usb(pico-hf-oscillator-test 1) 45 | 46 | # Add the standard include files to the build 47 | target_include_directories(pico-hf-oscillator-test PRIVATE 48 | ${CMAKE_CURRENT_LIST_DIR} 49 | ${CMAKE_CURRENT_LIST_DIR}/gpstime 50 | ${CMAKE_CURRENT_LIST_DIR}/hfconsole 51 | ${CMAKE_CURRENT_LIST_DIR}/.. 52 | ) 53 | 54 | add_compile_options(-Wall) 55 | 56 | # Add any user requested libraries 57 | target_link_libraries( 58 | pico-hf-oscillator-test 59 | pico_stdlib 60 | pico_sync 61 | pico_multicore 62 | hardware_timer 63 | hardware_clocks 64 | hardware_pio 65 | hardware_uart 66 | ) 67 | 68 | pico_add_extra_outputs(pico-hf-oscillator-test) 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Roman Piksaykin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Digital controlled radio frequency oscillator for Raspberry Pi Pico 2 | 3 | The library for Raspberry Pi Pico includes the headers and source code and all 4 | necessary build files to build a custom application which turns pico into 5 | precise PLL digital frequency oscillator of the whole of HF radio spectrum 6 | (1 Hz to 32.333 MHz) with millihertz resolution. 7 | 8 | # Precise frequency resolution 9 | The library provides about 23 milli-Hz frequency resolution. This resolution is limited by 24-bit register which is used in algorithm. 10 | The working WSPR beacon which has been built on the base of this project proves that the quality of generated signal is sufficient to such precise (~1.46 Hz step) frequency manipulation digital modes. 11 | The upper freq. limit is ~32.333 MHz and it is achieved only using Pico overclocking to 270MHz. 12 | 13 | ![mfsk-spectra](https://github.com/RPiks/pico-hf-oscillator/assets/47501785/a8309813-8e77-407e-abfc-58cbd262c35c) 14 | 15 | Here is an example of narrowband FSK (9.4 MHz carrier, 5 Hz step, 20 Hz range in total). 16 | 17 | # Phased locked loop in C 18 | The DCO uses phase locked loop principle programmed in C. 19 | 20 | # *NO* additional hardware 21 | The DCO provides the output signal on the GPIO pin. However if you want to 22 | transmit the signal, you should calculate and provide a lowpass filter of 23 | appropriate frequency. Please also figure out whether you possess rights 24 | to emit radio frequency energy on desired frequency. 25 | 26 | # GPS reference frequency correction (optional) since v.0.9 27 | GPS reference frequency correction option provides an absolute frequency error within about ~1Hz in long term. 28 | 29 | ![pico-hf-oscillator](https://github.com/RPiks/pico-hf-oscillator/assets/47501785/06700e39-6b5f-4a6a-828a-d1cfdd9418ce) 30 | 31 | # Dual-core 32 | The DCO uses extensively the secodary core of the pico. The first one is for 33 | your ideas how to modulate the DCO to obtain a desired signal. 34 | The DCO does *NOT* use any floating point operations - all time-critical 35 | instructions run in 1 CPU cycle. 36 | 37 | # Radio transmitters 38 | Owing to the meager frequency step, it is possible to use 3, 5, or 7th harmonics 39 | of generated frequency. The practical resolution will be quite the same - far 40 | below 1 Hz. Such solution completely cover all HF and VHF band up to ~233 MHz. 41 | 42 | # Console 43 | It is possible to work without compling the sources: load .uf2 to the Pico and start minicom serial console via USB: 44 | minicom -D /dev/ttyACM0 45 | Enter the command 'HELP' in the console. Follow instructions. 46 | Here is an example: https://www.youtube.com/watch?v=np87nLzC88o 47 | 48 | # Tests of RF spectrum quality 49 | Sweep test: https://youtu.be/nYC1VDBiz4o 50 | Pseudorandom MFSK test: https://www.youtube.com/shorts/CEPW8hwlG7k 51 | There is WSPR beacon project based on this oscillator: https://github.com/RPiks/pico-WSPR-tx 52 | ![image](https://github.com/RPiks/pico-hf-oscillator/assets/47501785/fd9b0eac-1900-43d0-bf83-3d95a7cbd87a) 53 | 54 | # For what? 55 | This is an experimental project of amateur radio hobby and it is devised by me 56 | in order to experiment with QRP narrowband digital modes. 57 | I am licensed radio amateur who is keen on experiments in the area of the 58 | digital modes on HF. 59 | My QRZ page is https://www.qrz.com/db/R2BDY 60 | 61 | ![DSC_0269](https://github.com/RPiks/pico-hf-oscillator/assets/47501785/dfa3ae65-5ceb-46cb-a4ec-bf7b5defc5ec) 62 | 63 | # Feedback 64 | I appreciate any thoughts or comments on that matter. 65 | I strongly appreciate if you use this project as a part of your one in accordance with the Licence. 66 | I have plans of building a transceiver on the base of this library. 67 | The WSPR beacon is working and available here: https://github.com/RPiks/pico-WSPR-tx 68 | 69 | # Quick-start 70 | 1. Install Raspberry Pi Pico SDK. Configure environment variables. Test whether 71 | it is built successfully. 72 | 73 | 2. git clone this repository. cd pico-hf-oscillator ; ./build.sh 74 | Check whether output file ./build/pico-hf-oscillator.uf2 appears. 75 | 76 | 3. Prepare the surrogate antenna (if you possess an SSB receiver) or pin-out 77 | for an oscilloscope or a spectrum analyser. The default output pin is GPIO6. 78 | 79 | 4. Load the .uf2 file (2) into the Pico. 80 | 81 | 5. Initialy the running frequency is 29.977777 MHz. 82 | 83 | 6. Set any other frequency up to 32.333333 MHz by #define GEN_FRQ_HZ and build the project. 84 | 85 | 7. Provide the feedback by clicking like (`a star`) on the github page of the project :) 86 | 87 | 88 | Cheers, 89 | Roman Piksaykin, amateur callsign R2BDY 90 | https://www.qrz.com/db/R2BDY 91 | -------------------------------------------------------------------------------- /bin/pico-hf-oscillator-USBconsole.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/bin/pico-hf-oscillator-USBconsole.uf2 -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd build 4 | cmake .. 5 | make 6 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf build/* 4 | 5 | -------------------------------------------------------------------------------- /conswrapper.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // conswrapper.c - Serial console commands processing manager. 10 | // 11 | // DESCRIPTION 12 | // - 13 | // 14 | // PLATFORM 15 | // Raspberry Pi pico. 16 | // 17 | // REVISION HISTORY 18 | // Rev 0.1 23 Dec 2023 Initial revision. 19 | // 20 | // PROJECT PAGE 21 | // https://github.com/RPiks/pico-hf-oscillator 22 | // 23 | // LICENCE 24 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 25 | // 26 | // Copyright (c) 2023 by Roman Piksaykin 27 | // 28 | // Permission is hereby granted, free of charge,to any person obtaining a copy 29 | // of this software and associated documentation files (the Software), to deal 30 | // in the Software without restriction,including without limitation the rights 31 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | // copies of the Software, and to permit persons to whom the Software is 33 | // furnished to do so, subject to the following conditions: 34 | // 35 | // The above copyright notice and this permission notice shall be included in 36 | // all copies or substantial portions of the Software. 37 | // 38 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 43 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | // THE SOFTWARE. 45 | /////////////////////////////////////////////////////////////////////////////// 46 | 47 | #include 48 | #include 49 | #include 50 | #include "hardware/uart.h" 51 | #include "./lib/assert.h" 52 | #include "piodco/piodco.h" 53 | #include "protos.h" 54 | 55 | extern PioDco DCO; 56 | 57 | /// @brief Console commands manager. Currently available: 58 | /// @brief HELP - Usage. 59 | /// @brief SETFREQ f - Set oscillator output frequency in Hz. 60 | /// @brief SWITCH ON/OFF - Switch output to ON or OFF state. 61 | /// @param cmd Ptr to command. 62 | /// @param narg Argument count. 63 | /// @param params Command params, full string. 64 | void ConsoleCommandsWrapper(char *cmd, int narg, char *params) 65 | { 66 | assert_(cmd); 67 | 68 | if(strstr(cmd, "HELP")) 69 | { 70 | printf("\n"); 71 | printf("Pico-hf-oscillator project HELP page\n"); 72 | printf("Copyright (c) 2023 by Roman Piksaykin\n"); 73 | printf("Build date: %s %s\n",__DATE__, __TIME__); 74 | printf("Project official page: github.com/RPiks/pico-hf-oscillator\n"); 75 | printf("----------------------------------------------------------\n"); 76 | printf("-\n"); 77 | printf(" HELP - this page.\n"); 78 | printf("-\n"); 79 | printf(" STATUS - print system status.\n"); 80 | printf("-\n"); 81 | printf(" SETFREQ f - set output frequency f in Hz.\n"); 82 | printf(" example: SETFREQ 14074010 - set output frequency to 14.074145 MHz.\n"); 83 | printf("-\n"); 84 | printf(" SWITCH s - enable/disable generation.\n"); 85 | printf(" example: SWITCH ON - enable generation.\n"); 86 | printf("-\n"); 87 | printf(" GPSREC OFF/uart_id,pps_pin,baud - enable/disable GPS receiver connection.\n"); 88 | printf(" example: GPSREC 0,3,9600 - enable GPS receiver connection with UART0 & PPS on gpio3, 9600 baud port speed.\n"); 89 | printf(" example: GPSREC OFF - disable GPS receiver connection.\n"); 90 | return; 91 | } else if(strstr(cmd, "SETFREQ")) 92 | { 93 | if(2 != narg) 94 | { 95 | PushErrorMessage(-1); 96 | return; 97 | } 98 | 99 | const uint32_t ui32frq = atol(params); 100 | if(ui32frq < 1000000L || ui32frq > 32333333) 101 | { 102 | PushErrorMessage(-11); 103 | return; 104 | } 105 | 106 | PioDCOSetFreq(&DCO, ui32frq, 0U); 107 | printf("\nFrequency is set to %lu+.000 Hz", ui32frq); 108 | return; 109 | 110 | } else if(strstr(cmd, "STATUS")) 111 | { 112 | PushStatusMessage(); 113 | return; 114 | } else if(strstr(cmd, "SWITCH")) 115 | { 116 | if(2 != narg) 117 | { 118 | PushErrorMessage(-1); 119 | return; 120 | } 121 | if(strstr(params, "ON")) 122 | { 123 | PioDCOStart(&DCO); 124 | printf("\nOutput is enabled"); 125 | return; 126 | } else if(strstr(params, "OFF")) 127 | { 128 | PioDCOStop(&DCO); 129 | printf("\nOutput is disabled"); 130 | return; 131 | } 132 | } else if(strstr(cmd, "GPSREC")) 133 | { 134 | if(4 == narg) 135 | { 136 | char *p = params; 137 | const uint32_t ui32uart = atol(p); 138 | if(0 != ui32uart && 1 != ui32uart) 139 | { 140 | PushErrorMessage(-12); 141 | return; 142 | } 143 | 144 | p += strlen(p) + 1; 145 | if(p) 146 | { 147 | if(strlen(p)) 148 | { 149 | const uint32_t ui32pps = atol(p); 150 | p += strlen(p) + 1; 151 | const uint32_t ui32baud = atol(p); 152 | //printf("\nbaud=%s\n", p); 153 | sleep_ms(5); 154 | if(DCO._pGPStime) 155 | { 156 | GPStimeDestroy(&DCO._pGPStime); 157 | printf("\nGPS subsystem is disabled."); 158 | } 159 | GPStimeContext *pGPS = GPStimeInit(ui32uart, ui32baud, ui32pps); 160 | assert_(pGPS); 161 | DCO._pGPStime = pGPS; 162 | printf("\nGPS subsystem is set to UART%lu (%lu baud) & PPS pin%lu", ui32uart, ui32baud, ui32pps); 163 | return; 164 | } 165 | } 166 | 167 | PushErrorMessage(-1); 168 | 169 | return; 170 | 171 | } else if(2 == narg) 172 | { 173 | if(DCO._pGPStime) 174 | { 175 | GPStimeDestroy(&DCO._pGPStime); 176 | printf("\nGPS subsystem is disabled."); 177 | } 178 | 179 | return; 180 | } 181 | } 182 | 183 | PushErrorMessage(-13); 184 | } 185 | 186 | void PushErrorMessage(int id) 187 | { 188 | switch(id) 189 | { 190 | case -1: 191 | printf("\nInvalid argument"); 192 | break; 193 | 194 | case -11: 195 | printf("\nInvalid frequency"); 196 | break; 197 | 198 | case -12: 199 | printf("\nInvalid UART id, should be 0 OR 1"); 200 | break; 201 | 202 | case -13: 203 | printf("\nInvalid command"); 204 | break; 205 | 206 | default: 207 | printf("\nUnknown error"); 208 | break; 209 | } 210 | } 211 | 212 | void PushStatusMessage(void) 213 | { 214 | printf("\nPico-hf-oscillator system status\n"); 215 | 216 | printf("Working freq: %lu Hz + %ld milliHz\n", DCO._ui32_frq_hz, DCO._ui32_frq_millihz); 217 | 218 | printf("Output is "); 219 | if(DCO._is_enabled) 220 | { 221 | printf("ENABLED"); 222 | } 223 | else 224 | { 225 | printf("DISABLED"); 226 | } 227 | 228 | if(DCO._pGPStime) 229 | { 230 | printf("\nGPS UART id %d", DCO._pGPStime->_uart_id); 231 | printf("\nGPS UART baud %d", DCO._pGPStime->_uart_baudrate); 232 | printf("\nGPS PPS GPIO pin %d", DCO._pGPStime->_pps_gpio); 233 | printf("\nGPS error count %ld", DCO._pGPStime->_i32_error_count); 234 | printf("\nGPS NAV solution flag %u", DCO._pGPStime->_time_data._u8_is_solution_active); 235 | printf("\nGPS GPRMC receive count %u", DCO._pGPStime->_time_data._u32_nmea_gprmc_count); 236 | printf("\nGPS PPS period %llu", DCO._pGPStime->_time_data._u64_pps_period_1M); 237 | printf("\nGPS frequency correction %lld ppb", DCO._pGPStime->_time_data._i32_freq_shift_ppb); 238 | printf("\nGPS lat %lld deg1e5", DCO._pGPStime->_time_data._i64_lat_100k); 239 | printf("\nGPS lon %lld deg1e5", DCO._pGPStime->_time_data._i64_lon_100k); 240 | } 241 | else 242 | { 243 | printf("\nGPS subsystem hasn't been initialized."); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /debug/logutils.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // logutils.h - A set of utilities for logging/debugging. 10 | // 11 | // DESCRIPTION 12 | // - 13 | // 14 | // HOWTOSTART 15 | // - 16 | // 17 | // PLATFORM 18 | // Raspberry Pi pico. 19 | // 20 | // REVISION HISTORY 21 | // - 22 | // 23 | // PROJECT PAGE 24 | // https://github.com/RPiks/pico-WSPR-tx 25 | // 26 | // LICENCE 27 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 28 | // 29 | // Copyright (c) 2023 by Roman Piksaykin 30 | // 31 | // Permission is hereby granted, free of charge,to any person obtaining a copy 32 | // of this software and associated documentation files (the Software), to deal 33 | // in the Software without restriction,including without limitation the rights 34 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | // copies of the Software, and to permit persons to whom the Software is 36 | // furnished to do so, subject to the following conditions: 37 | // 38 | // The above copyright notice and this permission notice shall be included in 39 | // all copies or substantial portions of the Software. 40 | // 41 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 46 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | // THE SOFTWARE. 48 | /////////////////////////////////////////////////////////////////////////////// 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "hardware/clocks.h" 55 | #include "pico/stdlib.h" 56 | 57 | void StampPrintf(const char* pformat, ...) 58 | { 59 | static uint32_t sTick = 0; 60 | if(!sTick) 61 | { 62 | stdio_init_all(); 63 | } 64 | 65 | uint64_t tm_us = to_us_since_boot(get_absolute_time()); 66 | 67 | const uint32_t tm_day = (uint32_t)(tm_us / 86400000000ULL); 68 | tm_us -= (uint64_t)tm_day * 86400000000ULL; 69 | 70 | const uint32_t tm_hour = (uint32_t)(tm_us / 3600000000ULL); 71 | tm_us -= (uint64_t)tm_hour * 3600000000ULL; 72 | 73 | const uint32_t tm_min = (uint32_t)(tm_us / 60000000ULL); 74 | tm_us -= (uint64_t)tm_min * 60000000ULL; 75 | 76 | const uint32_t tm_sec = (uint32_t)(tm_us / 1000000ULL); 77 | tm_us -= (uint64_t)tm_sec * 1000000ULL; 78 | 79 | printf("%02lud%02lu:%02lu:%02lu.%06llu [%04lu] ", tm_day, tm_hour, tm_min, tm_sec, tm_us, sTick++); 80 | 81 | va_list argptr; 82 | va_start(argptr, pformat); 83 | vprintf(pformat, argptr); 84 | va_end(argptr); 85 | 86 | printf("\n"); 87 | } 88 | -------------------------------------------------------------------------------- /debug/logutils.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGUTILS_H_ 2 | #define LOGUTILS_H_ 3 | 4 | void StampPrintf(const char* pformat, ...); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /defines.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // defines.h - Macro definitions of the project. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // . 15 | // 16 | // PLATFORM 17 | // Raspberry Pi pico. 18 | // 19 | // REVISION HISTORY 20 | // 21 | // Rev 0.1 05 Nov 2023 Initial release 22 | // Rev 0.2 18 Nov 2023 23 | // 24 | // PROJECT PAGE 25 | // https://github.com/RPiks/pico-hf-oscillator 26 | // 27 | // LICENCE 28 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 29 | // 30 | // Copyright (c) 2023 by Roman Piksaykin 31 | // 32 | // Permission is hereby granted, free of charge,to any person obtaining a copy 33 | // of this software and associated documentation files (the Software), to deal 34 | // in the Software without restriction,including without limitation the rights 35 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | // copies of the Software, and to permit persons to whom the Software is 37 | // furnished to do so, subject to the following conditions: 38 | // 39 | // The above copyright notice and this permission notice shall be included in 40 | // all copies or substantial portions of the Software. 41 | // 42 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 47 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | // THE SOFTWARE. 49 | /////////////////////////////////////////////////////////////////////////////// 50 | #ifndef DEFINES_H 51 | #define DEFINES_H 52 | 53 | #define DEBUGLOG 1 54 | 55 | #define FALSE 0 /* Something is false. */ 56 | #define TRUE 1 /* Something is true. */ 57 | #define BAD 0 /* Something is bad. */ 58 | #define GOOD 1 /* Something is good. */ 59 | #define INVALID 0 /* Something is invalid. */ 60 | #define VALID 1 /* Something is valid. */ 61 | #define NO 0 /* The answer is no. */ 62 | #define YES 1 /* The answer is yes. */ 63 | #define OFF 0 /* Turn something off. */ 64 | #define ON 1 /* Turn something on. */ 65 | 66 | #define RAM __not_in_flash_func /* Place time-critical func in RAM */ 67 | #define RAM_A __not_in_flash("A") /* Place time-critical var in RAM */ 68 | 69 | /* A macro for arithmetic right shifts, with casting of the argument. */ 70 | #define iSAR32(arg, rcount) (((int32_t)(arg)) >> (rcount)) 71 | #define iSAR64(arg, rcount) (((int64_t)(arg)) >> (rcount)) 72 | 73 | /* A macro of multiplication guarantees of doing so using 1 ASM command. */ 74 | #define iMUL32ASM(a, b) __mul_instruction((int32_t)(a), (int32_t)(b)) 75 | 76 | /* Performing the square by ASM. */ 77 | #define iSquare32ASM(x) (iMUL32ASM((x), (x))) 78 | 79 | #define ABS(x) ((x) > 0 ? (x) : -(x)) 80 | 81 | #define INVERSE(x) ((x) = -(x)) 82 | 83 | #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /doc/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/doc/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf -------------------------------------------------------------------------------- /doc/Raspberry-Pi-Pico-Pinout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/doc/Raspberry-Pi-Pico-Pinout.png -------------------------------------------------------------------------------- /doc/pico-hf-oscillator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/doc/pico-hf-oscillator.pdf -------------------------------------------------------------------------------- /doc/pico-hf-oscillator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/doc/pico-hf-oscillator.png -------------------------------------------------------------------------------- /doc/u-blox6-GPS-GLONASS-QZSS-V14_ReceiverDescrProtSpec_(GPS.G6-SW-12013)_Public.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RPiks/pico-hf-oscillator/1e7652c2d2a73774405453b52b0d01b372d244aa/doc/u-blox6-GPS-GLONASS-QZSS-V14_ReceiverDescrProtSpec_(GPS.G6-SW-12013)_Public.pdf -------------------------------------------------------------------------------- /gpstime/GPStime.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // gpstime.c - GPS time reference utilities for digital controlled radio freq 10 | // oscillator based on Raspberry Pi Pico. 11 | // 12 | // DESCRIPTION 13 | // 14 | // GPS time utilities for pico-hf-oscillator calculate a precise frequency 15 | // shift between the local Pico oscillator and reference oscill. of GPS system. 16 | // The value of the shift is used to correct generated frequency. The practical 17 | // precision of this solution depends on GPS receiver's time pulse stability, 18 | // as well as on quality of navigation solution of GPS receiver. 19 | // This quality can be estimated by GDOP and TDOP parameters received 20 | // in NMEA-0183 message packet from GPS receiver. 21 | // Owing to the meager frequency step in millihertz range, we obtain 22 | // a quasi-analog precision frequency source (if the GPS navigation works OK). 23 | // This is an experimental project of amateur radio class and it is devised 24 | // by me on the free will base in order to experiment with QRP narrowband 25 | // digital modes including extremely ones such as QRSS. 26 | // I appreciate any thoughts or comments on that matter. 27 | // 28 | // PLATFORM 29 | // Raspberry Pi pico. 30 | // 31 | // REVISION HISTORY 32 | // 33 | // Rev 0.1 25 Nov 2023 Initial release 34 | // 35 | // PROJECT PAGE 36 | // https://github.com/RPiks/pico-hf-oscillator 37 | // 38 | // LICENCE 39 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 40 | // 41 | // Copyright (c) 2023 by Roman Piksaykin 42 | // 43 | // Permission is hereby granted, free of charge,to any person obtaining a copy 44 | // of this software and associated documentation files (the Software), to deal 45 | // in the Software without restriction,including without limitation the rights 46 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 47 | // copies of the Software, and to permit persons to whom the Software is 48 | // furnished to do so, subject to the following conditions: 49 | // 50 | // The above copyright notice and this permission notice shall be included in 51 | // all copies or substantial portions of the Software. 52 | // 53 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 54 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 55 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 56 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 57 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 58 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 59 | // THE SOFTWARE. 60 | /////////////////////////////////////////////////////////////////////////////// 61 | #include "GPStime.h" 62 | 63 | static GPStimeContext *spGPStimeContext = NULL; 64 | static GPStimeData *spGPStimeData = NULL; 65 | 66 | /// @brief Initializes GPS time module Context. 67 | /// @param uart_id UART id to which GPS receiver is connected, 0 OR 1. 68 | /// @param uart_baud UART baudrate, 115200 max. 69 | /// @param pps_gpio GPIO pin of PPS (second pulse) from GPS receiver. 70 | /// @return the new GPS time Context. 71 | GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio) 72 | { 73 | ASSERT_(0 == uart_id || 1 == uart_id); 74 | ASSERT_(uart_baud <= 115200); 75 | ASSERT_(pps_gpio < 29); 76 | 77 | // Set up our UART with the required speed & assign pins. 78 | uart_init(uart_id ? uart1 : uart0, uart_baud); 79 | gpio_set_function(uart_id ? 8 : 0, GPIO_FUNC_UART); 80 | gpio_set_function(uart_id ? 9 : 1, GPIO_FUNC_UART); 81 | 82 | GPStimeContext *pgt = calloc(1, sizeof(GPStimeContext)); 83 | ASSERT_(pgt); 84 | 85 | pgt->_uart_id = uart_id; 86 | pgt->_uart_baudrate = uart_baud; 87 | pgt->_pps_gpio = pps_gpio; 88 | 89 | spGPStimeContext = pgt; 90 | spGPStimeData = &pgt->_time_data; 91 | 92 | gpio_init(pps_gpio); 93 | gpio_set_dir(pps_gpio, GPIO_IN); 94 | gpio_set_irq_enabled_with_callback(pps_gpio, GPIO_IRQ_EDGE_RISE, true, &GPStimePPScallback); 95 | 96 | uart_set_hw_flow(uart_id ? uart1 : uart0, false, false); 97 | uart_set_format(uart_id ? uart1 : uart0, 8, 1, UART_PARITY_NONE); 98 | uart_set_fifo_enabled(uart_id ? uart1 : uart0, false); 99 | irq_set_exclusive_handler(uart_id ? UART1_IRQ : UART0_IRQ, GPStimeUartRxIsr); 100 | irq_set_enabled(uart_id ? UART1_IRQ : UART0_IRQ, true); 101 | uart_set_irq_enables(uart_id ? uart1 : uart0, true, false); 102 | 103 | return pgt; 104 | } 105 | 106 | /// @brief Deinits the GPS module and destroys allocated resources. 107 | /// @param pp Ptr to Ptr of the Context. 108 | void GPStimeDestroy(GPStimeContext **pp) 109 | { 110 | ASSERT_(pp); 111 | ASSERT_(*pp); 112 | 113 | spGPStimeContext = NULL; /* Detach global context Ptr. */ 114 | spGPStimeData = NULL; 115 | 116 | uart_deinit((*pp)->_uart_id ? uart1 : uart0); 117 | free(*pp); 118 | *pp = NULL; 119 | } 120 | 121 | /// @brief The PPS interrupt service subroutine. 122 | /// @param gpio The GPIO pin of Pico which is connected to PPS output of GPS rec. 123 | void RAM (GPStimePPScallback)(uint gpio, uint32_t events) 124 | { 125 | const uint64_t tm64 = GetUptime64(); 126 | if(spGPStimeData) 127 | { 128 | spGPStimeData->_u64_sysclk_pps_last = tm64; 129 | ++spGPStimeData->_ix_last; 130 | spGPStimeData->_ix_last %= eSlidingLen; 131 | 132 | const int64_t dt_per_window = tm64 - spGPStimeData->_pu64_sliding_pps_tm[spGPStimeData->_ix_last]; 133 | spGPStimeData->_pu64_sliding_pps_tm[spGPStimeData->_ix_last] = tm64; 134 | 135 | if(ABS(dt_per_window - eCLKperTimeMark * eSlidingLen) < eMaxCLKdevPPM * eSlidingLen) 136 | { 137 | if(spGPStimeData->_u64_pps_period_1M) 138 | { 139 | spGPStimeData->_u64_pps_period_1M += iSAR64((int64_t)eDtUpscale * dt_per_window 140 | - spGPStimeData->_u64_pps_period_1M + 2, 2); 141 | spGPStimeData->_i32_freq_shift_ppb = (spGPStimeData->_u64_pps_period_1M 142 | - (int64_t)eDtUpscale * eCLKperTimeMark * eSlidingLen 143 | + (eSlidingLen >> 1)) / eSlidingLen; 144 | } 145 | else 146 | { 147 | spGPStimeData->_u64_pps_period_1M = (int64_t)eDtUpscale * dt_per_window; 148 | } 149 | } 150 | 151 | #ifdef NOP 152 | const int64_t dt_1M = (dt_per_window + (eSlidingLen >> 1)) / eSlidingLen; 153 | const uint64_t tmp = (spGPStimeData->_u64_pps_period_1M + (eSlidingLen >> 1)) / eSlidingLen; 154 | printf("%llu %lld %llu %lld\n", spGPStimeData->_u64_sysclk_pps_last, dt_1M, tmp, 155 | spGPStimeData->_i32_freq_shift_ppb); 156 | #endif 157 | 158 | } 159 | } 160 | 161 | /// @brief Calculates current unixtime using data available. 162 | /// @param pg Ptr to the context. 163 | /// @param u32_tmdst Ptr to destination unixtime val. 164 | /// @return 0 if OK. 165 | /// @return -1 There was NO historical GPS fixes. 166 | /// @return -2 The fix was expired (24hrs or more time ago). 167 | int GPStimeGetTime(const GPStimeContext *pg, uint32_t *u32_tmdst) 168 | { 169 | assert_(pg); 170 | assert(u32_tmdst); 171 | 172 | /* If there has been no fix, it's no way to get any time data... */ 173 | if(!pg->_time_data._u32_utime_nmea_last) 174 | { 175 | return -1; 176 | } 177 | 178 | const uint64_t tm64 = GetUptime64(); 179 | const uint64_t dt = tm64 - pg->_time_data._u64_sysclk_nmea_last; 180 | const uint32_t dt_sec = PicoU64timeToSeconds(dt); 181 | 182 | /* If expired. */ 183 | if(dt_sec > 86400) 184 | { 185 | return -2; 186 | } 187 | 188 | *u32_tmdst = pg->_time_data._u32_utime_nmea_last + dt_sec; 189 | 190 | return 0; 191 | } 192 | 193 | /// @brief UART FIFO ISR. Processes another N chars receiver from GPS rec. 194 | void RAM (GPStimeUartRxIsr)() 195 | { 196 | if(spGPStimeContext) 197 | { 198 | uart_inst_t *puart_id = spGPStimeContext->_uart_id ? uart1 : uart0; 199 | for(;;uart_is_readable(puart_id)) 200 | { 201 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 202 | uint8_t chr = uart_getc(puart_id); 203 | spGPStimeContext->_pbytebuff[spGPStimeContext->_u8_ixw++] = chr; 204 | spGPStimeContext->_is_sentence_ready = ('\n' == chr); 205 | break; 206 | } 207 | 208 | if(spGPStimeContext->_is_sentence_ready) 209 | { 210 | spGPStimeContext->_u8_ixw = 0; 211 | spGPStimeContext->_i32_error_count -= GPStimeProcNMEAsentence(spGPStimeContext); 212 | } 213 | } 214 | } 215 | 216 | /// @brief Processes a NMEA sentence GPRMC. 217 | /// @param pg Ptr to Context. 218 | /// @return 0 OK. 219 | /// @return -2 Error: bad lat format. 220 | /// @return -3 Error: bad lon format. 221 | /// @return -4 Error: no final '*' char ere checksum value. 222 | /// @attention Checksum validation is not implemented so far. !FIXME! 223 | int GPStimeProcNMEAsentence(GPStimeContext *pg) 224 | { 225 | assert_(pg); 226 | 227 | uint8_t *prmc = (uint8_t *)strnstr((char *)pg->_pbytebuff, "$GPRMC,", sizeof(pg->_pbytebuff)); 228 | if(prmc) 229 | { 230 | ++pg->_time_data._u32_nmea_gprmc_count; 231 | 232 | uint64_t tm_fix = GetUptime64(); 233 | uint8_t u8ixcollector[16] = {0}; 234 | uint8_t chksum = 0; 235 | for(uint8_t u8ix = 0, i = 0; u8ix != sizeof(pg->_pbytebuff); ++u8ix) 236 | { 237 | uint8_t *p = pg->_pbytebuff + u8ix; 238 | chksum ^= *p; 239 | if(',' == *p) 240 | { 241 | *p = 0; 242 | u8ixcollector[i++] = u8ix + 1; 243 | if('*' == *p || 12 == i) 244 | { 245 | break; 246 | } 247 | } 248 | } 249 | 250 | pg->_time_data._u8_is_solution_active = 'A' == prmc[u8ixcollector[1]]; 251 | 252 | if(pg->_time_data._u8_is_solution_active) 253 | { 254 | pg->_time_data._i64_lat_100k = (int64_t)(.5f + 1e5 * atof((const char *)prmc + u8ixcollector[2])); 255 | if('N' == prmc[u8ixcollector[3]]) { } 256 | else if('S' == prmc[u8ixcollector[3]]) 257 | { 258 | INVERSE(pg->_time_data._i64_lat_100k); 259 | } 260 | else 261 | { 262 | return -2; 263 | } 264 | 265 | pg->_time_data._i64_lon_100k = (int64_t)(.5f + 1e5 * atof((const char *)prmc + u8ixcollector[4])); 266 | if('E' == prmc[u8ixcollector[5]]) { } 267 | else if('W' == prmc[u8ixcollector[5]]) 268 | { 269 | INVERSE(pg->_time_data._i64_lon_100k); 270 | } 271 | else 272 | { 273 | return -3; 274 | } 275 | 276 | if('*' != prmc[u8ixcollector[11] + 1]) 277 | { 278 | return -4; 279 | } 280 | 281 | pg->_time_data._u32_utime_nmea_last = GPStime2UNIX(prmc + u8ixcollector[8], prmc + u8ixcollector[0]); 282 | pg->_time_data._u64_sysclk_nmea_last = tm_fix; 283 | } 284 | } 285 | 286 | return 0; 287 | } 288 | 289 | /// @brief Converts GPS time and date strings to unix time. 290 | /// @param pdate Date string, 6 chars in work. 291 | /// @param ptime Time string, 6 chars in work. 292 | /// @return Unix timestamp (epoch). 0 if bad imput format. 293 | uint32_t GPStime2UNIX(const char *pdate, const char *ptime) 294 | { 295 | assert_(pdate); 296 | assert_(ptime); 297 | 298 | if(strlen(pdate) == 6 && strlen(ptime) > 5) 299 | { 300 | struct tm ltm = {0}; 301 | 302 | ltm.tm_year = 100 + DecimalStr2ToNumber(pdate + 4); 303 | ltm.tm_mon = DecimalStr2ToNumber(pdate + 2) - 1; 304 | ltm.tm_mday = DecimalStr2ToNumber(pdate); 305 | 306 | ltm.tm_hour = DecimalStr2ToNumber(ptime); 307 | ltm.tm_min = DecimalStr2ToNumber(ptime + 2); 308 | ltm.tm_sec = DecimalStr2ToNumber(ptime + 4); 309 | 310 | return mktime(<m); 311 | } 312 | 313 | return 0; 314 | } 315 | 316 | /// @brief Dumps the GPS data struct to stdio. 317 | /// @param pd Ptr to Context. 318 | void GPStimeDump(const GPStimeData *pd) 319 | { 320 | assert_(pd); 321 | 322 | printf("\nGPS solution is active:%u\n", pd->_u8_is_solution_active); 323 | printf("GPRMC count:%lu\n", pd->_u32_nmea_gprmc_count); 324 | printf("NMEA unixtime last:%lu\n", pd->_u32_utime_nmea_last); 325 | printf("NMEA sysclock last:%llu\n", pd->_u64_sysclk_nmea_last); 326 | printf("GPS Latitude:%lld Longtitude:%lld\n", pd->_i64_lat_100k, pd->_i64_lon_100k); 327 | printf("PPS sysclock last:%llu\n", pd->_u64_sysclk_pps_last); 328 | printf("PPS period *1e6:%llu\n", (pd->_u64_pps_period_1M + (eSlidingLen>>1)) / eSlidingLen); 329 | printf("FRQ correction ppb:%lld\n\n", pd->_i32_freq_shift_ppb); 330 | } 331 | -------------------------------------------------------------------------------- /gpstime/GPStime.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // gpstime.h - GPS time reference utilities for digital controlled radio freq 10 | // oscillator based on Raspberry Pi Pico. 11 | // 12 | // DESCRIPTION 13 | // 14 | // GPS time utilities for pico-hf-oscillator calculate a precise frequency 15 | // shift between the local Pico oscillator and reference oscill. of GPS system. 16 | // The value of the shift is used to correct generated frequency. The practical 17 | // precision of this solution depends on GPS receiver's time pulse stability, 18 | // as well as on quality of navigation solution of GPS receiver. 19 | // This quality can be estimated by GDOP and TDOP parameters received 20 | // in NMEA-0183 message packet from GPS receiver. 21 | // Owing to the meager frequency step in millihertz range, we obtain 22 | // a quasi-analog precision frequency source (if the GPS navigation works OK). 23 | // This is an experimental project of amateur radio class and it is devised 24 | // by me on the free will base in order to experiment with QRP narrowband 25 | // digital modes including extremely ones such as QRSS. 26 | // I appreciate any thoughts or comments on that matter. 27 | // 28 | // PLATFORM 29 | // Raspberry Pi pico. 30 | // 31 | // REVISION HISTORY 32 | // 33 | // Rev 0.1 25 Nov 2023 Initial release 34 | // 35 | // PROJECT PAGE 36 | // https://github.com/RPiks/pico-hf-oscillator 37 | // 38 | // LICENCE 39 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 40 | // 41 | // Copyright (c) 2023 by Roman Piksaykin 42 | // 43 | // Permission is hereby granted, free of charge,to any person obtaining a copy 44 | // of this software and associated documentation files (the Software), to deal 45 | // in the Software without restriction,including without limitation the rights 46 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 47 | // copies of the Software, and to permit persons to whom the Software is 48 | // furnished to do so, subject to the following conditions: 49 | // 50 | // The above copyright notice and this permission notice shall be included in 51 | // all copies or substantial portions of the Software. 52 | // 53 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 54 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 55 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 56 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 57 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 58 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 59 | // THE SOFTWARE. 60 | /////////////////////////////////////////////////////////////////////////////// 61 | #ifndef GPSTIME_H_ 62 | #define GPSTIME_H_ 63 | 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include "pico/stdlib.h" 70 | #include "hardware/uart.h" 71 | #include "../defines.h" 72 | #include "../lib/assert.h" 73 | #include "../lib/utility.h" 74 | #include "../lib/thirdparty/strnstr.h" 75 | 76 | #define ASSERT_(x) assert_(x) 77 | 78 | enum 79 | { 80 | eDtUpscale = 1000000, 81 | eSlidingLen = 32, 82 | eCLKperTimeMark = 1000000, 83 | eMaxCLKdevPPM = 250 84 | }; 85 | 86 | typedef struct 87 | { 88 | uint8_t _u8_is_solution_active; /* A navigation solution is valid. */ 89 | uint32_t _u32_utime_nmea_last; /* The last unix time received from GPS. */ 90 | uint64_t _u64_sysclk_nmea_last; /* The sysclk of the last unix time received. */ 91 | int64_t _i64_lat_100k, _i64_lon_100k; /* The lat, lon, degrees, multiplied by 1e5. */ 92 | uint32_t _u32_nmea_gprmc_count; /* The count of $GPRMC sentences received */ 93 | 94 | uint64_t _u64_sysclk_pps_last; /* The sysclk of the last rising edge of PPS. */ 95 | uint64_t _u64_pps_period_1M; /* The PPS avg. period *1e6, filtered. */ 96 | 97 | uint64_t _pu64_sliding_pps_tm[eSlidingLen]; /* A sliding window to store PPS periods. */ 98 | uint8_t _ix_last; /* An index of last write to sliding window. */ 99 | 100 | int64_t _i32_freq_shift_ppb; /* Calcd frequency shift, parts per billion. */ 101 | 102 | } GPStimeData; 103 | 104 | typedef struct 105 | { 106 | int _uart_id; 107 | int _uart_baudrate; 108 | int _pps_gpio; 109 | 110 | GPStimeData _time_data; 111 | 112 | uint8_t _pbytebuff[256]; 113 | uint8_t _u8_ixw; 114 | uint8_t _is_sentence_ready; 115 | int32_t _i32_error_count; 116 | 117 | } GPStimeContext; 118 | 119 | GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio); 120 | void GPStimeDestroy(GPStimeContext **pp); 121 | 122 | int GPStimeProcNMEAsentence(GPStimeContext *pg); 123 | 124 | void RAM (GPStimePPScallback)(uint gpio, uint32_t events); 125 | void RAM (GPStimeUartRxIsr)(); 126 | 127 | int GPStimeGetTime(const GPStimeContext *pg, uint32_t *u32_tmdst); 128 | uint32_t GPStime2UNIX(const char *pdate, const char *ptime); 129 | 130 | void GPStimeDump(const GPStimeData *pd); 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /hfconsole/hfconsole.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // hfconsole.c - Serial console of Raspberry Pi Pico. 10 | // 11 | // DESCRIPTION 12 | // - 13 | // 14 | // PLATFORM 15 | // Raspberry Pi pico. 16 | // 17 | // REVISION HISTORY 18 | // Rev 0.1 16 Dec 2023 Initial revision. 19 | // 20 | // PROJECT PAGE 21 | // https://github.com/RPiks/pico-hf-oscillator 22 | // 23 | // LICENCE 24 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 25 | // 26 | // Copyright (c) 2023 by Roman Piksaykin 27 | // 28 | // Permission is hereby granted, free of charge,to any person obtaining a copy 29 | // of this software and associated documentation files (the Software), to deal 30 | // in the Software without restriction,including without limitation the rights 31 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | // copies of the Software, and to permit persons to whom the Software is 33 | // furnished to do so, subject to the following conditions: 34 | // 35 | // The above copyright notice and this permission notice shall be included in 36 | // all copies or substantial portions of the Software. 37 | // 38 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 43 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | // THE SOFTWARE. 45 | /////////////////////////////////////////////////////////////////////////////// 46 | #include "hfconsole.h" 47 | 48 | HFconsoleContext *HFconsoleInit(int uart_id, int baud) 49 | { 50 | assert_(uart_id < 2); 51 | HFconsoleContext *pctx = calloc(1, sizeof(HFconsoleContext)); 52 | assert_(pctx); 53 | 54 | memset(pctx->buffer, 0, sizeof(pctx->buffer)); 55 | 56 | pctx->_uart_id = uart_id; 57 | pctx->_uart_baudrate = baud; 58 | 59 | return pctx; 60 | } 61 | 62 | void HFconsoleDestroy(HFconsoleContext **pp) 63 | { 64 | if(pp && *pp) 65 | { 66 | free(*pp); 67 | *pp = NULL; 68 | } 69 | } 70 | 71 | void HFconsoleClear(HFconsoleContext *pc) 72 | { 73 | if(pc) 74 | { 75 | memset(pc->buffer, 0, sizeof(pc->buffer)); 76 | pc->ix = 0; 77 | } 78 | } 79 | 80 | int HFconsoleProcess(HFconsoleContext *p, int ms) 81 | { 82 | const int ichr = getchar_timeout_us(ms); 83 | if(ichr < 0) 84 | { 85 | return -1; 86 | } 87 | 88 | switch(ichr) 89 | { 90 | case 13: 91 | HFconsoleEmitCommand(p); 92 | HFconsoleClear(p); 93 | printf("\n=> "); 94 | break; 95 | 96 | case 8: 97 | if(p->ix) 98 | { 99 | --p->ix; 100 | } 101 | printf("%c", ichr); 102 | break; 103 | 104 | default: 105 | p->buffer[p->ix++] = (char)ichr; 106 | printf("%c", ichr); 107 | break; 108 | } 109 | 110 | return 0; 111 | } 112 | 113 | int HFconsoleEmitCommand(HFconsoleContext *pc) 114 | { 115 | char *p = strchr(pc->buffer, ' '); 116 | if(p) 117 | { 118 | if(strlen(p) < 1) 119 | { 120 | return -1; 121 | } 122 | int narg = 0; 123 | const char* delimiters = ",. "; 124 | char* token = strtok(pc->buffer, delimiters); 125 | while (token) 126 | { 127 | ++narg; 128 | token = strtok(NULL, delimiters); 129 | } 130 | 131 | if(pc->_pfwrapper) 132 | { 133 | *p = 0; 134 | (*pc->_pfwrapper)(pc->buffer, narg, p+1); 135 | } 136 | 137 | return 0; 138 | } 139 | else 140 | { 141 | if(pc->_pfwrapper) 142 | { 143 | (*pc->_pfwrapper)(pc->buffer, 0, pc->buffer); 144 | } 145 | } 146 | 147 | return 0; 148 | } 149 | 150 | void HFconsoleSetWrapper(HFconsoleContext *pc, void *pfwrapper) 151 | { 152 | assert_(pc); 153 | 154 | pc->_pfwrapper = pfwrapper; 155 | } 156 | -------------------------------------------------------------------------------- /hfconsole/hfconsole.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // hfconsole.h - Serial console of Raspberry Pi Pico. 10 | // 11 | // DESCRIPTION 12 | // - 13 | // 14 | // PLATFORM 15 | // Raspberry Pi pico. 16 | // 17 | // REVISION HISTORY 18 | // Rev 0.1 16 Dec 2023 Initial revision. 19 | // 20 | // PROJECT PAGE 21 | // https://github.com/RPiks/pico-hf-oscillator 22 | // 23 | // LICENCE 24 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 25 | // 26 | // Copyright (c) 2023 by Roman Piksaykin 27 | // 28 | // Permission is hereby granted, free of charge,to any person obtaining a copy 29 | // of this software and associated documentation files (the Software), to deal 30 | // in the Software without restriction,including without limitation the rights 31 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | // copies of the Software, and to permit persons to whom the Software is 33 | // furnished to do so, subject to the following conditions: 34 | // 35 | // The above copyright notice and this permission notice shall be included in 36 | // all copies or substantial portions of the Software. 37 | // 38 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 43 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | // THE SOFTWARE. 45 | /////////////////////////////////////////////////////////////////////////////// 46 | #ifndef HFTERM_H_ 47 | #define HFTERM_H_ 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include "pico/stdlib.h" 55 | #include "hardware/uart.h" 56 | #include "../lib/assert.h" 57 | #include "../lib/utility.h" 58 | 59 | typedef struct 60 | { 61 | int _uart_id; // UART id (-1 when use Pico USB port) 62 | int _uart_baudrate; // UART baud rate (isn't used when uaer id is 0) 63 | 64 | void (*_pfwrapper)(char *, int, char *); 65 | 66 | char buffer[256]; 67 | uint8_t ix; 68 | 69 | } HFconsoleContext; 70 | 71 | HFconsoleContext *HFconsoleInit(int uart_id, int baud); 72 | void HFconsoleDestroy(HFconsoleContext **pp); 73 | int HFconsoleProcess(HFconsoleContext *p, int ms); 74 | int HFconsoleEmitCommand(HFconsoleContext *pc); 75 | void HFconsoleSetWrapper(HFconsoleContext *pc, void *pfwrapper); 76 | void HFconsoleClear(HFconsoleContext *pc); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /hwdefs.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // hwdefs.h - Hardware related constant values for the project. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // . 15 | // 16 | // PLATFORM 17 | // Raspberry Pi pico. 18 | // 19 | // REVISION HISTORY 20 | // 21 | // Rev 0.1 05 Nov 2023 Initial release 22 | // Rev 0.2 18 Nov 2023 23 | // 24 | // PROJECT PAGE 25 | // https://github.com/RPiks/pico-hf-oscillator 26 | // 27 | // LICENCE 28 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 29 | // 30 | // Copyright (c) 2023 by Roman Piksaykin 31 | // 32 | // Permission is hereby granted, free of charge,to any person obtaining a copy 33 | // of this software and associated documentation files (the Software), to deal 34 | // in the Software without restriction,including without limitation the rights 35 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | // copies of the Software, and to permit persons to whom the Software is 37 | // furnished to do so, subject to the following conditions: 38 | // 39 | // The above copyright notice and this permission notice shall be included in 40 | // all copies or substantial portions of the Software. 41 | // 42 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 47 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | // THE SOFTWARE. 49 | /////////////////////////////////////////////////////////////////////////////// 50 | #ifndef HWDEFS_H 51 | #define HWDEFS_H 52 | 53 | /**************************************************************************** 54 | * General defs. 55 | ****************************************************************************/ 56 | #define kHZ 1000L 57 | 58 | /**************************************************************************** 59 | * RPi pico specific defs. 60 | ****************************************************************************/ 61 | #define CLK_ADC_FREQ 48000000L /* pico's ADC clock is 48M. */ 62 | #define GPIO(x) (x) /* pico GPIO number to address. */ 63 | #define PLL_SYS_MHZ 270 /* system clock. */ 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /lib/assert.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], 4 | // HAM radio callsign R2BDY https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // assert.c - Assertion functions for Raspberry Pi pico which use LED. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // Assertion functions which use LED to inform developer concerning 15 | // the exceptional runtime conditions. 16 | // 17 | // PLATFORM 18 | // Raspberry Pi pico. 19 | // 20 | // REVISION HISTORY 21 | // 22 | // Rev 1.0 25 Sep 2022 23 | // Production release. 24 | // 25 | // LICENCE 26 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 27 | // 28 | // Copyright (c) 2022 by Roman Piksaykin 29 | // 30 | // Permission is hereby granted, free of charge,to any person obtaining a copy 31 | // of this software and associated documentation files (the Software), to deal 32 | // in the Software without restriction,including without limitation the rights 33 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | // copies of the Software, and to permit persons to whom the Software is 35 | // furnished to do so, subject to the following conditions: 36 | // 37 | // The above copyright notice and this permission notice shall be included in 38 | // all copies or substantial portions of the Software. 39 | // 40 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 45 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | // THE SOFTWARE. 47 | /////////////////////////////////////////////////////////////////////////////// 48 | #include "assert.h" 49 | 50 | void assert_(bool val) 51 | { 52 | if(val) 53 | { 54 | return; 55 | } 56 | 57 | gpio_init(PICO_DEFAULT_LED_PIN); 58 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 59 | 60 | for(;;) 61 | { 62 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 63 | sleep_ms(50); 64 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 65 | sleep_ms(500); 66 | } 67 | } 68 | 69 | void assert_checkpoint(bool val, int n_blink) 70 | { 71 | if(val) 72 | { 73 | return; 74 | } 75 | 76 | gpio_init(PICO_DEFAULT_LED_PIN); 77 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 78 | 79 | for(;;) 80 | { 81 | for(int i = 0; i < n_blink; ++i) 82 | { 83 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 84 | sleep_ms(50); 85 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 86 | sleep_ms(50); 87 | } 88 | sleep_ms(1000); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/assert.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], 4 | // HAM radio callsign R2BDY https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // assert.h - Assertion functions for Raspberry Pi pico which use LED. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // Assertion functions which use LED to inform developer concerning 15 | // the exceptional runtime conditions. 16 | // 17 | // PLATFORM 18 | // Raspberry Pi pico. 19 | // 20 | // REVISION HISTORY 21 | // 22 | // Rev 1.0 25 Sep 2022 23 | // Production release. 24 | // 25 | // LICENCE 26 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 27 | // 28 | // Copyright (c) 2022 by Roman Piksaykin 29 | // 30 | // Permission is hereby granted, free of charge,to any person obtaining a copy 31 | // of this software and associated documentation files (the Software), to deal 32 | // in the Software without restriction,including without limitation the rights 33 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | // copies of the Software, and to permit persons to whom the Software is 35 | // furnished to do so, subject to the following conditions: 36 | // 37 | // The above copyright notice and this permission notice shall be included in 38 | // all copies or substantial portions of the Software. 39 | // 40 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 45 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | // THE SOFTWARE. 47 | /////////////////////////////////////////////////////////////////////////////// 48 | #include 49 | 50 | #include "pico/stdlib.h" 51 | 52 | void assert_(bool val); 53 | void assert_checkpoint(bool val, int n_blink); 54 | -------------------------------------------------------------------------------- /lib/thirdparty/strnstr.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2001 Mike Barcroft 3 | * Copyright (c) 1990, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * This code is derived from software contributed to Berkeley by 7 | * Chris Torek. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | #if defined(LIBC_SCCS) && !defined(lint) 35 | static char sccsid[] = "@(#)strstr.c 8.1 (Berkeley) 6/4/93"; 36 | #endif /* LIBC_SCCS and not lint */ 37 | #include 38 | __FBSDID("$FreeBSD: src/lib/libc/string/strnstr.c,v 1.5 2009/02/03 17:58:20 danger Exp $"); 39 | 40 | #include 41 | 42 | /* 43 | * Find the first occurrence of find in s, where the search is limited to the 44 | * first slen characters of s. 45 | */ 46 | char * 47 | strnstr(const char *s, const char *find, size_t slen) 48 | { 49 | char c, sc; 50 | size_t len; 51 | 52 | if ((c = *find++) != '\0') { 53 | len = strlen(find); 54 | do { 55 | do { 56 | if (slen-- < 1 || (sc = *s++) == '\0') 57 | return (NULL); 58 | } while (sc != c); 59 | if (len > slen) 60 | return (NULL); 61 | } while (strncmp(s, find, len) != 0); 62 | s--; 63 | } 64 | return ((char *)s); 65 | } 66 | -------------------------------------------------------------------------------- /lib/thirdparty/strnstr.h: -------------------------------------------------------------------------------- 1 | #ifndef STRNSTR_H_ 2 | #define STRNSTR_H_ 3 | 4 | char * 5 | strnstr(const char *s, const char *find, size_t slen); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /lib/utility.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // utility.h - Utilities for RPiks' projects. 10 | // 11 | // DESCRIPTION 12 | // - 13 | // 14 | // PLATFORM 15 | // Raspberry Pi pico. 16 | // 17 | // REVISION HISTORY 18 | // 19 | // Rev 0.1 25 Nov 2023 Initial release 20 | // 21 | // PROJECT PAGE 22 | // https://github.com/RPiks/pico-hf-oscillator 23 | // 24 | // LICENCE 25 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 26 | // 27 | // Copyright (c) 2023 by Roman Piksaykin 28 | // 29 | // Permission is hereby granted, free of charge,to any person obtaining a copy 30 | // of this software and associated documentation files (the Software), to deal 31 | // in the Software without restriction,including without limitation the rights 32 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | // copies of the Software, and to permit persons to whom the Software is 34 | // furnished to do so, subject to the following conditions: 35 | // 36 | // The above copyright notice and this permission notice shall be included in 37 | // all copies or substantial portions of the Software. 38 | // 39 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 44 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 45 | // THE SOFTWARE. 46 | /////////////////////////////////////////////////////////////////////////////// 47 | #ifndef UTILITY_H_ 48 | #define UTILITY_H_ 49 | 50 | #include 51 | #include "pico/stdlib.h" 52 | 53 | inline uint64_t GetUptime64(void) 54 | { 55 | const uint32_t lo = timer_hw->timelr; 56 | const uint32_t hi = timer_hw->timehr; 57 | 58 | return ((uint64_t)hi << 32U) | lo; 59 | } 60 | 61 | inline uint32_t GetTime32(void) 62 | { 63 | return timer_hw->timelr; 64 | } 65 | 66 | inline uint32_t PicoU64timeToSeconds(uint64_t u64tm) 67 | { 68 | return u64tm / 1000000U; // No rounding deliberately! 69 | } 70 | 71 | inline uint32_t DecimalStr2ToNumber(const char *p) 72 | { 73 | return 10U * (p[0] - '0') + (p[1] - '0'); 74 | } 75 | 76 | inline void PRN32(uint32_t *val) 77 | { 78 | *val ^= *val << 13; 79 | *val ^= *val >> 17; 80 | *val ^= *val << 5; 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /piodco/dco2.pio: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // dco2.pio - Digital controlled radio freq oscillator based on PIO. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // The oscillator provides precise generation of any frequency ranging 15 | // from 1 Hz to 33.333 MHz with tenth's of millihertz resolution (please note that 16 | // this is relative resolution owing to the fact that the absolute accuracy of 17 | // onboard crystal of pi pico is limited; the absoulte accuracy can be provided 18 | // when using GPS reference option included). 19 | // The DCO uses phase locked loop principle programmed in C and PIO asm. 20 | // The DCO does *NOT* use any floating point operations - all time-critical 21 | // instructions run in 1 CPU cycle. 22 | // Currently the upper freq. limit is about 33.333 MHz and it is achieved only 23 | // using pi pico overclocking to 270 MHz. 24 | // Owing to the meager frequency step, it is possible to use 3, 5, or 7th 25 | // harmonics of generated frequency. Such solution completely cover all HF and 26 | // a portion of VHF band up to about 233 MHz. 27 | // Unfortunately due to pure digital freq.synthesis principle the jitter may 28 | // be a problem on higher frequencies. You should assess the quality of generated 29 | // signal if you want to emit a noticeable power. 30 | // This is an experimental project of amateur radio class and it is devised 31 | // by me on the free will base in order to experiment with QRP narrowband 32 | // digital modes. 33 | // I appreciate any thoughts or comments on that matter. 34 | // 35 | // PLATFORM 36 | // Raspberry Pi pico. 37 | // 38 | // REVISION HISTORY 39 | // 40 | // Rev 0.1 05 Nov 2023 Initial release 41 | // Rev 0.2 18 Nov 2023 42 | // Rev 1.0 10 Dec 2023 Improved frequency range (to ~33.333 MHz). 43 | // 44 | // PROJECT PAGE 45 | // https://github.com/RPiks/pico-hf-oscillator 46 | // 47 | // LICENCE 48 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 49 | // 50 | // Copyright (c) 2023 by Roman Piksaykin 51 | // 52 | // Permission is hereby granted, free of charge,to any person obtaining a copy 53 | // of this software and associated documentation files (the Software), to deal 54 | // in the Software without restriction,including without limitation the rights 55 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | // copies of the Software, and to permit persons to whom the Software is 57 | // furnished to do so, subject to the following conditions: 58 | // 59 | // The above copyright notice and this permission notice shall be included in 60 | // all copies or substantial portions of the Software. 61 | // 62 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 67 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 68 | // THE SOFTWARE. 69 | /////////////////////////////////////////////////////////////////////////////// 70 | .program dco 71 | 72 | .wrap_target 73 | out y, 32 74 | mov x, y 75 | LOOP0: 76 | jmp x-- LOOP0 77 | set pins, 1 78 | 79 | mov x, y [1] 80 | LOOP1: 81 | jmp x-- LOOP1 82 | set pins, 0 83 | 84 | mov x, y [1] 85 | LOOP2: 86 | jmp x-- LOOP2 87 | set pins, 1 88 | 89 | mov x, y [1] 90 | LOOP3: 91 | jmp x-- LOOP3 92 | set pins, 0 93 | .wrap 94 | 95 | % c-sdk { 96 | 97 | #define PIOASM_DELAY_CYCLES 4 98 | 99 | static inline void dco_program_init(PIO pio, uint sm, uint offset, uint pin) 100 | { 101 | pio_sm_config c = dco_program_get_default_config(offset); 102 | 103 | sm_config_set_out_shift(&c, true, true, 32); // Autopull. 104 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 105 | 106 | sm_config_set_out_pins(&c, pin, 1); 107 | pio_gpio_init(pio, pin); 108 | 109 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 110 | 111 | sm_config_set_clkdiv_int_frac(&c, 1u, 0u); 112 | 113 | pio_sm_init(pio, sm, offset, &c); 114 | pio_sm_set_enabled(pio, sm, true); 115 | } 116 | // 117 | static inline void dco_program_puts(PIO pio, uint sm, const uint32_t *s) 118 | { 119 | pio_sm_put_blocking(pio, sm, s[0]); 120 | pio_sm_put_blocking(pio, sm, s[1]); 121 | pio_sm_put_blocking(pio, sm, s[2]); 122 | pio_sm_put_blocking(pio, sm, s[3]); 123 | pio_sm_put_blocking(pio, sm, s[4]); 124 | pio_sm_put_blocking(pio, sm, s[5]); 125 | pio_sm_put_blocking(pio, sm, s[6]); 126 | pio_sm_put_blocking(pio, sm, s[7]); 127 | } 128 | 129 | static inline void dco_program_puts1w(PIO pio, uint sm, const uint32_t val) 130 | { 131 | pio_sm_put_blocking(pio, sm, val); 132 | } 133 | %} 134 | -------------------------------------------------------------------------------- /piodco/piodco.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // piodco.c - Digital controlled radio freq oscillator based on PIO. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // The oscillator provides precise generation of any frequency ranging 15 | // from 1 Hz to 33.333 MHz with tenth's of millihertz resolution (please note that 16 | // this is relative resolution owing to the fact that the absolute accuracy of 17 | // onboard crystal of pi pico is limited; the absoulte accuracy can be provided 18 | // when using GPS reference option included). 19 | // The DCO uses phase locked loop principle programmed in C and PIO asm. 20 | // The DCO does *NOT* use any floating point operations - all time-critical 21 | // instructions run in 1 CPU cycle. 22 | // Currently the upper freq. limit is about 33.333 MHz and it is achieved only 23 | // using pi pico overclocking to 270 MHz. 24 | // Owing to the meager frequency step, it is possible to use 3, 5, or 7th 25 | // harmonics of generated frequency. Such solution completely cover all HF and 26 | // a portion of VHF band up to about 233 MHz. 27 | // Unfortunately due to pure digital freq.synthesis principle the jitter may 28 | // be a problem on higher frequencies. You should assess the quality of generated 29 | // signal if you want to emit a noticeable power. 30 | // This is an experimental project of amateur radio class and it is devised 31 | // by me on the free will base in order to experiment with QRP narrowband 32 | // digital modes. 33 | // I appreciate any thoughts or comments on that matter. 34 | // 35 | // PLATFORM 36 | // Raspberry Pi pico. 37 | // 38 | // REVISION HISTORY 39 | // 40 | // Rev 0.1 05 Nov 2023 Initial release 41 | // Rev 0.2 18 Nov 2023 42 | // Rev 1.0 10 Dec 2023 Improved frequency range (to ~33.333 MHz). 43 | // 44 | // PROJECT PAGE 45 | // https://github.com/RPiks/pico-hf-oscillator 46 | // 47 | // LICENCE 48 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 49 | // 50 | // Copyright (c) 2023 by Roman Piksaykin 51 | // 52 | // Permission is hereby granted, free of charge,to any person obtaining a copy 53 | // of this software and associated documentation files (the Software), to deal 54 | // in the Software without restriction,including without limitation the rights 55 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | // copies of the Software, and to permit persons to whom the Software is 57 | // furnished to do so, subject to the following conditions: 58 | // 59 | // The above copyright notice and this permission notice shall be included in 60 | // all copies or substantial portions of the Software. 61 | // 62 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 67 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 68 | // THE SOFTWARE. 69 | /////////////////////////////////////////////////////////////////////////////// 70 | #include "piodco.h" 71 | 72 | #include 73 | #include "../lib/assert.h" 74 | 75 | #include "build/dco2.pio.h" 76 | 77 | volatile int32_t si32precise_cycles; 78 | 79 | /// @brief Initializes DCO context and prepares PIO hardware. 80 | /// @param pdco Ptr to DCO context. 81 | /// @param gpio The GPIO of DCO output. 82 | /// @param cpuclkhz The system CPU clock freq., Hz. 83 | /// @return 0 if OK. 84 | int PioDCOInit(PioDco *pdco, int gpio, int cpuclkhz) 85 | { 86 | assert_(pdco); 87 | assert_(cpuclkhz); 88 | 89 | memset(pdco, 0, sizeof(PioDco)); 90 | 91 | pdco->_clkfreq_hz = cpuclkhz; 92 | pdco->_pio = pio0; 93 | pdco->_gpio = gpio; 94 | pdco->_offset = pio_add_program(pdco->_pio, &dco_program); 95 | pdco->_ism = pio_claim_unused_sm(pdco->_pio, true); 96 | 97 | gpio_init(pdco->_gpio); 98 | pio_gpio_init(pdco->_pio, pdco->_gpio); 99 | 100 | dco_program_init(pdco->_pio, pdco->_ism, pdco->_offset, pdco->_gpio); 101 | pdco->_pio_sm = dco_program_get_default_config(pdco->_offset); 102 | 103 | sm_config_set_out_shift(&pdco->_pio_sm, true, true, 32); // Autopull. 104 | sm_config_set_fifo_join(&pdco->_pio_sm, PIO_FIFO_JOIN_TX); 105 | sm_config_set_set_pins(&pdco->_pio_sm, pdco->_gpio, 1); 106 | 107 | pio_sm_init(pdco->_pio, pdco->_ism, pdco->_offset, &pdco->_pio_sm); 108 | 109 | return 0; 110 | } 111 | 112 | /// @brief Sets DCO working frequency in Hz: Fout = ui32_frq_hz + ui32_frq_millihz * 1e-3. 113 | /// @param pdco Ptr to DCO context. 114 | /// @param i32_frq_hz The `coarse` part of frequency [Hz]. Might be negative. 115 | /// @param ui32_frq_millihz The `fine` part of frequency [Hz]. 116 | /// @return 0 if OK. -1 invalid freq. 117 | /// @attention The func can be called while DCO running. 118 | int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, int32_t ui32_frq_millihz) 119 | { 120 | assert_(pdco); 121 | assert(pdco->_clkfreq_hz); 122 | 123 | /* RPix: Calculate an accurate value of phase increment of the freq 124 | per 1 tick of CPU clock, here 2^24 is scaling coefficient. */ 125 | const int64_t i64denominator = 2000LL * (int64_t)ui32_frq_hz + (int64_t)ui32_frq_millihz; 126 | pdco->_frq_cycles_per_pi = (int32_t)(((int64_t)pdco->_clkfreq_hz * (int64_t)(1<<24) * 1000LL 127 | +(i64denominator>>1)) / i64denominator); 128 | 129 | si32precise_cycles = pdco->_frq_cycles_per_pi - (PIOASM_DELAY_CYCLES<<24); 130 | 131 | pdco->_ui32_frq_hz = ui32_frq_hz; 132 | pdco->_ui32_frq_millihz = ui32_frq_millihz; 133 | 134 | return 0; 135 | } 136 | 137 | /// @brief Obtains the frequency shift [milliHz] which is calculated for a given frequency. 138 | /// @param pdco Ptr to Context. 139 | /// @param u64_desired_frq_millihz The frequency for which we want to calculate correction. 140 | /// @return The value of correction we need to subtract from desired freq. to compensate 141 | /// @return Pico's reference clock shift. 2854974. 142 | int32_t PioDCOGetFreqShiftMilliHertz(const PioDco *pdco, uint64_t u64_desired_frq_millihz) 143 | { 144 | assert_(pdco); 145 | if(!pdco->_pGPStime) 146 | { 147 | return 0U; 148 | } 149 | 150 | static int64_t i64_last_correction = 0; 151 | const int64_t dt = pdco->_pGPStime->_time_data._i32_freq_shift_ppb; /* Parts per billion. */ 152 | if(dt) 153 | { 154 | i64_last_correction = dt; 155 | } 156 | 157 | int32_t i32ret_millis; 158 | if(i64_last_correction) 159 | { 160 | int64_t i64corr_coeff = (u64_desired_frq_millihz + 500000LL) / 1000000LL; 161 | i32ret_millis = (i64_last_correction * i64corr_coeff + 50000LL) / 1000000LL; 162 | 163 | return i32ret_millis; 164 | } 165 | 166 | return 0U; 167 | } 168 | 169 | /// @brief Starts the DCO. 170 | /// @param pdco Ptr to DCO context. 171 | void PioDCOStart(PioDco *pdco) 172 | { 173 | assert_(pdco); 174 | pio_sm_set_enabled(pdco->_pio, pdco->_ism, true); 175 | 176 | pdco->_is_enabled = YES; 177 | } 178 | 179 | /// @brief Stops the DCO. 180 | /// @param pdco Ptr to DCO context. 181 | void PioDCOStop(PioDco *pdco) 182 | { 183 | assert_(pdco); 184 | pio_sm_set_enabled(pdco->_pio, pdco->_ism, false); 185 | 186 | pdco->_is_enabled = NO; 187 | } 188 | 189 | /// @brief Main worker task of DCO V.2. It is time critical, so it ought to be run on 190 | /// @brief the dedicated pi pico core. 191 | /// @param pDCO Ptr to DCO context. 192 | /// @return No return. It spins forever. 193 | void RAM (PioDCOWorker2)(PioDco *pDCO) 194 | { 195 | register PIO pio = pDCO->_pio; 196 | register uint sm = pDCO->_ism; 197 | register int32_t i32acc_error = 0; 198 | register uint32_t i32wc, i32reg; 199 | 200 | LOOP: 201 | i32reg = si32precise_cycles; 202 | i32wc = (i32reg - i32acc_error) >> 24U; 203 | pio_sm_put_blocking(pio, sm, i32wc); 204 | i32acc_error += (i32wc << 24U) - i32reg; 205 | 206 | goto LOOP; 207 | } 208 | 209 | /// @brief Main worker task of DCO. It is time critical, so it ought to be run on 210 | /// @brief the dedicated pi pico core. 211 | /// @param pDCO Ptr to DCO context. 212 | /// @return No return. It spins forever. 213 | void RAM (PioDCOWorker)(PioDco *pDCO) 214 | { 215 | assert_(pDCO); 216 | 217 | register PIO pio = pDCO->_pio; 218 | register uint sm = pDCO->_ism; 219 | register int32_t i32acc_error = 0; 220 | register uint32_t *preg32 = pDCO->_ui32_pioreg; 221 | register uint8_t *pu8reg = (uint8_t *)preg32; 222 | 223 | for(;;) 224 | { 225 | const register int32_t i32reg = si32precise_cycles; 226 | /* RPix: Load the next precise value of CPU CLK cycles per DCO cycle, 227 | scaled by 2^24. It yields about 24 millihertz resolution at @10MHz 228 | DCO frequency. */ 229 | for(int i = 0; i < 32; ++i) 230 | { 231 | /* RPix: Calculate the integer number of CPU CLK cycles per next 232 | DCO cycle, corrected by accumulated error (feedback of the PLL). */ 233 | const int32_t i32wc = iSAR32(i32reg - i32acc_error + (1<<23), 24); 234 | 235 | /* RPix: Calculate the difference betwixt calculated value scaled to 236 | fine resolution back and precise value of DCO cycles per CPU CLK cycle. 237 | This forms a phase locked loop which provides precise freq */ 238 | i32acc_error += (i32wc<<24) - i32reg; 239 | 240 | /* RPix: Set PIO array contents corrected by pio program delay 241 | of N CPU CLK cycles owing to pio asm instructions. */ 242 | pu8reg[i] = i32wc - PIOASM_DELAY_CYCLES; 243 | } 244 | 245 | dco_program_puts(pio, sm, preg32); 246 | } 247 | } 248 | 249 | /// @brief Sets DCO running mode. 250 | /// @param pdco Ptr to DCO context. 251 | /// @param emode Desired mode. 252 | /// @attention Not actual so far. User-independent freq. correction not impl'd yet. !FIXME! 253 | void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode) 254 | { 255 | assert_(pdco); 256 | pdco->_mode = emode; 257 | 258 | switch(emode) 259 | { 260 | case eDCOMODE_IDLE: 261 | PioDCOStop(pdco); 262 | break; 263 | 264 | case eDCOMODE_GPS_COMPENSATED: 265 | PioDCOStart(pdco); 266 | break; 267 | 268 | default: 269 | break; 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /piodco/piodco.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // piodco.h - Digital controlled radio freq oscillator based on PIO. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // The oscillator provides precise generation of any frequency ranging 15 | // from 1 Hz to 33.333 MHz with tenth's of millihertz resolution (please note that 16 | // this is relative resolution owing to the fact that the absolute accuracy of 17 | // onboard crystal of pi pico is limited; the absoulte accuracy can be provided 18 | // when using GPS reference option included). 19 | // The DCO uses phase locked loop principle programmed in C and PIO asm. 20 | // The DCO does *NOT* use any floating point operations - all time-critical 21 | // instructions run in 1 CPU cycle. 22 | // Currently the upper freq. limit is about 33.333 MHz and it is achieved only 23 | // using pi pico overclocking to 270 MHz. 24 | // Owing to the meager frequency step, it is possible to use 3, 5, or 7th 25 | // harmonics of generated frequency. Such solution completely cover all HF and 26 | // a portion of VHF band up to about 233 MHz. 27 | // Unfortunately due to pure digital freq.synthesis principle the jitter may 28 | // be a problem on higher frequencies. You should assess the quality of generated 29 | // signal if you want to emit a noticeable power. 30 | // This is an experimental project of amateur radio class and it is devised 31 | // by me on the free will base in order to experiment with QRP narrowband 32 | // digital modes. 33 | // I appreciate any thoughts or comments on that matter. 34 | // 35 | // PLATFORM 36 | // Raspberry Pi pico. 37 | // 38 | // REVISION HISTORY 39 | // 40 | // Rev 0.1 05 Nov 2023 Initial release 41 | // Rev 0.2 18 Nov 2023 42 | // Rev 1.0 10 Dec 2023 Improved frequency range (to ~33.333 MHz). 43 | // 44 | // PROJECT PAGE 45 | // https://github.com/RPiks/pico-hf-oscillator 46 | // 47 | // LICENCE 48 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 49 | // 50 | // Copyright (c) 2023 by Roman Piksaykin 51 | // 52 | // Permission is hereby granted, free of charge,to any person obtaining a copy 53 | // of this software and associated documentation files (the Software), to deal 54 | // in the Software without restriction,including without limitation the rights 55 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | // copies of the Software, and to permit persons to whom the Software is 57 | // furnished to do so, subject to the following conditions: 58 | // 59 | // The above copyright notice and this permission notice shall be included in 60 | // all copies or substantial portions of the Software. 61 | // 62 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 67 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 68 | // THE SOFTWARE. 69 | /////////////////////////////////////////////////////////////////////////////// 70 | #ifndef PIODCO_H_ 71 | #define PIODCO_H_ 72 | 73 | #include 74 | #include 75 | #include "pico/stdlib.h" 76 | #include "hardware/pio.h" 77 | 78 | #include "defines.h" 79 | 80 | #include "../gpstime/GPStime.h" 81 | 82 | enum PioDcoMode 83 | { 84 | eDCOMODE_IDLE = 0, /* No output. */ 85 | eDCOMODE_GPS_COMPENSATED= 2 /* Internally compensated, if GPS available. */ 86 | }; 87 | 88 | typedef struct 89 | { 90 | enum PioDcoMode _mode; /* Running mode. */ 91 | 92 | PIO _pio; /* Worker PIO on this DCO. */ 93 | int _gpio; /* Pico' GPIO for DCO output. */ 94 | 95 | pio_sm_config _pio_sm; /* Worker PIO parameter. */ 96 | int _ism; /* Index of state maschine. */ 97 | int _offset; /* Worker PIO u-program offset. */ 98 | 99 | int32_t _frq_cycles_per_pi; /* CPU CLK cycles per PI. */ 100 | 101 | uint32_t _ui32_pioreg[8]; /* Shift register to PIO. */ 102 | 103 | uint32_t _clkfreq_hz; /* CPU CLK freq, Hz. */ 104 | 105 | GPStimeContext *_pGPStime; /* Ptr to GPS time context. */ 106 | 107 | uint32_t _ui32_frq_hz; /* Working freq, Hz. */ 108 | int32_t _ui32_frq_millihz; /* Working freq additive shift, mHz. */ 109 | int _is_enabled; 110 | 111 | } PioDco; 112 | 113 | int PioDCOInit(PioDco *pdco, int gpio, int cpuclkhz); 114 | int PioDCOSetFreq(PioDco *pdco, uint32_t u32_frq_hz, int32_t u32_frq_millihz); 115 | int32_t PioDCOGetFreqShiftMilliHertz(const PioDco *pdco, uint64_t u64_desired_frq_millihz); 116 | 117 | void PioDCOStart(PioDco *pdco); 118 | void PioDCOStop(PioDco *pdco); 119 | 120 | void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode); 121 | 122 | void RAM (PioDCOWorker)(PioDco *pDCO); 123 | void RAM (PioDCOWorker2)(PioDco *pDCO); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /protos.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOS_H_ 2 | #define PROTOS_H_ 3 | 4 | #include "defines.h" 5 | 6 | /* main.c */ 7 | 8 | void RAM (SpinnerMFSKTest)(void); 9 | void RAM (SpinnerSweepTest)(void); 10 | void RAM (SpinnerRTTYTest)(void); 11 | void RAM (SpinnerMilliHertzTest)(void); 12 | void RAM (SpinnerWide4FSKTest)(void); 13 | void RAM (SpinnerGPSreferenceTest)(void); 14 | 15 | void core1_entry(); 16 | 17 | 18 | 19 | /* conswrapper.c */ 20 | 21 | void ConsoleCommandsWrapper(char *cmd, int narg, char *params); 22 | void PushErrorMessage(int id); 23 | void PushStatusMessage(void); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Roman Piksaykin [piksaykin@gmail.com], R2BDY 4 | // https://www.qrz.com/db/r2bdy 5 | // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // 9 | // test.c - Simple tests of digital controlled radio freq oscillator. 10 | // 11 | // 12 | // DESCRIPTION 13 | // 14 | // The oscillator provides precise generation of any frequency ranging 15 | // from 1 Hz to 33.333 MHz with tenth's of millihertz resolution (please note that 16 | // this is relative resolution owing to the fact that the absolute accuracy of 17 | // onboard crystal of pi pico is limited; the absoulte accuracy can be provided 18 | // when using GPS reference option included). 19 | // The DCO uses phase locked loop principle programmed in C and PIO asm. 20 | // The DCO does *NOT* use any floating point operations - all time-critical 21 | // instructions run in 1 CPU cycle. 22 | // Currently the upper freq. limit is about 33.333 MHz and it is achieved only 23 | // using pi pico overclocking to 270 MHz. 24 | // Owing to the meager frequency step, it is possible to use 3, 5, or 7th 25 | // harmonics of generated frequency. Such solution completely cover all HF and 26 | // a portion of VHF band up to about 233 MHz. 27 | // Unfortunately due to pure digital freq.synthesis principle the jitter may 28 | // be a problem on higher frequencies. You should assess the quality of generated 29 | // signal if you want to emit a noticeable power. 30 | // This is an experimental project of amateur radio class and it is devised 31 | // by me on the free will base in order to experiment with QRP narrowband 32 | // digital modes. 33 | // I appreciate any thoughts or comments on this matter. 34 | // 35 | // TESTS LIST 36 | // 37 | // SpinnerMFSKTest - It generates a random sequence of 2-FSK stream. 38 | // SpinnerSweepTest - Frequency sweep test of 5 Hz step. 39 | // SpinnerRTTYTest - Random RTTY sequence test (170 Hz). 40 | // SpinnerMilliHertzTest - A test of millihertz resolution of freq.setting. 41 | // SpinnerWide4FSKTest - Some `wide` 4-FSK test (100 Hz per step, 400 Hz overall). 42 | // SpinnerGPSreferenceTest - GPS receiver connection and working test. 43 | // 44 | // PLATFORM 45 | // Raspberry Pi pico. 46 | // 47 | // REVISION HISTORY 48 | // 49 | // Rev 0.1 05 Nov 2023 Initial release 50 | // Rev 0.2 18 Nov 2023 51 | // Rev 1.0 10 Dec 2023 Improved frequency range (to ~33.333 MHz). 52 | // 53 | // PROJECT PAGE 54 | // https://github.com/RPiks/pico-hf-oscillator 55 | // 56 | // LICENCE 57 | // MIT License (http://www.opensource.org/licenses/mit-license.php) 58 | // 59 | // Copyright (c) 2023 by Roman Piksaykin 60 | // 61 | // Permission is hereby granted, free of charge,to any person obtaining a copy 62 | // of this software and associated documentation files (the Software), to deal 63 | // in the Software without restriction,including without limitation the rights 64 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 65 | // copies of the Software, and to permit persons to whom the Software is 66 | // furnished to do so, subject to the following conditions: 67 | // 68 | // The above copyright notice and this permission notice shall be included in 69 | // all copies or substantial portions of the Software. 70 | // 71 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 72 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 73 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 74 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 75 | // LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, 76 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 77 | // THE SOFTWARE. 78 | /////////////////////////////////////////////////////////////////////////////// 79 | #include 80 | #include 81 | #include 82 | 83 | #include "defines.h" 84 | 85 | #include "piodco/piodco.h" 86 | #include "build/dco2.pio.h" 87 | #include "hardware/vreg.h" 88 | #include "pico/multicore.h" 89 | #include "pico/stdio/driver.h" 90 | 91 | #include "./lib/assert.h" 92 | #include "./debug/logutils.h" 93 | #include "hwdefs.h" 94 | 95 | #include 96 | 97 | #include 98 | 99 | #include "protos.h" 100 | 101 | //#define GEN_FRQ_HZ 32333333L 102 | #define GEN_FRQ_HZ 29977777L 103 | 104 | PioDco DCO; /* External in order to access in both cores. */ 105 | 106 | int main() 107 | { 108 | const uint32_t clkhz = PLL_SYS_MHZ * 1000000L; 109 | set_sys_clock_khz(clkhz / 1000L, true); 110 | 111 | stdio_init_all(); 112 | sleep_ms(1000); 113 | printf("Start\n"); 114 | 115 | HFconsoleContext *phfc = HFconsoleInit(-1, 0); 116 | HFconsoleSetWrapper(phfc, ConsoleCommandsWrapper); 117 | 118 | gpio_init(PICO_DEFAULT_LED_PIN); 119 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 120 | 121 | multicore_launch_core1(core1_entry); 122 | 123 | for(;;) 124 | { 125 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 126 | sleep_ms(5); 127 | int r = HFconsoleProcess(phfc, 10); 128 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 129 | sleep_ms(1); 130 | } 131 | 132 | for(;;) 133 | { 134 | sleep_ms(100); 135 | int chr = getchar_timeout_us(100);//getchar(); 136 | printf("%d %c\n", chr, (char)chr); 137 | } 138 | 139 | gpio_init(PICO_DEFAULT_LED_PIN); 140 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 141 | 142 | multicore_launch_core1(core1_entry); 143 | 144 | //SpinnerDummyTest(); 145 | //SpinnerSweepTest(); 146 | //SpinnerMFSKTest(); 147 | SpinnerRTTYTest(); 148 | //SpinnerMilliHertzTest(); 149 | //SpinnerWide4FSKTest(); 150 | //SpinnerGPSreferenceTest(); 151 | } 152 | 153 | /* This is the code of dedicated core. 154 | We deal with extremely precise real-time task. */ 155 | void core1_entry() 156 | { 157 | const uint32_t clkhz = PLL_SYS_MHZ * 1000000L; 158 | 159 | /* Initialize DCO */ 160 | assert_(0 == PioDCOInit(&DCO, 6, clkhz)); 161 | 162 | /* Run DCO. */ 163 | PioDCOStart(&DCO); 164 | 165 | /* Set initial freq. */ 166 | assert_(0 == PioDCOSetFreq(&DCO, GEN_FRQ_HZ, 0u)); 167 | 168 | /* Run the main DCO algorithm. It spins forever. */ 169 | PioDCOWorker2(&DCO); 170 | } 171 | 172 | void RAM (SpinnerMFSKTest)(void) 173 | { 174 | uint32_t rndval = 77777777; 175 | for(;;) 176 | { 177 | /* This example sets new RND frequency every ~250 ms. 178 | Frequency shift is 5 Hz for each step. 179 | */ 180 | PioDCOSetFreq(&DCO, GEN_FRQ_HZ - 5*(rndval & 7), 0u); 181 | 182 | /* LED signal */ 183 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 184 | sleep_ms(250); 185 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 186 | sleep_ms(250); 187 | 188 | PRN32(&rndval); 189 | } 190 | } 191 | 192 | void RAM (SpinnerSweepTest)(void) 193 | { 194 | int i = 0; 195 | for(;;) 196 | { 197 | /* This example sets new frequency every ~250 ms. 198 | Frequency shift is 5 Hz for each step. 199 | */ 200 | PioDCOSetFreq(&DCO, GEN_FRQ_HZ - 5*i, 0u); 201 | 202 | /* LED signal */ 203 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 204 | sleep_ms(500); 205 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 206 | sleep_ms(500); 207 | 208 | /* Return to initial freq after 20 steps (100 Hz). */ 209 | if(++i == 20) 210 | i = 0; 211 | } 212 | } 213 | 214 | void RAM (SpinnerRTTYTest)(void) 215 | { 216 | int32_t df = 170; /* 170 Hz freq diff. */ 217 | uint32_t rndval = 77777777; 218 | for(;;) 219 | { 220 | /* This example sets new PRN frequency every ~22 ms. 221 | Note: You should use precise timing while building a real transmitter. 222 | */ 223 | PioDCOSetFreq(&DCO, GEN_FRQ_HZ - df*(rndval & 1), 0u); 224 | 225 | /* LED signal */ 226 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 227 | sleep_ms(22); 228 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 229 | sleep_ms(22); 230 | 231 | PRN32(&rndval); 232 | } 233 | } 234 | 235 | void RAM (SpinnerMilliHertzTest)(void) 236 | { 237 | int i = 0; 238 | for(;;) 239 | { 240 | /* This example sets new frequency every ~1s. 241 | Frequency shift is 0.99 Hz for each step. 242 | */ 243 | PioDCOSetFreq(&DCO, GEN_FRQ_HZ, 990*(++i&1)); 244 | 245 | /* LED signal */ 246 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 247 | sleep_ms(1000); 248 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 249 | sleep_ms(1000); 250 | } 251 | } 252 | 253 | void RAM (SpinnerWide4FSKTest)(void) 254 | { 255 | int32_t df = 100; /* 100 Hz freq diff * 4 = 400 Hz. */ 256 | uint32_t rndval = 77777777; 257 | for(;;) 258 | { 259 | /* This example sets new PRN frequency every ~20 ms. 260 | Note: You should use precise timing while building a real transmitter. 261 | */ 262 | PioDCOSetFreq(&DCO, GEN_FRQ_HZ - df*(rndval & 3), 0u); 263 | 264 | /* LED signal */ 265 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 266 | sleep_ms(20); 267 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 268 | sleep_ms(20); 269 | 270 | PRN32(&rndval); 271 | } 272 | } 273 | 274 | /* This example sets the OUT frequency to 5.555 MHz. 275 | Next every ~1 sec the shift of the OUT frequency relative to GPS 276 | reference is calculated and the OUT frequency is corrected. 277 | The example is working only when GPS receiver provides an 278 | accurate PPS output (pulse per second). If no such option, 279 | the correction parameter is zero. 280 | */ 281 | void RAM (SpinnerGPSreferenceTest)(void) 282 | { 283 | const uint32_t ku32_freq = 5555000UL; 284 | const int kigps_pps_pin = 2; 285 | 286 | int32_t i32_compensation_millis = 0; 287 | 288 | GPStimeContext *pGPS = GPStimeInit(0, 9600, kigps_pps_pin); 289 | assert_(pGPS); 290 | DCO._pGPStime = pGPS; 291 | int tick = 0; 292 | for(;;) 293 | { 294 | PioDCOSetFreq(&DCO, ku32_freq, -2*i32_compensation_millis); 295 | 296 | /* LED signal */ 297 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 298 | sleep_ms(2500); 299 | 300 | i32_compensation_millis = 301 | PioDCOGetFreqShiftMilliHertz(&DCO, (uint64_t)(ku32_freq * 1000LL)); 302 | 303 | gpio_put(PICO_DEFAULT_LED_PIN, 0); 304 | sleep_ms(2500); 305 | 306 | if(0 == ++tick % 6) 307 | { 308 | //stdio_set_driver_enabled(&stdio_uart, false); 309 | GPStimeDump(&(pGPS->_time_data)); 310 | //stdio_set_driver_enabled(&stdio_uart, true); 311 | } 312 | } 313 | } 314 | --------------------------------------------------------------------------------