2024年12月1日日曜日

Pico Stack SDR (Si5351A) TS-830 IF ソフトウェア検波 受信テスト

 
TS-830 第2 IF 455KHz出力をPico Stack SDR (Si5351A)のミキサに接続。Si5351A局発を 455KHz固定しTS-830側で周波数選択を行いソフトウェア検波の受信テストを行いました。 
●ZEPエンジニアリング株式会社公開
【Pico SDR IQ.uf2】AM検波プログラムにLSB・USB・CWを追加


動画




参考サイト
●ZEPエンジニアリング株式会社 
●JR3XNW’s blog
●ソフトウェアラジオの自作!dsPICで計算した音を聴く
●Tj Lab ESP32-S3 AF信号処理ボード(全モード復調)

ブロック図





















回路図



































Arduino IDE
【ボード:Raspberry Pi Pico】

プログラム 
#include <Wire.h>
#include <LiquidCrystal.h>
//LiquidCrystal(rs, enable, d4, d5, d6, d7)
LiquidCrystal lcd(4, 5, 0, 1, 2, 3);

//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
const int pushSwitchPin = 10; //Reception mode change push switch (LSB→USB→CW→AM)
//Si5351A関係の定義
#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177
const uint32_t XtalFreq = 25000000;
uint32_t divider;
uint32_t PllFreq;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t l;
float f;
uint32_t P1;
uint32_t P2;
uint32_t P3;
float result;

//Rotary endoder関係の定義
#define ENC_A 17
#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 11
uint16_t STEP = 10000; //STEP 初期値

//VFO関係の定義
const uint32_t LOW_FREQ = 1820000; //下限周波数
const uint32_t HI_FREQ = 1820000; //上限周波数
uint32_t FREQ = 1820000; //VFO周波数初期値
uint32_t FREQ_OLD = FREQ; //周波数の前の値
int16_t df = 130; //周波数補正値(Hz)
String freqt = String(FREQ);
String freqtB = 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;
    count = 0;
  }else if(count <= -4){
    FREQ -= STEP;
    count = 0;
  }
  FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ); //VFOの下限と上限を超えないように
  old_value = value;
  }
}

//レジスタに1バイトデータを書き込む。
void Si5351_write(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

//Si5351Aの初期化
void Si5351_init(){
  Si5351_write(OUTPUT_CTRL,0xFF); //Disable Output
  Si5351_write(CLK0_CTRL,0x80); //CLOCK0 Power down
  Si5351_write(CLK1_CTRL,0x80); //CLOCK1 Power down
  Si5351_write(CLK2_CTRL,0x80); //CLOCK2 Power down 
  Si5351_write(XTAL_LOAD_C,0x92); //Crystal Load Capasitance=8pF
  Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
  Si5351_write(CLK0_CTRL,0x4F); //CLOCK0 Power up
  Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}

void VFO_Set(uint32_t freqency){
  
//PLLのセット
  divider = 900000000 / freqency;
  if (divider % 2) divider--;
  PllFreq = divider * freqency;
  mult = PllFreq / XtalFreq;
  l = PllFreq % XtalFreq;
  f = l;
  f *= 1048575;
  f /= XtalFreq;
  num = f;
  denom = 1048575;
  P1 = (uint32_t)(128 * ((float)num /(float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(128 * num -denom * P2);
  P3=denom;
  Parameter_write(MSNA_ADDR,P1,P2,P3);

//MultiSynth(分周器)のセット
  P1 = 128 * divider - 512;
  P2 = 0;
  P3 = 1;
  Parameter_write(MS0_ADDR,P1,P2,P3); 
}

//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(uint8_t REG_ADDR,uint32_t Pa1,uint32_t Pa2,uint32_t Pa3){
  Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
  Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
  Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
  Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
  Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF)); 
}

//周波数表示
void Freq_Disp(long frequency){
  freqt = String(frequency);
  freqtB = String(frequency/4); 
  String fH = freqt.substring(0,1);
  String fM = freqt.substring(1,4);
  String fL = freqt.substring(4,6); 
  String fHB = freqtB.substring(0,1);
  String fMB = freqtB.substring(1,4);
  String fLB = freqtB.substring(4,6);
  lcd.clear();
  lcd.setCursor(3,0);  
  if (STEP == 10)lcd.print("10Hz");
  if (STEP == 100)lcd.print("100Hz");
  if (STEP == 1000)lcd.print("1KHz");
  if (STEP == 10000)lcd.print("10KHz"); 
//MHzの表示 
  lcd.setCursor(1,1); 
  lcd.print(fH);
  lcd.setCursor(2,1); 
  lcd.print("."); 
  lcd.setCursor(9,1);
  lcd.print(fHB);     
  fH_old = fH;
//KHzの表示    
  lcd.setCursor(3,1); 
  lcd.print(fM);  
  lcd.setCursor(10,1);  
  lcd.print(fMB);   
  fM_old = fM;
//Hzの表示   
  lcd.setCursor(6,1); 
  lcd.print(fL); 
  lcd.setCursor(13,1); 
  lcd.print(fLB);   
  fL_old = fL;  
}

//STEP表示
void Step_Disp(){
  lcd.clear();
  lcd.setCursor(1, 0);     
  if (STEP == 10)lcd.print("10Hz");
  if (STEP == 100)lcd.print("100Hz");
  if (STEP == 1000)lcd.print("1KHz");
  if (STEP == 10000)lcd.print("10KHz");
  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);
  }
}

enum DemodulationMode { LSB, USB, CW, AM };
DemodulationMode currentMode = LSB;

   
    
//モード表示
void Mode_Disp(){
 lcd.setCursor(13, 0);
 if (currentMode == 0)lcd.print("LSB");
 if (currentMode == 1)lcd.print("USB");
 if (currentMode == 2)lcd.print("CW ");
 if (currentMode == 3)lcd.print("AM "); 
}
void setup(){ 
  Serial.begin( 9600 );
  Wire.setSDA(12);  //SDA 
  Wire.setSCL(13);  //SCL   
  Wire.begin();   
  pinMode(ENC_A, INPUT_PULLUP); //ロータリーエンコーダ 
  pinMode(ENC_B, INPUT_PULLUP);
  pinMode(SW_STEP, INPUT);    
  pinMode(25, OUTPUT);  // pico built-in LED 
  pinMode(pushSwitchPin, INPUT_PULLUP);
  attachInterrupt(ENC_A, rotary, CHANGE);//ロータリーエンコーダ割込み設定
  attachInterrupt(ENC_B, rotary, CHANGE);  
  lcd.begin( 16, 2 ); 
  lcd.clear();  
  Si5351_init(); //Si5351Aの初期化  
  Step_Disp();  
  VFO_Set(FREQ + df);
  Freq_Disp(FREQ);
  Mode_Disp();

//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 (GPIO15, PWM7B)
  gpio_set_function(15, GPIO_FUNC_PWM); //use GPIO15 as PWM
  audio_slice = pwm_gpio_to_slice_num(15); //get the slice number
  pwm_set_enabled(audio_slice, true); //enable the PWM channel
  pwm_set_wrap(audio_slice, 255); //PWM frequency divider setting 
  pwm_set_chan_level(audio_slice, PWM_CHAN_B, 128);  

 
  
}

void loop(){  
  digitalWrite(25, HIGH);  // Built-in LED lights up during sampling
 //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 40965 
    
    //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
    

  if (digitalRead(pushSwitchPin) == LOW) {
    delay(50); // Debounce delay
    currentMode = static_cast<DemodulationMode>((currentMode + 1) % 4);
    while (digitalRead(pushSwitchPin) == LOW); // Wait for switch release
    delay(50); // Debounce delay 
    Mode_Disp();      
  }   
  switch (currentMode) {
    case LSB:        
      result = I_signal - Q_signal;
      break;
    case USB:       
      result = I_signal + Q_signal;
      break;
    case CW:       
      result = I_signal * Q_signal;
      break;
    case AM:
      result = sqrt(I_signal*I_signal + Q_signal*Q_signal); 
      break;
  }

   //PWM output (you can change the value "0.1f" to adjust suitable volume.)
    pwm_set_chan_level(audio_slice, PWM_CHAN_B, (int)(256.0f * result * (float)audio_gain * 0.1f) ); 

  if(digitalRead(SW_STEP) == LOW)Set_Step();
  if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、Si5351Aの周波数を変更
    VFO_Set(FREQ + df);
    Freq_Disp(FREQ);
    FREQ_OLD = FREQ; //変更された周波数を保存
    Mode_Disp();    
  } 
}

0 件のコメント:

コメントを投稿

Pi Pico Rx - SDR試作 7MHz SSB CW受信

Pi Pico Rx(0-30MHz,CW/SSB/AM/FM)シンプルSDRを試作して、注目したPico PIOアッセンブルによるIQ局発と直交ミキサの構成で7MHz SSB CWの受信を行いました( FMは29MHzFMモードで受信確認) 同ブログ関連記事 【 Pi Pico...