Pi Pico Rx - 公開プログラムnco_pioを使い、Raspberry Pi Pico (RP2040) PIO制御による90度位相差クロック530~1600KHzの局発I Q→直交ミキサ→Pi Pico:ZEPエンジニアリング株式会社公開【5_PicoStackSDR_program.zip】
Pico SDR IQ.c -AM検波処理【sqrt(I_signal*I_signal+Q_signal*Q_signal)】による受信テストを行いました(デジタルフィルタ未使用)
※周波数ステップ100Hz以降は周波数表示・出力周波数共にステップしますが周波数表示と出力周波数に誤差が生じます。AM放送受信には影響が少ないです(通常1KHzでステップ操作)
動画
参考サイト
●101 Things
Pi Pico Rx
●Raspberry Pi PicoとMOSFET ③ PWMのAPI
回路図
オーディオアンプボード
アマゾン購入●アンプモジュール LM386 DC3-12V
【https://www.amazon.co.jp/dp/B07ND6X469?ref=ppx_yo2ov_dt_b_fed_asin_title】
90度位相差クロック測定
![]() |
上ーGPIO0 下ーGPIO1 (530KHz) |
※ 4/17更新 u8g2libライブラリを使用してOLED表示
プログラム Arduino IDE【ボード:Raspberry Pi Pico】
#include <Wire.h>
#include <Arduino.h>
#include <U8g2lib.h>
#include <hardware/pio.h>
#include "nco.h"
//pico sdk libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/irq.h"
#include "hardware/adc.h"
#include "hardware/pwm.h"
int audio_slice; //PWM slice for audio
int audio_gain = 3; //audio gain
float result;
// OLED設定
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
//エンコーダ設定
#define ENC_A 13
#define ENC_B 14
volatile uint8_t old_value = 0x11;
volatile uint8_t value = 0;
volatile uint8_t D;
volatile int8_t count = 0;
//STEP関係の定義
#define SW_STEP 15
uint16_t STEP = 1000; //STEP 初期値
//VFO関係の定義
const uint32_t LOW_FREQ = 530000; //下限周波数
const uint32_t HI_FREQ = 1600000; //上限周波数
uint32_t FREQ = 1241000; //VFO周波数初期値
uint32_t FREQ_OLD = FREQ; //周波数の前の値
int16_t df = 130; //周波数補正値(Hz)
String freqt = String(FREQ);
String fH_old = "";
String fM_old = "";
String fL_old = "";
//ROtary encoder 割込みサービスルーチン
void rotary(){
value = (digitalRead(ENC_B)<<1) | digitalRead(ENC_A);
if(old_value != value){
D = ((old_value << 1) ^ value) & 3;
if(D < 2){
count += 1;
}else{
count -= 1;
}
if(count >= 4){
FREQ += STEP; //STEP周波数
count = 0;
}else if(count <= -4){
FREQ -= STEP; //STEP周波数
count = 0;
}
FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ); //VFOの下限と上限を超えないように
old_value = value;
}
}
//周波数表示
void Freq_Disp(long frequency){
freqt = String(frequency);
String fH = freqt.substring(0,1);
String fM = freqt.substring(1,4);
String fL = freqt.substring(4,6);
//STEP表示
u8g2.clearBuffer(); // バッファのクリア
u8g2.setFont(u8g2_font_timB10_tf);
u8g2.setCursor(10,15);
if (STEP == 10)u8g2.print("10Hz");
if (STEP == 100)u8g2.print("100Hz");
if (STEP == 1000)u8g2.print("1KHz");
if (STEP == 10000)u8g2.print("10KHz");
u8g2.sendBuffer();
//MHzの表示
u8g2.setFont(u8g2_font_timB24_tf);
u8g2.setCursor(10,50);
u8g2.print(fH);
fH_old = fH;
//KHzの表示
u8g2.setCursor(35,50);
u8g2.print(fM);
fM_old = fM;
//Hzの表示
u8g2.setCursor(90,50);
u8g2.print(fL);
u8g2.sendBuffer();
fL_old = fL;
}
//STEP表示
void Step_Disp(){
u8g2.clearBuffer(); // バッファのクリア
u8g2.setFont(u8g2_font_ncenR10_tf);
u8g2.setCursor(10,15);
if (STEP == 10)u8g2.print("10Hz");
if (STEP == 100)u8g2.print("100Hz");
if (STEP == 1000)u8g2.print("1KHz");
if (STEP == 10000)u8g2.print("10KHz");
u8g2.sendBuffer();
Freq_Disp(FREQ);
}
//STEP切り替え
void Set_Step(){
if (STEP == 10){
STEP = 10000;
}else{
STEP /= 10;
}
delay(10);
Step_Disp();
while(digitalRead(SW_STEP) == LOW){
delay(10);
}
}
// clock関連
float divider;
float adjusted_frequency;
float system_clock_frequency = 133000000;//システムクロック
void setup() {
Wire.setSDA(16); //SDA
Wire.setSCL(17); //SCL
Wire.begin();
//ロータリーエンコーダとSTEP使用ピンの設定とプルアップ
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
//ロータリーエンコーダ割込み設定
attachInterrupt(ENC_A, rotary, CHANGE);
attachInterrupt(ENC_B, rotary, CHANGE);
// OLED 初期表示
u8g2.begin (); // OLEDの初期化
u8g2.setFlipMode(1); // 画面の向きを設定、数字を変えると向きが変わる
u8g2.clearBuffer(); // バッファのクリア
u8g2.setFont(u8g2_font_timB18_tf);
u8g2.drawStr(0, 30, "PIO VFO");
u8g2.sendBuffer(); // バッファを転送して表示
delay(2000);
u8g2.clearBuffer(); // バッファのクリア
Step_Disp();
Freq_Disp(FREQ);
//PIO init
PIO pio = pio0;
adjusted_frequency = FREQ;
uint sm;
uint offset = pio_add_program(pio, &nco_program); //offset変数 ← アセンブラのアドレス
nco_program_init(pio, sm, offset); //PIOの初期化
divider = system_clock_frequency/(4*adjusted_frequency); //クロック周波数分周
pio_sm_set_clkdiv(pio, sm, divider); //クロック分割を設定する
delay(50);
//ADC init
adc_init();
adc_gpio_init(26); //GPIO 26
adc_select_input(0); //GPIO26 is ADC channel 0
adc_set_clkdiv(0); //ADC clock: 48MHz
adc_gpio_init(27); //GPIO 27
adc_select_input(1); //GPIO27 is ADC channel 1
adc_set_clkdiv(0); //ADC clock: 48MHz
//PWM init for Audio (GPIO16, PWMチャネル0A)
gpio_set_function(10, GPIO_FUNC_PWM); //GP10をPWM出力設定
audio_slice = pwm_gpio_to_slice_num(10); //GP10 PWMスライス宣言
pwm_set_enabled(audio_slice, true); //PWM出力有効
pwm_set_wrap(audio_slice, 255); //PWMの分解能力を255に設定
pwm_set_chan_level(audio_slice, PWM_CHAN_A, 128);//GP10-5ACh Duty設定
}
void loop() {
if(digitalRead(SW_STEP) == LOW)Set_Step();
if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、周波数を変更
Freq_Disp(FREQ);
FREQ_OLD = FREQ; //変更された周波数を保存
adjusted_frequency = FREQ;
divider = system_clock_frequency/(4*adjusted_frequency); //クロック周波数分周
PIO pio = pio0;
uint sm;
pio_sm_set_clkdiv(pio, sm, divider); //クロック分周を設定する
delay(50);
}
//read ADC (In-phase channel)
adc_select_input(0);
int adc_data_I = adc_read(); //0 to 4095
//read ADC (Quadrature channel)
adc_select_input(1);
int adc_data_Q = adc_read(); //0 to 4095
//remove the offset and normalize the values
float I_signal = (float)(adc_data_I-2048)/2048.0f; //-1.0f to 1.0f
float Q_signal = (float)(adc_data_Q-2048)/2048.0f; //-1.0f to 1.0f
float output = sqrt(I_signal*I_signal + Q_signal*Q_signal); //AM検波
//復調信号出力設定
pwm_set_chan_level(audio_slice, PWM_CHAN_A, (int)(256.0f * output * (float)audio_gain * 0.1f) );
}
nco.h
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// --- //
// nco //
// --- //
#define nco_wrap_target 0
#define nco_wrap 3
static const uint16_t nco_program_instructions[] = {
// .wrap_target
0xe000, // 0: set pins, 0
0xe001, // 1: set pins, 1
0xe003, // 2: set pins, 3
0xe002, // 3: set pins, 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program nco_program = {
.instructions = nco_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config nco_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + nco_wrap_target, offset + nco_wrap);
return c;
}
static inline void nco_program_init(PIO pio, uint sm, uint offset) {
// PIOステートマシンコンフィグのデフォルト値を取得
pio_sm_config c = nco_program_get_default_config(offset);
// Map the state machine's OUT pin group to one pin, namely the `pin`
// parameter to this function.
//ピンと、ピンの数を指定
sm_config_set_set_pins(&c, 0, 2);
// Set this pin's GPIO function (connect PIO to the pad)
//GPIOをPIO 0に割り当てる
pio_gpio_init(pio, 0);
//GPIOをPIO 1に割り当てる
pio_gpio_init(pio, 1);
//出力電流2mA
gpio_set_drive_strength(0, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(1, GPIO_DRIVE_STRENGTH_2MA);
//スルーレート 遅い
gpio_set_slew_rate(0, GPIO_SLEW_RATE_SLOW);
gpio_set_slew_rate(1, GPIO_SLEW_RATE_SLOW);
// Set the pin direction to output at the PIO
//PIOでピンの方向を出力に設定する
pio_sm_set_consecutive_pindirs(pio, sm, 0, 2, true);
//set pio divider
//クロック周波数分周 1
sm_config_set_clkdiv(&c,1);
// Load our configuration, and jump to the start of the program
//プログラムを読込、先頭から実行
pio_sm_init(pio, sm, offset, &c);
// Set the state machine running
//PIOステートマシンを有効にする
pio_sm_set_enabled(pio, sm, true);
}
#endif
0 件のコメント:
コメントを投稿