Initial release.

gpstest
roman 2023-11-11 01:44:13 +03:00
rodzic cfcb7ba717
commit ed69251907
5 zmienionych plików z 166 dodań i 52 usunięć

Wyświetl plik

@ -34,7 +34,6 @@ pico_generate_pio_header(pico-DCO-test ${CMAKE_CURRENT_LIST_DIR}/piodco/dco.pio)
target_sources(pico-DCO-test PUBLIC
${CMAKE_CURRENT_LIST_DIR}/lib/assert.c
${CMAKE_CURRENT_LIST_DIR}/piodco/piodco.c
${CMAKE_CURRENT_LIST_DIR}/dcoisr/dcoisr.c
${CMAKE_CURRENT_LIST_DIR}/test.c
)
@ -55,6 +54,7 @@ target_link_libraries(
pico-DCO-test
pico_stdlib
pico_sync
pico_multicore
hardware_timer
hardware_clocks
hardware_pio

Wyświetl plik

@ -1,2 +1,51 @@
# pico-DCO
Digital controlled oscillator for raspberry pi pico (DC to ~30MHz frequency band)
# Digital controlled radio frequency oscillator for Raspberry Pi Pico
The library for Raspberry Pi Pico includes the headers and source code and all
necessary build files to build a custom application which turns pico into
precise PLL digital frequency oscillator of the portion of HF radio spectrum
1.1 to 9.4MHz with high resolution.
# Precise frequency resolution
The library provides 1 Hz frequency resolution in initialize function. This
resolution can be easily increased to 23 millihertz, which is limited by
24-bit register which is used in algorithm.
Currently the upper freq. limit is about 9.8 MHz and it is achieved only using
pi pico overclocking to 270MHz.
# Phased locked loop in C
The DCO uses phase locked loop principle programmed in C.
# Integer arithmetic
The DCO does *NOT* use any floating point operations - all time-critical
instructions run in 1 CPU cycle.
# Radio transmitters
Owing to the meager frequency step, it is possible to use 3, 5, or 7th harmonics
of generated frequency. Such solution completely cover all HF and low band up to
65.8 MHz.
# For what?
This is an experimental project of amateur radio class and it is devised by me on
the free will base in order to experiment with QRP narrowband digital modes.
# Feedback
I gracefully appreciate any thoughts or comments on that matter.
# Quick-start
1. Install Raspberry Pi Pico SDK. Configure environment variables. Test whether
it is built successfully.
2. git clone this repository. cd pico-DCO ; ./build.sh
Check whether output file ./build/pico-DCO.uf2 appears.
3. Prepare the surrogate antenna (if you possess an SSB receiver) or pin-out
for an oscilloscope or a spectrum analyser. The default output pin is GPIO6.
4. Load the .uf2 file (2) into the Pico.
5. Initialy the running frequency is 9.4 MHz.
6. Set any other frequency ranging from 1.1 to 9.4 MHz by #define GEN_FRQ_HZ and build the project.
7. Provide the feedback by clicking like on the github page of the project.

Wyświetl plik

@ -12,7 +12,7 @@
// DESCRIPTION
//
// The oscillator provides precise generation of any frequency ranging
// from 1.5 to 9.8 MHz with tenth's of millihertz resolution (please note that
// from 1.1 to 9.4 MHz with tenth's of millihertz resolution (please note that
// this is relative resolution owing to the fact that the absolute accuracy of
// onboard crystal of pi pico is limited).
// The DCO uses phase locked loop principle programmed in C.
@ -66,6 +66,8 @@
#include "build/dco.pio.h"
int32_t si32precise_cycles;
/// @brief Initializes DCO context and prepares PIO hardware.
/// @param pdco Ptr to DCO context.
/// @param gpio The GPIO of DCO output.
@ -105,16 +107,15 @@ int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz)
assert_(pdco);
assert(pdco->_clkfreq_hz);
if(pdco->_clkfreq_hz / ui32_frq_hz < 27)
{
//return -1;
}
ui32_frq_hz <<= 1;
/* RPix: Calculate an accurate value of phase increment of the freq
per 1 tick of CPU clock, here 2^24 is scaling coefficient. */
pdco->_frq_cycles_per_pi = (int32_t)(((int64_t)pdco->_clkfreq_hz * (int64_t)(1<<24)
+ (ui32_frq_hz>>1)) / (int64_t)ui32_frq_hz);
si32precise_cycles = pdco->_frq_cycles_per_pi;
return 0;
}
@ -138,7 +139,6 @@ void PioDCOStop(PioDco *pdco)
/// @brief the dedicated pi pico core.
/// @param pDCO Ptr to DCO context.
/// @return No return. It spins forever.
static int32_t si32precise_cycles;
void RAM (PioDCOWorker)(PioDco *pDCO)
{
assert_(pDCO);
@ -150,9 +150,10 @@ void RAM (PioDCOWorker)(PioDco *pDCO)
register uint32_t *preg32 = pDCO->_ui32_pioreg;
register uint8_t *pu8reg = (uint8_t *)preg32;
si32precise_cycles = pDCO->_frq_cycles_per_pi;
//si32precise_cycles = pDCO->_frq_cycles_per_pi;
for(;;)
{
const register int32_t i32reg = si32precise_cycles;
/* RPix: Load the next precise value of CPU CLK cycles per DCO cycle,
scaled by 2^24. It yields about 24 millihertz resolution at @10MHz
DCO frequency. */
@ -160,13 +161,13 @@ void RAM (PioDCOWorker)(PioDco *pDCO)
{
/* RPix: Calculate the integer number of CPU CLK cycles per next
DCO cycle, corrected by accumulated error (feedback of the PLL). */
const int32_t i32wc = iSAR(si32precise_cycles - i32acc_error + (1<<23), 24);
const int32_t i32wc = iSAR(i32reg - i32acc_error + (1<<23), 24);
/* RPix: Calculate the difference btw calculated value scaled to
`fine` state and precise value of DCO cycles per CPU CLK cycle.
This forms a phase locked loop which provides precise freq
on long run. */
i32acc_error += (i32wc<<24) - si32precise_cycles;
i32acc_error += (i32wc<<24) - i32reg;
/* RPix: Set PIO array contents corrected by pio program delay
of N CPU CLK cycles owing to pio asm instructions. */

Wyświetl plik

@ -12,7 +12,7 @@
// DESCRIPTION
//
// The oscillator provides precise generation of any frequency ranging
// from 1.5 to 9.8 MHz with tenth's of millihertz resolution (please note that
// from 1.1 to 9.4 MHz with tenth's of millihertz resolution (please note that
// this is relative resolution owing to the fact that the absolute accuracy of
// onboard crystal of pi pico is limited).
// The DCO uses phase locked loop principle programmed in C.

144
test.c
Wyświetl plik

@ -1,3 +1,68 @@
///////////////////////////////////////////////////////////////////////////////
//
// Roman Piksaykin [piksaykin@gmail.com], R2BDY
// https://www.qrz.com/db/r2bdy
//
///////////////////////////////////////////////////////////////////////////////
//
//
// test.c - Simple test of digital controlled radio freq oscillator based on PIO.
//
//
// DESCRIPTION
//
// The oscillator provides precise generation of any frequency ranging
// from 1.1 to 9.4 MHz with tenth's of millihertz resolution (please note that
// this is relative resolution owing to the fact that the absolute accuracy of
// onboard crystal of pi pico is limited).
// The DCO uses phase locked loop principle programmed in C.
// The DCO does *NOT* use any floating point operations - all time-critical
// instructions run in 1 CPU cycle.
// Currently the upper freq. limit is about 9.8 MHz and it is achieved only
// using pi pico overclocking to 270MHz.
// Owing to the meager frequency step, it is possible to use 3, 5, or 7th
// harmonics of generated frequency. Such solution completely cover all HF and
// low band up to about 66 MHz.
// This is an experimental project of amateur radio class and it is devised
// by me on the free will base in order to experiment with QRP narrowband
// digital modes.
// I gracefully appreciate any thoughts or comments on that matter.
//
// HOWTOSTART
// Set frequency by #define GEN_FRQ_HZ and build the project. The default
// output pin is GPIO6.
//
// PLATFORM
// Raspberry Pi pico.
//
// REVISION HISTORY
//
// Rev 0.1 05 Nov 2023
// Initial release.
//
// LICENCE
// MIT License (http://www.opensource.org/licenses/mit-license.php)
//
// Copyright (c) 2023 by Roman Piksaykin
//
// Permission is hereby granted, free of charge,to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction,including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <stdlib.h>
@ -6,67 +71,66 @@
#include "piodco/piodco.h"
#include "build/dco.pio.h"
#include "hardware/vreg.h"
#include "pico/multicore.h"
#include "./lib/assert.h"
#include "hwdefs.h"
#include "./dcoisr/dcoisr.h"
void __not_in_flash_func (WorkerCycle)(PioDco *pDCO, const int32_t i32frq_in_clk, uint32_t *preg32)
#define GEN_FRQ_HZ 9400000L
PioDco DCO;
/* This is the code of dedicated core.
We deal with extremely precise real-time task. */
void core1_entry()
{
register int32_t acc_phase_error = 0;
register uint8_t *preg8 = (uint8_t *)preg32;
register PIO pio = pDCO->_pio;
register uint sm = pDCO->_ism;
const uint32_t clkhz = PLL_SYS_MHZ * 1000000L;
const uint32_t freq_hz = GEN_FRQ_HZ;
pio_sm_set_enabled(pio, sm, true);
/* Initialize DCO */
assert_(0 == PioDCOInit(&DCO, 6, clkhz));
/* Run DCO. */
PioDCOStart(&DCO);
/* Set initial freq. */
assert_(0 == PioDCOSetFreq(&DCO, freq_hz));
/* Run the main DCO algorithm. It spins forever. */
PioDCOWorker(&DCO);
}
void RAM (Spinner)(void)
{
int i = 0;
for(;;)
{
preg8 = (uint8_t *)preg32;
for(int i = 0; i < 32; ++i)
{
register const int32_t i32wc = (i32frq_in_clk - acc_phase_error + (1<<23)) >> 24;
acc_phase_error += (i32wc<<24) - i32frq_in_clk;
/* This example sets new frequency every ~500 ms.
Frequency shift is 5 Hz for each step.
*/
PioDCOSetFreq(&DCO, GEN_FRQ_HZ - 5*i);
*preg8++ = i32wc - PIOASM_DELAY_CYCLES;
}
/* LED signal */
gpio_put(PICO_DEFAULT_LED_PIN, 1);
sleep_ms(500);
gpio_put(PICO_DEFAULT_LED_PIN, 0);
sleep_ms(500);
dco_program_puts(pio, sm, preg32);
/* Return to initial freq after 20 steps (100 Hz). */
if(++i == 20)
i = 0;
}
}
int main()
{
PioDco DCO;
//DcoIsrContext ISRC;
const uint32_t clkhz = PLL_SYS_MHZ * 1000000L;
set_sys_clock_khz(clkhz / 1000L, true);
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
const uint32_t freq_hz = 2*10000000L;
multicore_launch_core1(core1_entry);
assert_(0 == PioDCOInit(&DCO, 6, clkhz));
PioDCOStart(&DCO);
assert_(0 == PioDCOSetFreq(&DCO, freq_hz));
PioDCOWorker(&DCO);
/*
//DcoIsrInit(&ISRC, &DCO);
uint32_t preg32[8] = {0};
uint8_t *preg8 = (uint8_t *)preg32;
//const uint64_t frqhz = 2*(3573000L);
//const uint64_t frqhz = 2*7074000L + 1;
const uint64_t frqhz = 2*10000000L;
//const uint64_t frqhz = 2*10136000L;
//const int64_t frqhz = 2*(14074000L);
const int64_t i64frq_in_clk = ((int64_t)clkhz * (int64_t)(1<<24) + (frqhz>>1)) / frqhz;
const int32_t i32frq_in_clk = i64frq_in_clk;
WorkerCycle(&DCO, i32frq_in_clk, preg32);
*/
Spinner();
}