Tj Lab「ESP32-S3 AF信号処理ボード 」プログラムを使用してI2SによるSSB受信テストを行いました。
Si5351A 7MHz VFO→直交ミキサ→PCM1808モジュール(ADC入力)→SSB復調【Lch_in[i] = rxbuf[j] - rxbuf[j+1]】→PCM1808モジュール(DAC出力)→スピーカーによる受信テスト(デジタルフィルタ未使用)
動画
参考サイト
回路図
ESP32-S3 開発ボード
アマゾン購入
5Vピン → 5V出ない(入力ピン)?
(赤い線材)三端子レギュレータから直接5Vを取出しPCM1808のアナログ電圧(5V)に供給。(自己責任で)
PCM5102A - 32ビットDAC
アマゾン購入
【SCK】ブリッジ(内部クロック)【H1L】(FLT) L側 ブリッジ
【H2L】(DEMP) L側 ブリッジ
【H3L】(XSMT) H側 ブリッジ
【H4L】(FMT) L側 ブリッジ
PCM1808 - 24ビットADC
アマゾン購入
回路不明
IC周辺にコンデンサー・抵抗がついているので外付け必要ない?
SCK ー MCLK(マスタークロック)プログラムで変更可能
●I2S・24ビット
FMT - GND
●スレーブ・モード
MD1 - GND
MD0 - GND
ボードマネージャ
追加ボードマネージャーURLを変更
【https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json】
【esp 32】
【バージョン3.2.0】インストール
ライブラリマネージャー
●ESP32-audioI2S-master
所定のlibrariesに貼付
【i2s】
【バージョン3.2.1】インストール
プログラム Arduino IDE【ボード:ESP32S3 Dev Module】
#include <Wire.h>
#include <Arduino.h>
#include <U8g2lib.h>
#include <driver/i2s.h>
#define fsample 48000
#define BLOCK_SAMPLES 64
//buffers
int rxbuf[BLOCK_SAMPLES*2], txbuf[BLOCK_SAMPLES*2];
float Lch_in[BLOCK_SAMPLES], Rch_in[BLOCK_SAMPLES];
float Lch_out[BLOCK_SAMPLES], Rch_out[BLOCK_SAMPLES];
#define I2C_SCL 5
#define I2C_SDA 4
#define SW_Mode 16
//Si5351A関係の定義
#define Si5351A_ADDR 0x60
#define OUTPUT_CTRL 3 //Output Enable Control
#define CLK0_CTRL 16 //CLK0 Control
#define CLK1_CTRL 17 //CLK1 Control
#define CLK2_CTRL 18 //CLK2 Control
#define MSNA_ADDR 26 //Multisynth NA Parameters
#define MSNB_ADDR 34 //Multisynth NB Parameters
#define MS0_ADDR 42 //Multisynth0 Parameters
#define MS1_ADDR 50 //Multisynth1 Parameters
#define MS2_ADDR 58 //Multisynth2 Parameters
#define CLK0_PHOFF 165 //CLK0 Initial Phase Offset
#define CLK1_PHOFF 166 //CLK1 Initial Phase Offset
#define PLL_RESET 177 //PLL Reset
#define XTAL_LOAD_C 183 //Crystal Internal Load Capacitance
uint32_t frequency;
const uint32_t XtalFreq = 25000000; //25MHz
uint32_t P1;
uint32_t P2;
uint32_t P3;
uint32_t PllFreq;
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t divider;
char PLL;
uint8_t PLL_ADDR;
uint8_t MS_ADDR;
// OLED設定
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
//Rotary endoder関係の定義
#define ENC_A 6
#define ENC_B 7
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 = 10000; //STEP 初期値
//VFO関係の定義
const uint32_t LOW_FREQ = 7000000; //下限周波数
const uint32_t HI_FREQ = 7200000; //上限周波数
uint32_t FREQ = LOW_FREQ; //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;
count = 0;
}else if(count <= -4){
FREQ -= STEP;
count = 0;
}
FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ); //VFOの下限と上限を超えないように
old_value = value;
}
}
//Si5351のレジスタに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); //Reg3 Disable Output
Si5351_write(CLK0_CTRL,0x80); //Reg16 CLOCK0 Power down
Si5351_write(CLK1_CTRL,0x80); //Reg17 CLOCK1 Power down
Si5351_write(CLK2_CTRL,0x80); //Reg18 CLOCK2 Power down
Si5351_write(XTAL_LOAD_C,0x92); //Reg183 Crystal Load Capasitance=8pF
Si5351_write(PLL_RESET,0xA0); //Reg177 Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0x4F); //Reg16 CLOCK0 Power up
Si5351_write(CLK1_CTRL,0x4F); //Reg17 CLOCK0 Power up
// Si5351_write(CLK2_CTRL,0x4F); //Reg18 CLOCK0 Power up
Si5351_write(OUTPUT_CTRL,0xFC); //Reg3 Enable CLOCK0,CLOCK1
}
//PLLの設定
void PLL_Set(char Pll,uint32_t Freq,uint32_t Div){
PllFreq = Div * Freq; //fvco =d * fout
mult = PllFreq / XtalFreq; //整数部 mult = a = fvco / fxtal
l = PllFreq % XtalFreq; // L = fvco % fxtal
f = l;
f *= 1048575;
f /= XtalFreq;
num = f;
denom = 1048575;
P1 = (uint32_t)(128 * ((float)num /(float)denom)); //128*(b/c)
P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512); //128*a + (128*(b/c))-512
P2 = (uint32_t)(128 * ((float)num / (float)denom)); //128*(b/c)
P2 = (uint32_t)(128 * num -denom * P2); //128*b–c*(128*(b/c))
P3=denom;
if (Pll == 'A')
{
PLL_ADDR = MSNA_ADDR;
}else
{
PLL_ADDR = MSNB_ADDR;
}
Parameter_write(PLL_ADDR,P1,P2,P3);
}
//MultiSynth(分周器)のセット
void MS_Set(uint8_t MS_No,uint32_t Div){
P1 = 128 * Div - 512;
P2 = 0;
P3 = 1;
switch(MS_No){
case 0:
MS_ADDR = MS0_ADDR;
break;
case 1:
MS_ADDR = MS1_ADDR;
break;
case 2:
MS_ADDR = MS2_ADDR;
break;
default:
MS_ADDR = MS0_ADDR;
}
Parameter_write(MS_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);
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_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();
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);
}
}
void si5351(){
//PLLAのセット 900MHz,CLK0=10MHz
//N(divider) = fvco(PLLA) / fout(CLK0)
frequency = FREQ;
divider = 900000000 / frequency; //divider = fvco / fout
if (divider % 2) divider--;
PLL_Set('A',frequency,divider);
//CLK0の設定
Si5351_write(CLK0_CTRL,0x4C); //Reg16 Sorce PLLA
MS_Set(0,divider);
Si5351_write(CLK0_PHOFF,0);//CLK0 delay 0
//CLK1の設定
Si5351_write(CLK1_CTRL,0x4C); //Reg17 Sorce PLLA
MS_Set(1,divider);
Si5351_write(CLK1_PHOFF,divider);//Reg166 delay T/4 (90degree)
Si5351_write(PLL_RESET,0xA0); //Reg177 PLLA and PLLB
}
void setup(){
//Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
pinMode(SW_Mode, INPUT_PULLUP);
//ロータリーエンコーダとSTEP使用ピンの設定とプルアップ
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(SW_STEP, INPUT_PULLUP);
//ロータリーエンコーダ割込み設定
attachInterrupt(ENC_A, rotary, CHANGE);
attachInterrupt(ENC_B, rotary, CHANGE);
// I2S setup ------------------------------------------------------------
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), //I2S作業モード
.sample_rate = fsample, //I2Sサンプルレート
.bits_per_sample = (i2s_bits_per_sample_t)32, //サンプルあたりのI2Sビット
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //I2Sチャネルフォーマット
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), //I2S通信フォーマット
.intr_alloc_flags = 0, //割り込みを割り当てるために使用されるフラグ
.dma_buf_count = 6, //I2S DMAバッファ数
.dma_buf_len = BLOCK_SAMPLES*4, //I2S DMAバッファ長
.use_apll = false, //2SはAPLLをメインI2Sクロックとして使用し、正確なクロックを取得
.tx_desc_auto_clear = true, //データが利用できない場合にノイズを回避
.fixed_mclk = 0, //固定MCLK出力を使用するI2S
};
i2s_driver_install( I2S_NUM_0, &i2s_config, 0, NULL); //I2S ドライバーをインストールして起動
i2s_pin_config_t pin_config = {
.bck_io_num = 42, //BCK
.ws_io_num = 2, //LRC,LCK
.data_out_num = 41, //DIN
.data_in_num = 1 //OUT
};
i2s_set_pin( I2S_NUM_0, &pin_config);
// OLED 初期表示
u8g2.begin (); // OLEDの初期化
u8g2.setFlipMode(0); // 画面の向きを設定、数字を変えると向きが変わる
u8g2.clearBuffer(); // バッファのクリア
u8g2.setFont(u8g2_font_timB18_tf);
u8g2.drawStr(0, 30, "ESP SDR");
u8g2.sendBuffer(); // バッファを転送して表示
delay(2000);
u8g2.clearBuffer(); // バッファのクリア
Step_Disp();
Freq_Disp(FREQ);
//Si5351Aの初期化
Si5351_init();
si5351();
}
void loop(){
if(digitalRead(SW_STEP) == LOW)Set_Step();
if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、Si5351Aの周波数を変更
si5351();
Freq_Disp(FREQ);
FREQ_OLD = FREQ; //変更された周波数を保存
}
size_t readsize = 0;
//Input I2S DMA 受信バッファからデータを読み取り
esp_err_t res = i2s_read(I2S_NUM_0, &rxbuf[0], BLOCK_SAMPLES*2*4, &readsize, portMAX_DELAY);
if (res == ESP_OK && readsize==BLOCK_SAMPLES*2*4) {
int j=0;
for (int i=0; i<BLOCK_SAMPLES; i++) {
Lch_in[i] = (float) rxbuf[j];
Rch_in[i] = (float) rxbuf[j+1];
Lch_in[i] = rxbuf[j] - rxbuf[j+1]; //SSB検波
j+=2;
}
//-------Signal process -------------------------------
for (int i=0; i<BLOCK_SAMPLES; i++) {
//L R 同じ信号
Lch_out[i] = Lch_in[i];
Rch_out[i] = Lch_in[i];
}
//------------------------------------------------------
//Output
j=0;
for (int i=0; i<BLOCK_SAMPLES; i++) {
txbuf[j] = (int) Lch_out[i];
txbuf[j+1] = (int) Rch_out[i];
j+=2;
}
//I2S DMA送信バッファにデータを書き込み
i2s_write( I2S_NUM_0, &txbuf[0], BLOCK_SAMPLES*2*4, &readsize, portMAX_DELAY);
}
}

.png)
.png)
0 件のコメント:
コメントを投稿