WSPR centre frequencies are 7040 000 00, 10140 100 00 & 14090 000 00 cHZ. Turn the Rotary Encoder to chose one of five presets are given per band at 50Hz intervals.
// Universal_WSPR transmits WSPR
// use WSPR_Symbol_Generator sketch to make symbol tone table
// here it is hard coded for _M6KWH IO92 20 (100mW)
// then edit code to insert symbols and msg displays, line 73, 136 & 137
// step in 50Hz steps, giving 5 frequencies per band
// output on CLK0 VFO
// ----- CONNECTIONS
// DDS I2C SI5351
// SCL = A5
// SDA = A4
// I2C address 0x60
// ------
// display I2C LCD 16 * 2
// o A5 SCL (y)
// o A4 SDA (or)
// o +5 (r)
// o GND (bwn)
// I2C address 0x27
// -----
// encoder KY-040
// o D2 DT (y)
// o D3 CLK (g)
// o D4 SW (or)
// o +5 (r)
// o GND (bwn)
// -----
#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"
#include "TimerOne.h"
// LCD
#define LCDADDR 0x27
#define LCDCOLS 16
#define LCDROWS 2
// rotary Encoder pins 2 & 3 (DT & CLK), button 4 (SW)
#define DT 2
#define CLK 3
#define SW 4
// transmit pin
#define TX 12
// dds object
Si5351 dds;
// LCD object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);
Rotary rot = Rotary(DT, CLK);
uint32_t freq;
uint8_t tone_ptr = 0; // Pointer to the current symbol
volatile uint8_t next_tone = 0; // Incremented by the ISR
uint8_t next_tone2 = 0; // Local store, to compare against.
uint8_t step_tone = 0; // Flag used to signify we need to move to the next symbol
uint8_t b; // band 0 - 26
// band frequencies (cHz)
uint32_t band[15] =
{704000000, 704005000, 704010000, 704015000, 704020000,
1014010000, 1014015000, 1014020000, 1014025000, 1014030000,
1409700000, 1409705000, 1409710000, 1409715000, 1409720000
}; // 40, 30, 20m
// msg _M6KWH IO92 20 - use WSSPR Symbol Generator program to create
uint8_t msg[162] =
{ 3, 1, 2, 0, 0, 0, 2, 2, 1, 2, 0, 2, 3, 1, 3, 0, 2, 2, 1, 2, 0, 3, 0, 1, 3, 1, 1, 0, 2, 0, 0, 2,
0, 0, 1, 0, 0, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 2, 1, 3, 0, 0, 1, 3, 0, 1, 2, 0, 0, 1, 3, 2, 1, 0,
2, 2, 0, 3, 3, 2, 3, 2, 3, 0, 3, 0, 3, 2, 0, 3, 2, 0, 1, 2, 1, 3, 0, 0, 0, 3, 1, 0, 1, 0, 1, 0,
2, 2, 3, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 2, 1, 3, 1, 2, 1, 1, 0, 0, 3, 3, 2, 1, 2, 0, 0, 1, 1, 1,
2, 2, 2, 0, 0, 3, 0, 3, 2, 0, 3, 1, 2, 2, 2, 0, 2, 0, 2, 3, 1, 0, 1, 0, 1, 1, 2, 0, 0, 3, 1, 0,
2, 0
};
// tone out at FREQ
uint32_t tones[4] = {0, 146, 293, 440};
void setup()
{
// init LCD & backlight on
lcd.init();
lcd.backlight();
dds.init(SI5351_CRYSTAL_LOAD_8PF, 0); // default 25MHz XTAL
// all CLK disabled
dds.output_enable(SI5351_CLK0, 0);
dds.output_enable(SI5351_CLK1, 0);
dds.output_enable(SI5351_CLK2, 0);
// encoder, button, RX, TX, band and XMIT pins
pinMode(DT, INPUT_PULLUP);
pinMode(CLK, INPUT_PULLUP);
pinMode(SW, INPUT_PULLUP);
digitalWrite(TX, HIGH); // no TX
b = 7; // band, middle 30m
// init display
dispMsg(0, 0, "WSPR ");
dispFreq(5, 0, band[b], 2); // display freq b
}
void loop()
{
tune();
tx(); // await button push
}
// tune
void tune()
{
unsigned char dir; // tuning direction CW/CCW
dir = rot.process(); // read encoder
if(dir != DIR_NONE) // turned?
{
if(dir == DIR_CW && b > 0) b += 1; // increment band freq +/- 1
if(dir == DIR_CCW && b < 15) b -= 1;
dispFreq(5, 0, band[b], 2); // display freq
}
}
// transmit
void tx()
{
if (digitalRead(SW) == LOW)
{
while (!digitalRead(SW)); // wait for release
dispMsg(0, 0, " M6KWH IO92 20 ");
dispMsg(0, 1, "Syb 100mW TX"); // show transmit
txWspr(b); // transmit on freq b
// restor display
dispMsg(0, 0, "WSPR ");
dispFreq(5, 0, band[b], 2); // display freq
dispMsg(0, 1, " "); // clear row 1
}
}
// transmit msg at freq fTable[b]
void txWspr(uint8_t b)
{
// Start Transmitter
digitalWrite(TX, LOW);
// Start the timer interrupt, is called every 8192/12000 seconds.
Timer1.initialize(682666UL);
Timer1.attachInterrupt(wspr_isr);
// Transmit!
while (1)
{
// do the increment checking without the interrupts, in case it gets
// modified while we are checking.
noInterrupts();
if (next_tone > next_tone2)
{
step_tone = 1; // next_tone has incremented, raise flag
next_tone2 = next_tone;
}
interrupts();
if (step_tone)
{
// Got a call from the ISR to increment the tone
step_tone = 0;
// We enable the clock here to avoid having it be active before the first tone
// is due to be transmitted.
dds.output_enable(SI5351_CLK0, 1);
// output freq
freq = band[b] + tones[msg[tone_ptr]];
freqOut(freq);
dispNum(4, 1, tone_ptr); // display symbol number
tone_ptr++; // next tone
// If we're at the end of the symbol array, disable the timer and break.
if (tone_ptr == 162)
{
Timer1.detachInterrupt();
next_tone = 0;
next_tone2 = 0;
tone_ptr = 0;
break;
}
}
}
// Disable clock, re-write display
dds.output_enable(SI5351_CLK0, 0);
digitalWrite(TX, HIGH);
}
// Timer ISR.
void wspr_isr(void)
{
next_tone += 1;
}
// frequency (in cHz) for VFO, on CLK0
void freqOut(uint32_t f)
{
dds.set_freq(f, 0ULL, SI5351_CLK0); // f cHz
}
// display char msg at col c, row r
void dispMsg(uint8_t c, uint8_t r, char *m)
{
lcd.setCursor(c, r);
lcd.print(m);
}
// display a number at col c, row r
void dispNum(uint8_t c, uint8_t r, uint16_t n)
{
lcd.setCursor(c, r);
lcd.print(n);
}
// display freq in kHz,col c, row r, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint32_t f, uint8_t d)
{
lcd.setCursor(c, r); // clear last freq display
lcd.print(" ");
lcd.setCursor(c, r); // clear last freq display
lcd.print((float)f / 100000, d); // convert to float for print function
lcd.print("kHz");
}
No comments:
Post a Comment