Created
October 22, 2018 20:42
-
-
Save zoonman/c774ef71581b1efa468ffdccc9d961a5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define ENCODER_OPTIMIZE_INTERRUPTS | |
#include <Encoder.h> | |
#include <Wire.h> | |
#include <si5351.h> | |
#include <Adafruit_ST7735.h> | |
#include <Fonts/FreeSansBold15pt7b.h> | |
// For the breakout, you can use any 2 or 3 pins | |
// These pins will also work for the 1.8" TFT shield | |
#define TFT_CS 10 | |
#define TFT_RST 8 | |
#define TFT_DC 9 | |
// encoder pins setup | |
#define ENC_PIN_A INT0 | |
#define ENC_PIN_B INT1 | |
#define START_F (uint32_t)21150000 | |
#define STR_BUFFER_SIZE 12 | |
#define BANDS 8 | |
// colors | |
#define COLOR_BRIGHT_GREEN 0x96C0 | |
#define COLOR_DARK_GREEN 0x29C0 | |
#define COLOR_DARK_RED 0x6800 | |
#define COLOR_MEDIUM_RED 0x9041 | |
#define COLOR_BRIGHT_RED 0xE800 | |
#define COLOR_BRIGHT_BLUE 0x1473 | |
#define COLOR_BAND_BACKGROUND 0x0208 | |
#define COLOR_GRAY_MEDIUM 0x9CD3 | |
// display | |
#define TFT_HEIGHT 128 | |
#define TFT_WIDTH 160 | |
#define TFT_QUOTER_WIDTH TFT_WIDTH/4 | |
// positions | |
#define FREQUENCY_X 10 | |
#define FREQUENCY_Y TFT_HEIGHT / 2 | |
#define FREQUENCY_FAKE_SPACE '/' | |
#define BAR_WIDTH 14 | |
#define SCALE_Y TFT_HEIGHT / 4 * 3 | |
#define SCALE_T 3 | |
int d,pd; | |
char b[STR_BUFFER_SIZE]; | |
volatile uint8_t pl = 1; | |
uint8_t freqMultiplier = 5; | |
// volatile keyword must be used for global variables used inside interrupt handlers | |
// @see https://barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword | |
volatile uint32_t cFrequency = 0; // Hz 4294967296 | |
volatile uint32_t oFrequency = 0; // Hz | |
long positionLeft = 0; | |
// RX or TX | |
volatile bool tx = false; // rx | |
volatile uint16_t scalePosX = 0; | |
// | |
enum Mode {CW = 0, LSB, USB, AM, FM}; | |
const char* ModeNames[] = {"CW", "LSB", "USB", "AM", "FM"}; | |
volatile Mode currentMode = LSB; | |
// Bands | |
struct BandRecord { | |
uint8_t id; // meters | |
uint16_t start; // kHZ | |
uint16_t width; // kHz | |
}; | |
BandRecord BandsBounds[BANDS] = { | |
{160, 1800, 200}, | |
{80, 3500, 500}, | |
{40, 7000, 300}, | |
{30, 10100, 150}, | |
{20, 14000, 350}, | |
{17, 18068, 100}, | |
{15, 21000, 450}, | |
{10, 28000, 1700} | |
}; | |
volatile int8_t currentBandIndex = -1; | |
Encoder myEnc(2, 3); // pin (2 = D2, 3 = D3) | |
// Init Display | |
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); | |
// | |
Si5351 si5351; | |
void drawLevel(uint8_t l) { | |
tft.drawRect(0, TFT_HEIGHT - 10, TFT_WIDTH, 10, COLOR_BAND_BACKGROUND); | |
if (l == pl) return; | |
if (l > 12) l = 12; | |
if (l < 1) l = 1; | |
uint8_t i; | |
if (l > pl) { | |
for (i = pl; i < l; i++) { | |
drawLevelBar(i); | |
} | |
pl = l; | |
} | |
if (l < pl) { | |
for (i = pl; i > l; i--) { | |
tft.fillRect((i - 1) * BAR_WIDTH + 2, TFT_HEIGHT - 8, BAR_WIDTH + (i > 9) ? 2 : 0, 6, ST77XX_BLACK); | |
} | |
pl = l; | |
} | |
} | |
void drawLevelBar(uint8_t l) { | |
uint16_t color; | |
uint8_t wOffset = (l > 9) ? 2 : 0, sOffset = 0; | |
uint8_t lxp = (l - 1) * BAR_WIDTH + 2; | |
switch (l) { | |
case 10: color = ST77XX_YELLOW; break; | |
case 11: color = ST77XX_ORANGE; sOffset = 2; break; | |
case 12: color = ST77XX_RED; break; | |
default: | |
color = ST77XX_GREEN; | |
} | |
tft.fillRect(lxp + sOffset, TFT_HEIGHT - 8, BAR_WIDTH - 1 + wOffset, 6, color); | |
if (l > 9) { | |
itoa(l+1, b, 10); | |
b[0] = '+'; | |
b[2] = '0'; | |
b[3] = 0; | |
lxp -= 7; | |
if (l > 10) { | |
lxp += 3; | |
} | |
} else { | |
itoa(l, b, 10); | |
} | |
textxy(lxp + 4, TFT_HEIGHT - 18, b, color); | |
} | |
void wipe(char *buf) { | |
for (int8_t i = 0;i < STR_BUFFER_SIZE;i++) { | |
buf[i] = 0; | |
} | |
} | |
void setFrequency() { | |
uint8_t hiBit = 0; | |
char st[STR_BUFFER_SIZE]; | |
char f[STR_BUFFER_SIZE]; | |
wipe(st); | |
wipe(f); | |
ultoa(cFrequency, st, 10); | |
int8_t j = STR_BUFFER_SIZE-2; | |
for (int8_t i = STR_BUFFER_SIZE-2;i >= 0;i--) { | |
while (st[j] == 0 && j > 0) { | |
j--; | |
}; | |
if (i == 7) { | |
f[i] = FREQUENCY_FAKE_SPACE; | |
goto fend; | |
} | |
if (i == 3) { | |
f[i] = '.'; | |
goto fend; | |
} | |
if (j >= 0) { | |
f[i] = st[j]; | |
j--; | |
} else { | |
f[i] = FREQUENCY_FAKE_SPACE; | |
} | |
fend: | |
; | |
} | |
int16_t x1, y1; | |
uint16_t w, h; | |
tft.setTextSize(1); | |
tft.setFont(&FreeSansBold15pt7b); | |
tft.setTextColor(COLOR_BRIGHT_GREEN); | |
tft.getTextBounds(f, FREQUENCY_X, FREQUENCY_Y, &x1, &y1, &w, &h); | |
tft.fillRect(x1, y1, w+10, h, ST77XX_BLACK); | |
tft.setCursor(FREQUENCY_X, FREQUENCY_Y); | |
tft.print(f); | |
tft.setFont(); | |
//tft.drawRect(x1, y1, w+10, h, COLOR_BRIGHT_BLUE); | |
oFrequency = cFrequency; | |
// | |
si5351.set_freq(cFrequency * 100ULL, SI5351_CLK0); | |
updateBand(); | |
displayScale(); | |
} | |
void displayMode() { | |
if (tx) { | |
drawRoundTextBox(0, 0, TFT_QUOTER_WIDTH, 15, "TX", COLOR_BRIGHT_RED, COLOR_MEDIUM_RED); | |
} else { | |
drawRoundTextBox(0, 0, TFT_QUOTER_WIDTH, 15, "RX", COLOR_BRIGHT_GREEN, COLOR_DARK_GREEN); | |
} | |
} | |
void displayBand() { | |
if (currentBandIndex == BANDS) { | |
drawRoundTextBox(TFT_WIDTH - TFT_QUOTER_WIDTH, 0, TFT_QUOTER_WIDTH, 15, "AIR", COLOR_BRIGHT_RED, COLOR_DARK_RED); | |
} else { | |
itoa(BandsBounds[currentBandIndex].id, b, 10); | |
drawRoundTextBox(TFT_WIDTH - TFT_QUOTER_WIDTH, 0, TFT_QUOTER_WIDTH, 15, b, COLOR_BRIGHT_GREEN, COLOR_DARK_GREEN); | |
} | |
tft.fillRect(0, SCALE_Y - 2, TFT_WIDTH-1, SCALE_T * 2, ST77XX_BLACK); | |
} | |
void drawRoundTextBox(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const char *text, uint16_t c, uint16_t bg) { | |
tft.fillRoundRect(x, y, w, h, 2, bg); | |
tft.drawRoundRect(x, y, w, h, 2, c); | |
int16_t tx, ty; uint16_t tw, th; | |
tft.getTextBounds(text, x, y, &tx, &ty, &tw, &th); | |
textxy(x + (w - tw)/2, y + (h - th) / 2 + 1, text, c, bg); | |
} | |
void updateBand() { | |
int i; | |
for (i = 0; i < BANDS; i++) { | |
if (cFrequency/1000 >= BandsBounds[i].start && cFrequency/1000 <= BandsBounds[i].start + BandsBounds[i].width) { | |
if (currentBandIndex != i) { | |
currentBandIndex = i; | |
displayBand(); | |
} | |
break; | |
} | |
} | |
if (i == BANDS && currentBandIndex != i) { | |
currentBandIndex = i; | |
displayBand(); | |
} | |
} | |
void displayScale() { | |
tft.drawFastHLine(0, SCALE_Y, TFT_WIDTH-1, COLOR_GRAY_MEDIUM); | |
tft.drawFastVLine(0, SCALE_Y - 2, 5, COLOR_GRAY_MEDIUM); | |
tft.drawFastVLine(TFT_WIDTH-1, SCALE_Y - 2, 5, COLOR_GRAY_MEDIUM); | |
uint16_t fStep = 500; // kHz | |
uint16_t fStart = 1000; // kHz | |
uint16_t fWidth = 30000; // kHz | |
if (currentBandIndex != BANDS) { | |
fStep = 50; | |
fStart = BandsBounds[currentBandIndex].start; | |
fWidth = BandsBounds[currentBandIndex].width; | |
} | |
// draw ticks | |
for (uint32_t f = 0; f < fWidth; f += fStep) { | |
tft.drawFastVLine( (f * TFT_WIDTH) / fWidth, SCALE_Y, f % (fStep *2) == 0 ? 4 : 2, COLOR_GRAY_MEDIUM); | |
} | |
uint16_t newScalePosX = (cFrequency/1000 - fStart) * TFT_WIDTH / fWidth; | |
if (scalePosX != newScalePosX) { | |
scaleTriangle(ST77XX_BLACK); | |
scalePosX = newScalePosX; | |
scaleTriangle(COLOR_BRIGHT_BLUE); | |
} | |
} | |
void scaleTriangle(uint16_t c) { | |
tft.fillTriangle(scalePosX, SCALE_Y - 1, scalePosX - SCALE_T, SCALE_Y - SCALE_T - 1, scalePosX + SCALE_T, SCALE_Y - 1 - SCALE_T, c); | |
} | |
void textxy(uint8_t x, uint8_t y, const char *text) { | |
tft.setCursor(x, y); | |
tft.print(text); | |
} | |
void textxy(uint8_t x, uint8_t y, const char *text, uint16_t c) { | |
tft.setTextColor(c); | |
textxy(x, y, text); | |
} | |
void textxy(uint8_t x, uint8_t y, const char *text, uint16_t c, uint16_t b) { | |
tft.setTextColor(c, b); | |
textxy(x, y, text); | |
} | |
void displayModulation() { | |
drawRoundTextBox(TFT_WIDTH - TFT_QUOTER_WIDTH*2, 0, TFT_QUOTER_WIDTH-1, 15, ModeNames[currentMode], COLOR_BRIGHT_BLUE, COLOR_BAND_BACKGROUND); | |
} | |
uint32_t p10(uint8_t i) { | |
if (i < 1) return 1; | |
return 10 * p10(i - 1); | |
} | |
// SETUP ----------------------------------------------------------------------- | |
void setup() { | |
// put your setup code here, to run once: | |
pinMode(LED_BUILTIN, OUTPUT); | |
tft.initR(INITR_BLACKTAB); // initialize a ST7735S chip, black tab | |
tft.fillScreen(ST77XX_BLACK); | |
tft.setRotation(1); | |
d = 1; | |
drawLevel(12); | |
Serial.begin(9600); | |
int i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_0PF, 0, 0); | |
cFrequency = START_F; | |
setFrequency(); | |
myEnc.write(positionLeft); | |
setFrequency(); | |
displayModulation(); | |
displayMode(); | |
updateBand(); | |
textxy(0, TFT_HEIGHT / 4, "Frequency:", COLOR_GRAY_MEDIUM, ST77XX_BLACK); | |
textxy(0, TFT_HEIGHT / 3 * 2 , "Step: ", COLOR_GRAY_MEDIUM, ST77XX_BLACK); | |
itoa(p10(freqMultiplier-1), b, 10); | |
textxy(30, TFT_HEIGHT / 3 * 2, b, COLOR_GRAY_MEDIUM, ST77XX_BLACK); | |
} | |
// MAIN LOOP =================================================================== | |
void loop() { | |
d = analogRead(A0); | |
if (pd != d) { | |
itoa(d, b, 10); | |
tft.setTextColor(COLOR_BRIGHT_BLUE); | |
textxy(TFT_WIDTH - 30, TFT_HEIGHT-30, b, COLOR_BRIGHT_BLUE, ST77XX_BLACK); | |
pd = d; | |
} | |
long newLeft = myEnc.read(); | |
if (newLeft > positionLeft+2) { | |
cFrequency-= p10(freqMultiplier-1); | |
positionLeft = newLeft; | |
} else if (newLeft < positionLeft-2) { | |
cFrequency+= p10(freqMultiplier-1); | |
positionLeft = newLeft; | |
} | |
if (oFrequency != cFrequency) { | |
setFrequency(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment