Skip to content

Instantly share code, notes, and snippets.

@xand79
Forked from NT7S/Si5351A_PSK.ino
Last active April 23, 2023 19:56
Show Gist options
  • Save xand79/2765c7f69804ad036e7260f7819c57d3 to your computer and use it in GitHub Desktop.
Save xand79/2765c7f69804ad036e7260f7819c57d3 to your computer and use it in GitHub Desktop.
Generate PSK31 with an Si5351A
/*
* Эта версия передатчика читает символы из последовательного порта и передаёт построчно.
* BPSK31/63 выбирается в переменной.
* Частота формируется биениями между выходами 0 и 1, в схеме нужен смеситель для сложения:
* выходы соединяются резистором 100 Ом, за ним трансформатор на ферритовом кольце.
*/
#include "si5351.h"
#include "Wire.h"
#include "varicode.h"
Si5351 si5351;
volatile int index = 0;
volatile uint16_t symbol;
volatile int psk_bit = 0;
volatile int zero_count = 0;
volatile int set_vfo = 0;
#define BPSK31_baud 3125;
#define BPSK63_baud 6250;
unsigned int baud_rate = BPSK31_baud //выбор режима модуляции
uint64_t freq = 1014400000ULL; //частота передачи
String mystring = ""; //передаваемая строка
int psk_bit_prev = 0; //предыдущий переданный бит
unsigned int Nch = 0; //номер передаваемого символа строки
unsigned long period = 0; //время между передачами битов
boolean TXed = 0; //признак конца передачи
unsigned int led = 13; //индикатор модуляции
unsigned int prm = 7; //переключатель антенна-приёмник
unsigned int prd = 8; //переключатель антенна-передатчик
int32_t cal_factor = 10; //90680 готовый китайский модуль из моего анализатора;
void setup() {
Serial.begin(115200);
//инициализация синтезатора
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); //8 пикофарад нагрузки на кварц
delay(10);
//зададим нагрузку поменьше из 2/4/6/8 мА, чтобы увеличить время нарастания-спада (8 мА = 1 нс)
//и уменьшить дрожание, то есть jitter; но не сильно мало, чтобы хорошо работал смеситель
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_4MA);
si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_4MA);
//коррекция в зависимости от фактически установленного кварца
si5351.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
//разрешаем дробные делители
si5351.set_clock_fanout(SI5351_FANOUT_XO, 1);
//включаем ФАПЧ А на частоте 870 000 000
si5351.set_pll(87000000000ULL, SI5351_PLLA);
//задействуем ФАПЧ А на выходах 0 и 1
si5351.set_ms_source(SI5351_CLK0, SI5351_PLLA);
si5351.set_ms_source(SI5351_CLK1, SI5351_PLLA);
//изначально ставим передачу в нули, чтобы приёмник лучше синхронизировался в начале передачи
si5351.set_freq(freq + (baud_rate / 2), SI5351_CLK0);
si5351.set_freq(freq - (baud_rate / 2), SI5351_CLK1);
//устанавливаем фазу в 0
si5351.set_phase(SI5351_CLK0, 0);
si5351.set_phase(SI5351_CLK1, 0);
//сброс ФАПЧ
si5351.si5351_write(SI5351_PLL_RESET, 0xAC);
//загружаем первый символ согласно варикоду
symbol = varicode[0]; //mystring.charAt(index)];
//выключаем все выходы
si5351.output_enable(SI5351_CLK0, 0);
si5351.output_enable(SI5351_CLK1, 0);
si5351.output_enable(SI5351_CLK2, 0);
//будем мигать светодиодом синхронно с модуляцией
pinMode(led, OUTPUT);
pinMode(prm, OUTPUT);
pinMode(prd, OUTPUT);
digitalWrite(led, 0);
digitalWrite(prm, 1);
digitalWrite(prd, 0);
Serial.println("Setup OK");
delay(3000);
}
//читаем строку из последовательного порта
void serialEvent() {
while(Serial.available()) {
char ch = Serial.read();
if (ch == 10) {
if ((mystring.substring(0,1)=="*")) {
Serial.print("Freq. set to ");
mystring = mystring.substring(1,10);
Serial.println(mystring);
freq = mystring.toInt() * 100;
} else {
mystring = mystring + "\n";
Serial.println(mystring);
Serial.println(mystring.length());
Transmit();
}
Nch = 0;
mystring = "";
} else {
mystring = mystring + ch;
Nch++;
}
}
}
//выборка символа
void selChar() {
if (symbol == 0) {
if(zero_count) {
psk_bit = 0;
zero_count--;
} else {
index++;
// сбросим указатель символа в сообщении на начальный, если строка подошла к концу
if(index >= mystring.length()) {
index = 0;
TXed = 1; //сигнализируем, что передача всё
}
symbol = varicode[mystring.charAt(index)];
}
} else {
if(symbol & 0x8000) {// 1 in the MSB position
psk_bit = 1;
} else { // 0 in the MSB position
psk_bit = 0;
}
symbol <<= 1;
if(symbol == 0) {
zero_count = 2;
} else {
zero_count = 0;
}
}
set_vfo = 1;
}
//переключение состояний
void tx() {
if(set_vfo) {
set_vfo = 0;
if(psk_bit) {
if(psk_bit_prev == 0) {
psk_one();
digitalWrite(led, HIGH);
}
psk_bit_prev = 1;
} else {
if(psk_bit_prev == 1) {
psk_zero();
digitalWrite(led, LOW);
}
psk_bit_prev = 0;
}
}
}
//непосредственно передача строки
void Transmit() {
unsigned int baud_period = 100000/baud_rate*1000; //период следования бит
psk_zero(); //по нулям лучше синхронизируется в начале передачи
digitalWrite(prm, 0); //переключаем антенну
digitalWrite(prd, 1);
delay(20); //переходные процессы
si5351.output_enable(SI5351_CLK0, 1);
si5351.output_enable(SI5351_CLK1, 1);
delay(2000); //дождаться, пока приёмник подхватит
while (!TXed) {
//каждые 32 мс => 31.25 Гц или 16 мс => 62.5 Гц в зависимости от скорости
if ((micros()-period) > baud_period) {
period = micros();
selChar();
tx();
}
}
psk_one(); //в конце единицы
delay(1000); //в виде паузы после передачи
si5351.output_enable(SI5351_CLK0, 0);
si5351.output_enable(SI5351_CLK1, 0);
delay(20); //переходные процессы
digitalWrite(prm, 0); //переключаем антенну
digitalWrite(prd, 1);
TXed = 0;
}
void loop() {
}
void psk_zero() {
si5351.set_freq(freq + (baud_rate / 2), SI5351_CLK0);
si5351.set_freq(freq - (baud_rate / 2), SI5351_CLK1);
}
void psk_one() {
si5351.set_freq(freq, SI5351_CLK0);
si5351.set_freq(freq, SI5351_CLK1);
}
uint16_t varicode[] =
{
0b1010101011000000, // 0 NUL
0b1011011011000000, // 1 SOH
0b1011101101000000, // 2 STX
0b1101110111000000, // 3 ETX
0b1011101011000000, // 4 EOT
0b1101011111000000, // 5 ENQ
0b1011101111000000, // 6 ACK
0b1011111101000000, // 7 BEL
0b1011111111000000, // 8 BS
0b1110111100000000, // 9 HT
0b1110100000000000, // 10 LF
0b1101101111000000, // 11 VT
0b1011011101000000, // 12 FF
0b1111100000000000, // 13 CR
0b1101110101000000, // 14 SO
0b1110101011000000, // 15 SI
0b1011110111000000, // 16 DLE
0b1011110101000000, // 17 DC1
0b1110101101000000, // 18 DC2
0b1110101111000000, // 19 DC3
0b1101011011000000, // 20 DC4
0b1101101011000000, // 21 NAK
0b1101101101000000, // 22 SYN
0b1101010111000000, // 23 ETB
0b1101111011000000, // 24 CAN
0b1101111101000000, // 25 EM
0b1110110111000000, // 26 SUB
0b1101010101000000, // 27 ESC
0b1101011101000000, // 28 FS
0b1110111011000000, // 29 GS
0b1011111011000000, // 30 RS
0b1101111111000000, // 31 US
0b1000000000000000, // 32 SP
0b1111111110000000, // 33 !
0b1010111110000000, // 34 "
0b1111101010000000, // 35 #
0b1110110110000000, // 36 $
0b1011010101000000, // 37 %
0b1010111011000000, // 38 &
0b1011111110000000, // 39 '
0b1111101100000000, // 40 (
0b1111011100000000, // 41 )
0b1011011110000000, // 42 *
0b1110111110000000, // 43 +
0b1110101000000000, // 44 ,
0b1101010000000000, // 45 -
0b1010111000000000, // 46 .
0b1101011110000000, // 47 /
0b1011011100000000, // 48 0
0b1011110100000000, // 49 1
0b1110110100000000, // 50 2
0b1111111100000000, // 51 3
0b1011101110000000, // 52 4
0b1010110110000000, // 53 5
0b1011010110000000, // 54 6
0b1101011010000000, // 55 7
0b1101010110000000, // 56 8
0b1101101110000000, // 57 9
0b1111010100000000, // 58 :
0b1101111010000000, // 59 ;
0b1111011010000000, // 60 <
0b1010101000000000, // 61 =
0b1110101110000000, // 62 >
0b1010101111000000, // 63 ?
0b1010111101000000, // 64 @
0b1111101000000000, // 65 A
0b1110101100000000, // 66 B
0b1010110100000000, // 67 C
0b1011010100000000, // 68 D
0b1110111000000000, // 69 E
0b1101101100000000, // 70 F
0b1111110100000000, // 71 G
0b1010101010000000, // 72 H
0b1111111000000000, // 73 I
0b1111111010000000, // 74 J
0b1011111010000000, // 75 K
0b1101011100000000, // 76 L
0b1011101100000000, // 77 M
0b1101110100000000, // 78 N
0b1010101100000000, // 79 O
0b1101010100000000, // 80 P
0b1110111010000000, // 81 Q
0b1010111100000000, // 82 R
0b1101111000000000, // 83 S
0b1101101000000000, // 84 T
0b1010101110000000, // 85 U
0b1101101010000000, // 86 V
0b1010111010000000, // 87 W
0b1011101010000000, // 88 X
0b1011110110000000, // 89 Y
0b1010101101000000, // 90 Z
0b1111101110000000, // 91 [
0b1111011110000000, // 92 backslash
0b1111110110000000, // 93 ]
0b1010111111000000, // 94 ^
0b1011011010000000, // 95 _
0b1011011111000000, // 96 `
0b1011000000000000, // 97 a
0b1011111000000000, // 98 b
0b1011110000000000, // 99 c
0b1011010000000000, // 100 d
0b1100000000000000, // 101 e
0b1111010000000000, // 102 f
0b1011011000000000, // 103 g
0b1010110000000000, // 104 h
0b1101000000000000, // 105 i
0b1111010110000000, // 106 j
0b1011111100000000, // 107 k
0b1101100000000000, // 108 l
0b1110110000000000, // 109 m
0b1111000000000000, // 110 n
0b1110000000000000, // 111 o
0b1111110000000000, // 112 p
0b1101111110000000, // 113 q
0b1010100000000000, // 114 r
0b1011100000000000, // 115 s
0b1010000000000000, // 116 t
0b1101110000000000, // 117 u
0b1111011000000000, // 118 v
0b1101011000000000, // 119 w
0b1101111100000000, // 120 x
0b1011101000000000, // 121 y
0b1110101010000000, // 122 z
0b1010110111000000, // 123 {
0b1101110110000000, // 124 |
0b1010110101000000, // 125 }
0b1011010111000000, // 126 ~
0b1110110101000000 // 127 (del)
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment