Compare commits
332 Commits
pdq_gfx_up
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
9d96fa649e | ||
|
6dceffd993 | ||
|
36cd505e8e | ||
|
6d4fc996d9 | ||
|
6c9e759901 | ||
|
4918670eaa | ||
|
4eecda170d | ||
|
50381da6de | ||
|
b136f5e43c | ||
|
a4f5f4b3cb | ||
|
ab678f3e8b | ||
|
c62eaaba2e | ||
|
c11c8bfbe3 | ||
|
84f47b0a23 | ||
|
740e881d28 | ||
|
71e5f877da | ||
|
e6fa6f935b | ||
|
b3a7e34d1b | ||
|
d282062eef | ||
|
f76b50fcc3 | ||
|
488e462c6c | ||
|
c323cec0cb | ||
|
f541ff8928 | ||
|
e7748d2878 | ||
|
e168a4d5b0 | ||
|
e3f23ae755 | ||
|
0b65559e97 | ||
|
2a412fd34a | ||
|
a138490dcc | ||
|
4cf8f7a840 | ||
|
6a6dc2a29a | ||
|
a7cbf50384 | ||
|
e809b37e29 | ||
|
e770230d14 | ||
|
7a32c8e205 | ||
|
97d825351c | ||
|
2e74193d93 | ||
|
ce0fac95fe | ||
|
2505e54235 | ||
|
8045b9b02b | ||
|
eba70c64ef | ||
|
53a39b0699 | ||
|
c6d83beff0 | ||
|
8e4adbff72 | ||
|
a3b9f703ba | ||
|
8aa80bdbc7 | ||
|
18a05460aa | ||
|
e8c80a8304 | ||
|
4e961bdea7 | ||
|
501205ebd5 | ||
|
a9b9b62dd1 | ||
|
55822dadf7 | ||
|
ab8ba66067 | ||
|
61c22474e9 | ||
|
77b1bd291e | ||
|
aa7e6825b4 | ||
|
fafc9acd0e | ||
|
b49b61615b | ||
|
cd3364c835 | ||
|
e980cff9ff | ||
|
0467223fe6 | ||
|
94be10fd87 | ||
|
8cc272b065 | ||
|
f9907cebde | ||
|
b1e4466b0e | ||
|
6cd72a5b8a | ||
|
e85db6781a | ||
|
bd1ff23d8f | ||
|
c45a824276 | ||
|
2c4448f6a0 | ||
|
7a788b6297 | ||
|
da45643035 | ||
|
0fe54d054f | ||
|
2a8ff5e758 | ||
|
06fa0e9ad7 | ||
|
1f5f3a1f9f | ||
|
85ea714955 | ||
|
4e7d1717e0 | ||
|
9b9a1610c2 | ||
|
1f97be3263 | ||
|
2db9899d4d | ||
|
fdd2afce98 | ||
|
0f45a2fc43 | ||
|
f66a173dbe | ||
|
8af59b0a85 | ||
|
5596c0c8ab | ||
|
91776fdcc7 | ||
|
80494b62b9 | ||
|
98e62a6dca | ||
|
40a13f1b5f | ||
|
1ee39ec535 | ||
|
cc013e5be4 | ||
|
1766386411 | ||
|
a2eae89733 | ||
|
1622d0ebe7 | ||
|
4a96027a07 | ||
|
67d34a3461 | ||
|
6face82c36 | ||
|
e593e0a060 | ||
|
5217858e23 | ||
|
363ea027f9 | ||
|
f5f914a2a9 | ||
|
ff4cc62e70 | ||
|
0091a1e17a | ||
|
7d84ab744b | ||
|
b87ef7575a | ||
|
f090a75221 | ||
|
f113551aa6 | ||
|
1e66731649 | ||
|
b2e17548c2 | ||
|
b116be8644 | ||
|
3438bef4fb | ||
|
b909f4d652 | ||
|
f7229ebaf2 | ||
|
6481812b4f | ||
|
cad3c3c57e | ||
|
98adba28ba | ||
|
2bfa97687d | ||
|
0b96cfb1f7 | ||
|
3dcbb8a0fe | ||
|
e9e134079a | ||
|
c8a90efee8 | ||
|
72e4a71a4d | ||
|
4582521f1b | ||
|
bba125590b | ||
|
f47c45194e | ||
|
2f964b3ffb | ||
|
04560ef8b6 | ||
|
d42571f7a5 | ||
|
4a0b0ec653 | ||
|
2d3cc48b19 | ||
|
423246f2b8 | ||
|
5721e06a51 | ||
|
23ecb4e4b2 | ||
|
77fffccb46 | ||
|
405df5a757 | ||
|
70a1025603 | ||
|
4a27962a5b | ||
|
3b932d54b4 | ||
|
c176604320 | ||
|
2c20483931 | ||
|
6686f199b0 | ||
|
2ff1d0d7e8 | ||
|
c727d876eb | ||
|
efd3bfc5ad | ||
|
2722b4367a | ||
|
2826dfde56 | ||
|
8239761a26 | ||
|
25d5f3aaa9 | ||
|
4583288bfa | ||
|
55096c9284 | ||
|
8bd9d59e5a | ||
|
2f9a0a3e04 | ||
|
51288f1db3 | ||
|
90d550a03a | ||
|
61b79ba06a | ||
|
5c0ecb087b | ||
|
fa62b1136f | ||
|
1077b0bf84 | ||
|
254b60d88f | ||
|
1869e20cf0 | ||
|
846c59e5cc | ||
|
dc5104324b | ||
|
c4e9d3c156 | ||
|
6dacc470a2 | ||
|
bc1e546ece | ||
|
7a149a979d | ||
|
bafe61d10a | ||
|
ecc3b9c1fb | ||
|
cc763a0dce | ||
|
5c04e23504 | ||
|
9541c009be | ||
|
d4867ac35b | ||
|
4483852742 | ||
|
a92fdc7793 | ||
|
26c670ce14 | ||
|
947624518d | ||
|
72fc92b584 | ||
|
b29bd955ca | ||
|
e50857f181 | ||
|
bd616caaba | ||
|
d9667d6756 | ||
|
5e30551bf2 | ||
|
9e21faa6f1 | ||
|
5a12a636be | ||
|
79a37620c2 | ||
|
66a9d82308 | ||
|
e42b21fdf3 | ||
|
49c5607dd3 | ||
|
e2249af826 | ||
|
5fb47187a6 | ||
|
8faa9c58c7 | ||
|
a7a2655f02 | ||
|
f42ad2bcd2 | ||
|
21eb8978d5 | ||
|
801b1b08c1 | ||
|
b805761415 | ||
|
e35a9eecec | ||
|
1c60b8b63f | ||
|
94aae2f289 | ||
|
225464c51a | ||
|
03406f62cc | ||
|
bc5c39357f | ||
|
d440be6c66 | ||
|
13fa83d440 | ||
|
0a880fe8a6 | ||
|
80454612c0 | ||
|
0ad20caa4b | ||
|
87cf184fe6 | ||
|
40c23919ed | ||
|
fbc718b2ee | ||
|
2d48b58461 | ||
|
6ddd46a853 | ||
|
6313aa5adc | ||
|
1115441a57 | ||
|
04d836ab73 | ||
|
14d19ebbd8 | ||
|
015afb5cde | ||
|
21736fcc1d | ||
|
03c159ba51 | ||
|
ceec8e2eb2 | ||
|
0417907d03 | ||
|
c013281a31 | ||
|
13f4bdbe83 | ||
|
8db6401bec | ||
|
e67885d8a0 | ||
|
e35a2addf9 | ||
|
95ea5b0ee2 | ||
|
fe1b8cfa34 | ||
|
953a59f309 | ||
|
b0b11a93f0 | ||
|
bbe34daff7 | ||
|
298bb479b5 | ||
|
1505936713 | ||
|
fe9f04d146 | ||
|
fea7b8d868 | ||
|
c38460576b | ||
|
6dd4b37d8a | ||
|
f54320e8cb | ||
|
b65e5eb90c | ||
|
2ac24eb28d | ||
|
e88ee1826e | ||
|
1175d1f7eb | ||
|
9635d404b0 | ||
|
c6b21bfa6d | ||
|
68b85e6fa0 | ||
|
54e4d4bf06 | ||
|
a8910e1ba2 | ||
|
0c575d222f | ||
|
6dae08e222 | ||
|
20a4577ed2 | ||
|
1b8966103d | ||
|
814b646ab0 | ||
|
70153f77d2 | ||
|
13df195efc | ||
|
418acf644b | ||
|
a9e73bb102 | ||
|
d854a51184 | ||
|
edbb17e4c4 | ||
|
1c099663fc | ||
|
b0193ca762 | ||
|
88d16bb073 | ||
|
3087830bfb | ||
|
bbdd6840c9 | ||
|
13f109c8b3 | ||
|
4aea1b7126 | ||
|
755b2214f9 | ||
|
9fb4efc5ac | ||
|
59347af9ed | ||
|
1f033b6af4 | ||
|
dd3a6162af | ||
|
6d5fb97c62 | ||
|
d032664620 | ||
|
850cda59e4 | ||
|
ece69fff3a | ||
|
4265d56d32 | ||
|
9af0c1c965 | ||
|
dc1da20b13 | ||
|
58bfe55620 | ||
|
b99e13eff4 | ||
|
315d9348f0 | ||
|
9b224699ee | ||
|
d0900aa392 | ||
|
ec409d8805 | ||
|
a8e616637c | ||
|
00f5d2691d | ||
|
e65f4d57a4 | ||
|
03fd8463eb | ||
|
8212ecf1e9 | ||
|
f79ca7c693 | ||
|
897b5c5de5 | ||
|
e1d0ee1c43 | ||
|
bc83bcd5d1 | ||
|
b100da145e | ||
|
e0bc5999c8 | ||
|
ebaf7c44de | ||
|
f0e0640115 | ||
|
6fe6185b19 | ||
|
7b2057eabb | ||
|
692c862b6e | ||
|
c17136279e | ||
|
79fd056af0 | ||
|
afeb056667 | ||
|
d62ff59c90 | ||
|
9a2a25abe9 | ||
|
9a205522d9 | ||
|
6e797adc70 | ||
|
2fc534f12a | ||
|
baf880c7ae | ||
|
f6894e03f8 | ||
|
227cbe9b07 | ||
|
f329950b6e | ||
|
3598328918 | ||
|
50a00ffb52 | ||
|
858205b4cd | ||
|
228b5d9589 | ||
|
44988f48fb | ||
|
ef8036ee19 | ||
|
9a68846b58 | ||
|
b655704264 | ||
|
a0abe466c0 | ||
|
4674230f51 | ||
|
a2109de63f | ||
|
073cfd4cb9 | ||
|
8d34a3461b | ||
|
fbfb7c89d6 | ||
|
4d7739d6dd | ||
|
4cefa08eaf | ||
|
e9702bd955 | ||
|
39168d663e | ||
|
0bf8424ab9 | ||
|
9dee71f5bd |
@ -101,7 +101,17 @@ const uint8_t FreeSansBold9pt7bBitmaps[] PROGMEM = {
|
||||
0x78, 0x1E, 0x03, 0x00, 0xC0, 0x70, 0x38, 0x0E, 0x00, 0xFE, 0xFE, 0x0E,
|
||||
0x1C, 0x38, 0x38, 0x70, 0xE0, 0xFF, 0xFF, 0x37, 0x66, 0x66, 0x6E, 0xE6,
|
||||
0x66, 0x66, 0x67, 0x30, 0xFF, 0xFF, 0x80, 0xCE, 0x66, 0x66, 0x67, 0x76,
|
||||
0x66, 0x66, 0x6E, 0xC0, 0x71, 0x8E };
|
||||
0x66, 0x66, 0x6E, 0xC0, 0x71, 0x8E, 0x0C, 0x00, 0xF0, 0x00, 0x8F, 0x10,
|
||||
0x1C, 0xF3, 0x83, 0xFF, 0xFC, 0x7F, 0xFF, 0xE3, 0xF0, 0xFE, 0x1E, 0x07,
|
||||
0xC1, 0xC0, 0x38, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80,
|
||||
0x1F, 0x1C, 0x03, 0x81, 0xE0, 0x78, 0x3F, 0x0F, 0xC7, 0xFF, 0xFE, 0x3F,
|
||||
0xFF, 0xC3, 0xCF, 0x38, 0x08, 0xF1, 0x00, 0x0F, 0x00, 0x00, 0xEE, 0xFD,
|
||||
0xDF, 0xBB, 0x80, 0x0E, 0xEF, 0xDD, 0xFB, 0xB8, 0x00, 0xEE, 0xFD, 0xDF,
|
||||
0xBB, 0x80, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00,
|
||||
0x0E, 0x00, 0x1C, 0x00, 0x38, 0x1F, 0xFF, 0xDF, 0xFF, 0x0F, 0xF8, 0x0F,
|
||||
0xE0, 0x1F, 0xC0, 0x3F, 0x80, 0x7F, 0x01, 0xEF, 0x03, 0x06, 0x04, 0x04,
|
||||
0x00
|
||||
};
|
||||
|
||||
const GFXglyph FreeSansBold9pt7bGlyphs[] PROGMEM = {
|
||||
{ 0, 0, 0, 5, 0, 1 }, // 0x20 ' '
|
||||
@ -198,11 +208,18 @@ const GFXglyph FreeSansBold9pt7bGlyphs[] PROGMEM = {
|
||||
{ 1207, 4, 17, 7, 1, -12 }, // 0x7B '{'
|
||||
{ 1216, 1, 17, 5, 2, -12 }, // 0x7C '|'
|
||||
{ 1219, 4, 17, 7, 2, -12 }, // 0x7D '}'
|
||||
{ 1228, 8, 2, 9, 0, -4 } }; // 0x7E '~'
|
||||
{ 1228, 8, 2, 9, 0, -4 }, // 0x7E '~'
|
||||
{ 1231, 20, 20, 22, 1, -12 }, // 0x7F gear icon
|
||||
{ 1231, 0, 0, 10, 0, 1 }, // 0x80 10px space to match numbers
|
||||
{ 1231, 0, 0, 4, 0, 1 }, // 0x81 4px space to match period
|
||||
{ 1282, 11, 15, 13, 1, -10 }, // 0x82 numpad icon
|
||||
{ 1304, 15, 15, 17, 1, -10 }, // 0x83 star icon
|
||||
};
|
||||
|
||||
const GFXfont FreeSansBold9pt7b PROGMEM = {
|
||||
(uint8_t *)FreeSansBold9pt7bBitmaps,
|
||||
(GFXglyph *)FreeSansBold9pt7bGlyphs,
|
||||
0x20, 0x7E, 22 };
|
||||
|
||||
// Approx. 1902 bytes
|
||||
0x20, //first character
|
||||
0x83, //last character
|
||||
22 //yAdvance (newline)
|
||||
};
|
||||
|
@ -145,6 +145,7 @@ public:
|
||||
static void drawChar(coord_t x, coord_t y, unsigned char c, color_t color, color_t bg, uint8_t size);
|
||||
static void drawCharGFX(coord_t x, coord_t y, unsigned char c, color_t color, color_t bg, uint8_t size);
|
||||
static inline void setCursor(coord_t x, coord_t y);
|
||||
static inline void setBound(coord_t x, coord_t y);
|
||||
static inline void setTextColor(color_t c);
|
||||
static inline void setTextColor(color_t c, color_t bg);
|
||||
static inline void setTextSize(uint8_t s);
|
||||
@ -157,8 +158,8 @@ public:
|
||||
static inline uint8_t getRotation() __attribute__ ((always_inline)) { return rotation; }
|
||||
static inline coord_t getCursorX() __attribute__ ((always_inline)) { return cursor_x; }
|
||||
static inline coord_t getCursorY() __attribute__ ((always_inline)) { return cursor_y; }
|
||||
static inline void getTextBounds(char *string, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
|
||||
static inline void getTextBounds(const __FlashStringHelper *s, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
|
||||
static inline void getTextBounds(const char *string, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi = _width);
|
||||
static inline void getTextBounds(const __FlashStringHelper *s, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi = _width);
|
||||
|
||||
virtual size_t write(uint8_t); // used by Arduino "Print.h" (and the one required virtual function)
|
||||
|
||||
@ -167,6 +168,7 @@ protected:
|
||||
static coord_t WIDTH, HEIGHT; // This is the 'raw' display w/h - never changes
|
||||
static coord_t _width, _height; // Display w/h as modified by current rotation
|
||||
static coord_t cursor_x, cursor_y;
|
||||
static coord_t bound_x1, bound_x2;
|
||||
static color_t textcolor, textbgcolor;
|
||||
static uint8_t textsize;
|
||||
static uint8_t rotation;
|
||||
@ -216,6 +218,10 @@ int16_t PDQ_GFX<HW>::cursor_x;
|
||||
template<class HW>
|
||||
int16_t PDQ_GFX<HW>::cursor_y;
|
||||
template<class HW>
|
||||
int16_t PDQ_GFX<HW>::bound_x1;
|
||||
template<class HW>
|
||||
int16_t PDQ_GFX<HW>::bound_x2;
|
||||
template<class HW>
|
||||
color_t PDQ_GFX<HW>::textcolor;
|
||||
template<class HW>
|
||||
color_t PDQ_GFX<HW>::textbgcolor;
|
||||
@ -239,6 +245,8 @@ PDQ_GFX<HW>::PDQ_GFX(coord_t w, coord_t h)
|
||||
_height = (int16_t)h;
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
bound_x1 = 0;
|
||||
bound_x2 = WIDTH;
|
||||
rotation = 0;
|
||||
textsize = 1;
|
||||
textcolor = 0xffff;
|
||||
@ -740,7 +748,7 @@ size_t PDQ_GFX<HW>::write(uint8_t c)
|
||||
{
|
||||
if(c == '\n')
|
||||
{
|
||||
cursor_x = 0;
|
||||
cursor_x = bound_x1;
|
||||
cursor_y += (coord_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
|
||||
}
|
||||
else if (c != '\r')
|
||||
@ -756,10 +764,10 @@ size_t PDQ_GFX<HW>::write(uint8_t c)
|
||||
if ((w > 0) && (h > 0))
|
||||
{
|
||||
coord_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic
|
||||
if(wrap && ((cursor_x + textsize * (xo + w)) >= _width))
|
||||
if(wrap && ((cursor_x + textsize * (xo + w)) >= bound_x2))
|
||||
{
|
||||
// Drawing character would go off right edge; wrap to new line
|
||||
cursor_x = 0;
|
||||
cursor_x = bound_x1;
|
||||
cursor_y += (coord_t)textsize *
|
||||
(uint8_t)pgm_read_byte(&gfxFont->yAdvance);
|
||||
}
|
||||
@ -831,7 +839,7 @@ void PDQ_GFX<HW>::drawChar(coord_t x, coord_t y, unsigned char c, color_t color,
|
||||
|
||||
// Draw a character with GFX font
|
||||
template<class HW>
|
||||
void PDQ_GFX<HW>::drawCharGFX(coord_t x, coord_t y, unsigned char c, color_t color, color_t bg, uint8_t size)
|
||||
void PDQ_GFX<HW>::drawCharGFX(coord_t x, coord_t y, unsigned char c, color_t color, color_t /*bg*/, uint8_t size)
|
||||
{
|
||||
// Character is assumed previously filtered by write() to eliminate
|
||||
// newlines, returns, non-printable characters, etc. Calling drawChar()
|
||||
@ -970,6 +978,13 @@ void PDQ_GFX<HW>::setCursor(coord_t x, coord_t y)
|
||||
cursor_y = (int16_t)y;
|
||||
}
|
||||
|
||||
template<class HW>
|
||||
void PDQ_GFX<HW>::setBound(coord_t x1, coord_t x2)
|
||||
{
|
||||
bound_x1 = (int16_t)x1;
|
||||
bound_x2 = (int16_t)x2;
|
||||
}
|
||||
|
||||
template<class HW>
|
||||
void PDQ_GFX<HW>::setTextSize(uint8_t s)
|
||||
{
|
||||
@ -1046,10 +1061,13 @@ void PDQ_GFX<HW>::setFont(const GFXfont *f)
|
||||
|
||||
// Pass string and a cursor position, returns UL corner and W,H.
|
||||
template<class HW>
|
||||
void PDQ_GFX<HW>::getTextBounds(char *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
|
||||
void PDQ_GFX<HW>::getTextBounds(const char *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi)
|
||||
{
|
||||
uint8_t c; // Current character
|
||||
|
||||
coord_t xs = x;
|
||||
coord_t xe = xs+wi;
|
||||
|
||||
*x1 = x;
|
||||
*y1 = y;
|
||||
*w = *h = 0;
|
||||
@ -1080,9 +1098,9 @@ void PDQ_GFX<HW>::getTextBounds(char *str, coord_t x, coord_t y, int16_t *x1, in
|
||||
xa = pgm_read_byte(&glyph->xAdvance);
|
||||
xo = pgm_read_byte(&glyph->xOffset);
|
||||
yo = pgm_read_byte(&glyph->yOffset);
|
||||
if (wrap && ((x + (((int16_t)xo + gw) * ts)) >= _width)) // Line wrap
|
||||
if (wrap && ((x + (((int16_t)xo + gw) * ts)) >= xe)) // Line wrap
|
||||
{
|
||||
x = 0; // Reset x to 0
|
||||
x = xs; // Reset x to 0
|
||||
y += ya; // Advance y by 1 line
|
||||
}
|
||||
gx1 = x + xo * ts;
|
||||
@ -1103,7 +1121,7 @@ void PDQ_GFX<HW>::getTextBounds(char *str, coord_t x, coord_t y, int16_t *x1, in
|
||||
}
|
||||
else // Newline
|
||||
{
|
||||
x = 0; // Reset x
|
||||
x = xs; // Reset x
|
||||
y += ya; // Advance y by 1 line
|
||||
}
|
||||
}
|
||||
@ -1158,10 +1176,12 @@ void PDQ_GFX<HW>::getTextBounds(char *str, coord_t x, coord_t y, int16_t *x1, in
|
||||
|
||||
// Same as above, but for PROGMEM strings
|
||||
template<class HW>
|
||||
void PDQ_GFX<HW>::getTextBounds(const __FlashStringHelper *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
|
||||
void PDQ_GFX<HW>::getTextBounds(const __FlashStringHelper *str, coord_t x, coord_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, coord_t wi)
|
||||
{
|
||||
uint8_t *s = (uint8_t *)str;
|
||||
uint8_t c;
|
||||
coord_t xs = x;
|
||||
coord_t xe = xs+wi;
|
||||
|
||||
*x1 = x;
|
||||
*y1 = y;
|
||||
@ -1194,9 +1214,9 @@ void PDQ_GFX<HW>::getTextBounds(const __FlashStringHelper *str, coord_t x, coord
|
||||
xa = pgm_read_byte(&glyph->xAdvance);
|
||||
xo = pgm_read_byte(&glyph->xOffset);
|
||||
yo = pgm_read_byte(&glyph->yOffset);
|
||||
if (wrap && ((x + (((int16_t)xo + gw) * ts)) >= _width)) // Line wrap
|
||||
if (wrap && ((x + (((int16_t)xo + gw) * ts)) >= xe)) // Line wrap
|
||||
{
|
||||
x = 0; // Reset x to 0
|
||||
x = xs; // Reset x to 0
|
||||
y += ya; // Advance y by 1 line
|
||||
}
|
||||
gx1 = x + xo * ts;
|
||||
@ -1217,7 +1237,7 @@ void PDQ_GFX<HW>::getTextBounds(const __FlashStringHelper *str, coord_t x, coord
|
||||
}
|
||||
else // Newline
|
||||
{
|
||||
x = 0; // Reset x
|
||||
x = xs; // Reset x
|
||||
y += ya; // Advance y by 1 line
|
||||
}
|
||||
}
|
||||
|
120
PDQ_MinLib/Picopixel.h
Normal file
120
PDQ_MinLib/Picopixel.h
Normal file
@ -0,0 +1,120 @@
|
||||
// Picopixel by Sebastian Weber. A tiny font
|
||||
// with all characters within a 6 pixel height.
|
||||
|
||||
const uint8_t PicopixelBitmaps[] PROGMEM = {
|
||||
0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A,
|
||||
0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60,
|
||||
0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C,
|
||||
0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94,
|
||||
0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA,
|
||||
0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4,
|
||||
0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58,
|
||||
0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7,
|
||||
0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55,
|
||||
0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5,
|
||||
0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE,
|
||||
0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92,
|
||||
0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75,
|
||||
0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54,
|
||||
0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A};
|
||||
|
||||
const GFXglyph PicopixelGlyphs[] PROGMEM = {{0, 0, 0, 2, 0, 1}, // 0x20 ' '
|
||||
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
|
||||
{1, 3, 2, 4, 0, -4}, // 0x22 '"'
|
||||
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
|
||||
{6, 3, 6, 4, 0, -4}, // 0x24 '$'
|
||||
{9, 3, 5, 4, 0, -4}, // 0x25 '%'
|
||||
{11, 4, 5, 5, 0, -4}, // 0x26 '&'
|
||||
{14, 1, 2, 2, 0, -4}, // 0x27 '''
|
||||
{15, 2, 5, 3, 0, -4}, // 0x28 '('
|
||||
{17, 2, 5, 3, 0, -4}, // 0x29 ')'
|
||||
{19, 3, 3, 4, 0, -3}, // 0x2A '*'
|
||||
{21, 3, 3, 4, 0, -3}, // 0x2B '+'
|
||||
{23, 2, 2, 3, 0, 0}, // 0x2C ','
|
||||
{24, 3, 1, 4, 0, -2}, // 0x2D '-'
|
||||
{25, 1, 1, 2, 0, 0}, // 0x2E '.'
|
||||
{26, 3, 5, 4, 0, -4}, // 0x2F '/'
|
||||
{28, 3, 5, 4, 0, -4}, // 0x30 '0'
|
||||
{30, 2, 5, 3, 0, -4}, // 0x31 '1'
|
||||
{32, 3, 5, 4, 0, -4}, // 0x32 '2'
|
||||
{34, 3, 5, 4, 0, -4}, // 0x33 '3'
|
||||
{36, 3, 5, 4, 0, -4}, // 0x34 '4'
|
||||
{38, 3, 5, 4, 0, -4}, // 0x35 '5'
|
||||
{40, 3, 5, 4, 0, -4}, // 0x36 '6'
|
||||
{42, 3, 5, 4, 0, -4}, // 0x37 '7'
|
||||
{44, 3, 5, 4, 0, -4}, // 0x38 '8'
|
||||
{46, 3, 5, 4, 0, -4}, // 0x39 '9'
|
||||
{48, 1, 3, 2, 0, -3}, // 0x3A ':'
|
||||
{49, 2, 4, 3, 0, -3}, // 0x3B ';'
|
||||
{50, 2, 3, 3, 0, -3}, // 0x3C '<'
|
||||
{51, 3, 3, 4, 0, -3}, // 0x3D '='
|
||||
{53, 2, 3, 3, 0, -3}, // 0x3E '>'
|
||||
{54, 3, 5, 4, 0, -4}, // 0x3F '?'
|
||||
{56, 3, 5, 4, 0, -4}, // 0x40 '@'
|
||||
{58, 3, 5, 4, 0, -4}, // 0x41 'A'
|
||||
{60, 3, 5, 4, 0, -4}, // 0x42 'B'
|
||||
{62, 3, 5, 4, 0, -4}, // 0x43 'C'
|
||||
{64, 3, 5, 4, 0, -4}, // 0x44 'D'
|
||||
{66, 3, 5, 4, 0, -4}, // 0x45 'E'
|
||||
{68, 3, 5, 4, 0, -4}, // 0x46 'F'
|
||||
{70, 3, 5, 4, 0, -4}, // 0x47 'G'
|
||||
{72, 3, 5, 4, 0, -4}, // 0x48 'H'
|
||||
{74, 1, 5, 2, 0, -4}, // 0x49 'I'
|
||||
{75, 3, 5, 4, 0, -4}, // 0x4A 'J'
|
||||
{77, 3, 5, 4, 0, -4}, // 0x4B 'K'
|
||||
{79, 3, 5, 4, 0, -4}, // 0x4C 'L'
|
||||
{81, 5, 5, 6, 0, -4}, // 0x4D 'M'
|
||||
{85, 4, 5, 5, 0, -4}, // 0x4E 'N'
|
||||
{88, 3, 5, 4, 0, -4}, // 0x4F 'O'
|
||||
{90, 3, 5, 4, 0, -4}, // 0x50 'P'
|
||||
{92, 3, 6, 4, 0, -4}, // 0x51 'Q'
|
||||
{95, 3, 5, 4, 0, -4}, // 0x52 'R'
|
||||
{97, 3, 5, 4, 0, -4}, // 0x53 'S'
|
||||
{99, 3, 5, 4, 0, -4}, // 0x54 'T'
|
||||
{101, 3, 5, 4, 0, -4}, // 0x55 'U'
|
||||
{103, 3, 5, 4, 0, -4}, // 0x56 'V'
|
||||
{105, 5, 5, 6, 0, -4}, // 0x57 'W'
|
||||
{109, 3, 5, 4, 0, -4}, // 0x58 'X'
|
||||
{111, 3, 5, 4, 0, -4}, // 0x59 'Y'
|
||||
{113, 3, 5, 4, 0, -4}, // 0x5A 'Z'
|
||||
{115, 2, 5, 3, 0, -4}, // 0x5B '['
|
||||
{117, 3, 5, 4, 0, -4}, // 0x5C '\'
|
||||
{119, 2, 5, 3, 0, -4}, // 0x5D ']'
|
||||
{121, 3, 2, 4, 0, -4}, // 0x5E '^'
|
||||
{122, 4, 1, 4, 0, 1}, // 0x5F '_'
|
||||
{123, 2, 2, 3, 0, -4}, // 0x60 '`'
|
||||
{124, 3, 4, 4, 0, -3}, // 0x61 'a'
|
||||
{126, 3, 5, 4, 0, -4}, // 0x62 'b'
|
||||
{128, 3, 3, 4, 0, -2}, // 0x63 'c'
|
||||
{130, 3, 5, 4, 0, -4}, // 0x64 'd'
|
||||
{132, 3, 4, 4, 0, -3}, // 0x65 'e'
|
||||
{134, 2, 5, 3, 0, -4}, // 0x66 'f'
|
||||
{136, 3, 5, 4, 0, -3}, // 0x67 'g'
|
||||
{138, 3, 5, 4, 0, -4}, // 0x68 'h'
|
||||
{140, 1, 5, 2, 0, -4}, // 0x69 'i'
|
||||
{141, 2, 6, 3, 0, -4}, // 0x6A 'j'
|
||||
{143, 3, 5, 4, 0, -4}, // 0x6B 'k'
|
||||
{145, 2, 5, 3, 0, -4}, // 0x6C 'l'
|
||||
{147, 5, 3, 6, 0, -2}, // 0x6D 'm'
|
||||
{149, 3, 3, 4, 0, -2}, // 0x6E 'n'
|
||||
{151, 3, 3, 4, 0, -2}, // 0x6F 'o'
|
||||
{153, 3, 4, 4, 0, -2}, // 0x70 'p'
|
||||
{155, 3, 4, 4, 0, -2}, // 0x71 'q'
|
||||
{157, 2, 3, 3, 0, -2}, // 0x72 'r'
|
||||
{158, 3, 4, 4, 0, -3}, // 0x73 's'
|
||||
{160, 2, 5, 3, 0, -4}, // 0x74 't'
|
||||
{162, 3, 3, 4, 0, -2}, // 0x75 'u'
|
||||
{164, 3, 3, 4, 0, -2}, // 0x76 'v'
|
||||
{166, 5, 3, 6, 0, -2}, // 0x77 'w'
|
||||
{168, 3, 3, 4, 0, -2}, // 0x78 'x'
|
||||
{170, 3, 4, 4, 0, -2}, // 0x79 'y'
|
||||
{172, 3, 4, 4, 0, -3}, // 0x7A 'z'
|
||||
{174, 3, 5, 4, 0, -4}, // 0x7B '{'
|
||||
{176, 1, 6, 2, 0, -4}, // 0x7C '|'
|
||||
{177, 3, 5, 4, 0, -4}, // 0x7D '}'
|
||||
{179, 4, 2, 5, 0, -3}}; // 0x7E '~'
|
||||
|
||||
const GFXfont Picopixel PROGMEM = {(uint8_t *)PicopixelBitmaps,
|
||||
(GFXglyph *)PicopixelGlyphs, 0x20, 0x7E, 7};
|
||||
|
||||
// Approx. 852 bytes
|
471
PDQ_MinLib/TomThumb.h
Normal file
471
PDQ_MinLib/TomThumb.h
Normal file
@ -0,0 +1,471 @@
|
||||
/**
|
||||
** The original 3x5 font is licensed under the 3-clause BSD license:
|
||||
**
|
||||
** Copyright 1999 Brian J. Swetland
|
||||
** Copyright 1999 Vassilii Khachaturov
|
||||
** Portions (of vt100.c/vt100.h) copyright Dan Marks
|
||||
**
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions, and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions, and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the authors may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
** Modifications to Tom Thumb for improved readability are from Robey Pointer,
|
||||
** see:
|
||||
** http://robey.lag.net/2010/01/23/tiny-monospace-font.html
|
||||
**
|
||||
** The original author does not have any objection to relicensing of Robey
|
||||
** Pointer's modifications (in this file) in a more permissive license. See
|
||||
** the discussion at the above blog, and also here:
|
||||
** http://opengameart.org/forumtopic/how-to-submit-art-using-the-3-clause-bsd-license
|
||||
**
|
||||
** Feb 21, 2016: Conversion from Linux BDF --> Adafruit GFX font,
|
||||
** with the help of this Python script:
|
||||
** https://gist.github.com/skelliam/322d421f028545f16f6d
|
||||
** William Skellenger (williamj@skellenger.net)
|
||||
** Twitter: @skelliam
|
||||
**
|
||||
*/
|
||||
|
||||
#define TOMTHUMB_USE_EXTENDED 0
|
||||
|
||||
const uint8_t TomThumbBitmaps[] PROGMEM = {
|
||||
0x00, /* 0x20 space */
|
||||
0x80, 0x80, 0x80, 0x00, 0x80, /* 0x21 exclam */
|
||||
0xA0, 0xA0, /* 0x22 quotedbl */
|
||||
0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /* 0x23 numbersign */
|
||||
0x60, 0xC0, 0x60, 0xC0, 0x40, /* 0x24 dollar */
|
||||
0x80, 0x20, 0x40, 0x80, 0x20, /* 0x25 percent */
|
||||
0xC0, 0xC0, 0xE0, 0xA0, 0x60, /* 0x26 ampersand */
|
||||
0x80, 0x80, /* 0x27 quotesingle */
|
||||
0x40, 0x80, 0x80, 0x80, 0x40, /* 0x28 parenleft */
|
||||
0x80, 0x40, 0x40, 0x40, 0x80, /* 0x29 parenright */
|
||||
0xA0, 0x40, 0xA0, /* 0x2A asterisk */
|
||||
0x40, 0xE0, 0x40, /* 0x2B plus */
|
||||
0x40, 0x80, /* 0x2C comma */
|
||||
0xE0, /* 0x2D hyphen */
|
||||
0x80, /* 0x2E period */
|
||||
0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */
|
||||
0x60, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x30 zero */
|
||||
0x40, 0xC0, 0x40, 0x40, 0x40, /* 0x31 one */
|
||||
0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */
|
||||
0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */
|
||||
0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */
|
||||
0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */
|
||||
0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */
|
||||
0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */
|
||||
0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */
|
||||
0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */
|
||||
0x80, 0x00, 0x80, /* 0x3A colon */
|
||||
0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */
|
||||
0x20, 0x40, 0x80, 0x40, 0x20, /* 0x3C less */
|
||||
0xE0, 0x00, 0xE0, /* 0x3D equal */
|
||||
0x80, 0x40, 0x20, 0x40, 0x80, /* 0x3E greater */
|
||||
0xE0, 0x20, 0x40, 0x00, 0x40, /* 0x3F question */
|
||||
0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */
|
||||
0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */
|
||||
0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */
|
||||
0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */
|
||||
0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */
|
||||
0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */
|
||||
0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */
|
||||
0x60, 0x80, 0xE0, 0xA0, 0x60, /* 0x47 G */
|
||||
0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x48 H */
|
||||
0xE0, 0x40, 0x40, 0x40, 0xE0, /* 0x49 I */
|
||||
0x20, 0x20, 0x20, 0xA0, 0x40, /* 0x4A J */
|
||||
0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /* 0x4B K */
|
||||
0x80, 0x80, 0x80, 0x80, 0xE0, /* 0x4C L */
|
||||
0xA0, 0xE0, 0xE0, 0xA0, 0xA0, /* 0x4D M */
|
||||
0xA0, 0xE0, 0xE0, 0xE0, 0xA0, /* 0x4E N */
|
||||
0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */
|
||||
0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */
|
||||
0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */
|
||||
0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */
|
||||
0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */
|
||||
0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */
|
||||
0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */
|
||||
0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */
|
||||
0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */
|
||||
0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */
|
||||
0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */
|
||||
0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */
|
||||
0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */
|
||||
0x80, 0x40, 0x20, /* 0x5C backslash */
|
||||
0xE0, 0x20, 0x20, 0x20, 0xE0, /* 0x5D bracketright */
|
||||
0x40, 0xA0, /* 0x5E asciicircum */
|
||||
0xE0, /* 0x5F underscore */
|
||||
0x80, 0x40, /* 0x60 grave */
|
||||
0xC0, 0x60, 0xA0, 0xE0, /* 0x61 a */
|
||||
0x80, 0xC0, 0xA0, 0xA0, 0xC0, /* 0x62 b */
|
||||
0x60, 0x80, 0x80, 0x60, /* 0x63 c */
|
||||
0x20, 0x60, 0xA0, 0xA0, 0x60, /* 0x64 d */
|
||||
0x60, 0xA0, 0xC0, 0x60, /* 0x65 e */
|
||||
0x20, 0x40, 0xE0, 0x40, 0x40, /* 0x66 f */
|
||||
0x60, 0xA0, 0xE0, 0x20, 0x40, /* 0x67 g */
|
||||
0x80, 0xC0, 0xA0, 0xA0, 0xA0, /* 0x68 h */
|
||||
0x80, 0x00, 0x80, 0x80, 0x80, /* 0x69 i */
|
||||
0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /* 0x6A j */
|
||||
0x80, 0xA0, 0xC0, 0xC0, 0xA0, /* 0x6B k */
|
||||
0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x6C l */
|
||||
0xE0, 0xE0, 0xE0, 0xA0, /* 0x6D m */
|
||||
0xC0, 0xA0, 0xA0, 0xA0, /* 0x6E n */
|
||||
0x40, 0xA0, 0xA0, 0x40, /* 0x6F o */
|
||||
0xC0, 0xA0, 0xA0, 0xC0, 0x80, /* 0x70 p */
|
||||
0x60, 0xA0, 0xA0, 0x60, 0x20, /* 0x71 q */
|
||||
0x60, 0x80, 0x80, 0x80, /* 0x72 r */
|
||||
0x60, 0xC0, 0x60, 0xC0, /* 0x73 s */
|
||||
0x40, 0xE0, 0x40, 0x40, 0x60, /* 0x74 t */
|
||||
0xA0, 0xA0, 0xA0, 0x60, /* 0x75 u */
|
||||
0xA0, 0xA0, 0xE0, 0x40, /* 0x76 v */
|
||||
0xA0, 0xE0, 0xE0, 0xE0, /* 0x77 w */
|
||||
0xA0, 0x40, 0x40, 0xA0, /* 0x78 x */
|
||||
0xA0, 0xA0, 0x60, 0x20, 0x40, /* 0x79 y */
|
||||
0xE0, 0x60, 0xC0, 0xE0, /* 0x7A z */
|
||||
0x60, 0x40, 0x80, 0x40, 0x60, /* 0x7B braceleft */
|
||||
0x80, 0x80, 0x00, 0x80, 0x80, /* 0x7C bar */
|
||||
0xC0, 0x40, 0x20, 0x40, 0xC0, /* 0x7D braceright */
|
||||
0x60, 0xC0, /* 0x7E asciitilde */
|
||||
#if (TOMTHUMB_USE_EXTENDED)
|
||||
0x80, 0x00, 0x80, 0x80, 0x80, /* 0xA1 exclamdown */
|
||||
0x40, 0xE0, 0x80, 0xE0, 0x40, /* 0xA2 cent */
|
||||
0x60, 0x40, 0xE0, 0x40, 0xE0, /* 0xA3 sterling */
|
||||
0xA0, 0x40, 0xE0, 0x40, 0xA0, /* 0xA4 currency */
|
||||
0xA0, 0xA0, 0x40, 0xE0, 0x40, /* 0xA5 yen */
|
||||
0x80, 0x80, 0x00, 0x80, 0x80, /* 0xA6 brokenbar */
|
||||
0x60, 0x40, 0xA0, 0x40, 0xC0, /* 0xA7 section */
|
||||
0xA0, /* 0xA8 dieresis */
|
||||
0x60, 0x80, 0x60, /* 0xA9 copyright */
|
||||
0x60, 0xA0, 0xE0, 0x00, 0xE0, /* 0xAA ordfeminine */
|
||||
0x40, 0x80, 0x40, /* 0xAB guillemotleft */
|
||||
0xE0, 0x20, /* 0xAC logicalnot */
|
||||
0xC0, /* 0xAD softhyphen */
|
||||
0xC0, 0xC0, 0xA0, /* 0xAE registered */
|
||||
0xE0, /* 0xAF macron */
|
||||
0x40, 0xA0, 0x40, /* 0xB0 degree */
|
||||
0x40, 0xE0, 0x40, 0x00, 0xE0, /* 0xB1 plusminus */
|
||||
0xC0, 0x40, 0x60, /* 0xB2 twosuperior */
|
||||
0xE0, 0x60, 0xE0, /* 0xB3 threesuperior */
|
||||
0x40, 0x80, /* 0xB4 acute */
|
||||
0xA0, 0xA0, 0xA0, 0xC0, 0x80, /* 0xB5 mu */
|
||||
0x60, 0xA0, 0x60, 0x60, 0x60, /* 0xB6 paragraph */
|
||||
0xE0, 0xE0, 0xE0, /* 0xB7 periodcentered */
|
||||
0x40, 0x20, 0xC0, /* 0xB8 cedilla */
|
||||
0x80, 0x80, 0x80, /* 0xB9 onesuperior */
|
||||
0x40, 0xA0, 0x40, 0x00, 0xE0, /* 0xBA ordmasculine */
|
||||
0x80, 0x40, 0x80, /* 0xBB guillemotright */
|
||||
0x80, 0x80, 0x00, 0x60, 0x20, /* 0xBC onequarter */
|
||||
0x80, 0x80, 0x00, 0xC0, 0x60, /* 0xBD onehalf */
|
||||
0xC0, 0xC0, 0x00, 0x60, 0x20, /* 0xBE threequarters */
|
||||
0x40, 0x00, 0x40, 0x80, 0xE0, /* 0xBF questiondown */
|
||||
0x40, 0x20, 0x40, 0xE0, 0xA0, /* 0xC0 Agrave */
|
||||
0x40, 0x80, 0x40, 0xE0, 0xA0, /* 0xC1 Aacute */
|
||||
0xE0, 0x00, 0x40, 0xE0, 0xA0, /* 0xC2 Acircumflex */
|
||||
0x60, 0xC0, 0x40, 0xE0, 0xA0, /* 0xC3 Atilde */
|
||||
0xA0, 0x40, 0xA0, 0xE0, 0xA0, /* 0xC4 Adieresis */
|
||||
0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /* 0xC5 Aring */
|
||||
0x60, 0xC0, 0xE0, 0xC0, 0xE0, /* 0xC6 AE */
|
||||
0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /* 0xC7 Ccedilla */
|
||||
0x40, 0x20, 0xE0, 0xC0, 0xE0, /* 0xC8 Egrave */
|
||||
0x40, 0x80, 0xE0, 0xC0, 0xE0, /* 0xC9 Eacute */
|
||||
0xE0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCA Ecircumflex */
|
||||
0xA0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCB Edieresis */
|
||||
0x40, 0x20, 0xE0, 0x40, 0xE0, /* 0xCC Igrave */
|
||||
0x40, 0x80, 0xE0, 0x40, 0xE0, /* 0xCD Iacute */
|
||||
0xE0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCE Icircumflex */
|
||||
0xA0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCF Idieresis */
|
||||
0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD0 Eth */
|
||||
0xC0, 0x60, 0xA0, 0xE0, 0xA0, /* 0xD1 Ntilde */
|
||||
0x40, 0x20, 0xE0, 0xA0, 0xE0, /* 0xD2 Ograve */
|
||||
0x40, 0x80, 0xE0, 0xA0, 0xE0, /* 0xD3 Oacute */
|
||||
0xE0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD4 Ocircumflex */
|
||||
0xC0, 0x60, 0xE0, 0xA0, 0xE0, /* 0xD5 Otilde */
|
||||
0xA0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD6 Odieresis */
|
||||
0xA0, 0x40, 0xA0, /* 0xD7 multiply */
|
||||
0x60, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD8 Oslash */
|
||||
0x80, 0x40, 0xA0, 0xA0, 0xE0, /* 0xD9 Ugrave */
|
||||
0x20, 0x40, 0xA0, 0xA0, 0xE0, /* 0xDA Uacute */
|
||||
0xE0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDB Ucircumflex */
|
||||
0xA0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDC Udieresis */
|
||||
0x20, 0x40, 0xA0, 0xE0, 0x40, /* 0xDD Yacute */
|
||||
0x80, 0xE0, 0xA0, 0xE0, 0x80, /* 0xDE Thorn */
|
||||
0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /* 0xDF germandbls */
|
||||
0x40, 0x20, 0x60, 0xA0, 0xE0, /* 0xE0 agrave */
|
||||
0x40, 0x80, 0x60, 0xA0, 0xE0, /* 0xE1 aacute */
|
||||
0xE0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE2 acircumflex */
|
||||
0x60, 0xC0, 0x60, 0xA0, 0xE0, /* 0xE3 atilde */
|
||||
0xA0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE4 adieresis */
|
||||
0x60, 0x60, 0x60, 0xA0, 0xE0, /* 0xE5 aring */
|
||||
0x60, 0xE0, 0xE0, 0xC0, /* 0xE6 ae */
|
||||
0x60, 0x80, 0x60, 0x20, 0x40, /* 0xE7 ccedilla */
|
||||
0x40, 0x20, 0x60, 0xE0, 0x60, /* 0xE8 egrave */
|
||||
0x40, 0x80, 0x60, 0xE0, 0x60, /* 0xE9 eacute */
|
||||
0xE0, 0x00, 0x60, 0xE0, 0x60, /* 0xEA ecircumflex */
|
||||
0xA0, 0x00, 0x60, 0xE0, 0x60, /* 0xEB edieresis */
|
||||
0x80, 0x40, 0x80, 0x80, 0x80, /* 0xEC igrave */
|
||||
0x40, 0x80, 0x40, 0x40, 0x40, /* 0xED iacute */
|
||||
0xE0, 0x00, 0x40, 0x40, 0x40, /* 0xEE icircumflex */
|
||||
0xA0, 0x00, 0x40, 0x40, 0x40, /* 0xEF idieresis */
|
||||
0x60, 0xC0, 0x60, 0xA0, 0x60, /* 0xF0 eth */
|
||||
0xC0, 0x60, 0xC0, 0xA0, 0xA0, /* 0xF1 ntilde */
|
||||
0x40, 0x20, 0x40, 0xA0, 0x40, /* 0xF2 ograve */
|
||||
0x40, 0x80, 0x40, 0xA0, 0x40, /* 0xF3 oacute */
|
||||
0xE0, 0x00, 0x40, 0xA0, 0x40, /* 0xF4 ocircumflex */
|
||||
0xC0, 0x60, 0x40, 0xA0, 0x40, /* 0xF5 otilde */
|
||||
0xA0, 0x00, 0x40, 0xA0, 0x40, /* 0xF6 odieresis */
|
||||
0x40, 0x00, 0xE0, 0x00, 0x40, /* 0xF7 divide */
|
||||
0x60, 0xE0, 0xA0, 0xC0, /* 0xF8 oslash */
|
||||
0x80, 0x40, 0xA0, 0xA0, 0x60, /* 0xF9 ugrave */
|
||||
0x20, 0x40, 0xA0, 0xA0, 0x60, /* 0xFA uacute */
|
||||
0xE0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFB ucircumflex */
|
||||
0xA0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFC udieresis */
|
||||
0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /* 0xFD yacute */
|
||||
0x80, 0xC0, 0xA0, 0xC0, 0x80, /* 0xFE thorn */
|
||||
0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /* 0xFF ydieresis */
|
||||
0x00, /* 0x11D gcircumflex */
|
||||
0x60, 0xC0, 0xE0, 0xC0, 0x60, /* 0x152 OE */
|
||||
0x60, 0xE0, 0xC0, 0xE0, /* 0x153 oe */
|
||||
0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x160 Scaron */
|
||||
0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x161 scaron */
|
||||
0xA0, 0x00, 0xA0, 0x40, 0x40, /* 0x178 Ydieresis */
|
||||
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17D Zcaron */
|
||||
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17E zcaron */
|
||||
0x00, /* 0xEA4 uni0EA4 */
|
||||
0x00, /* 0x13A0 uni13A0 */
|
||||
0x80, /* 0x2022 bullet */
|
||||
0xA0, /* 0x2026 ellipsis */
|
||||
0x60, 0xE0, 0xE0, 0xC0, 0x60, /* 0x20AC Euro */
|
||||
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0xFFFD uniFFFD */
|
||||
#endif /* (TOMTHUMB_USE_EXTENDED) */
|
||||
};
|
||||
|
||||
/* {offset, width, height, advance cursor, x offset, y offset} */
|
||||
const GFXglyph TomThumbGlyphs[] PROGMEM = {
|
||||
{0, 8, 1, 2, 0, -5}, /* 0x20 space */
|
||||
{1, 8, 5, 2, 0, -5}, /* 0x21 exclam */
|
||||
{6, 8, 2, 4, 0, -5}, /* 0x22 quotedbl */
|
||||
{8, 8, 5, 4, 0, -5}, /* 0x23 numbersign */
|
||||
{13, 8, 5, 4, 0, -5}, /* 0x24 dollar */
|
||||
{18, 8, 5, 4, 0, -5}, /* 0x25 percent */
|
||||
{23, 8, 5, 4, 0, -5}, /* 0x26 ampersand */
|
||||
{28, 8, 2, 2, 0, -5}, /* 0x27 quotesingle */
|
||||
{30, 8, 5, 3, 0, -5}, /* 0x28 parenleft */
|
||||
{35, 8, 5, 3, 0, -5}, /* 0x29 parenright */
|
||||
{40, 8, 3, 4, 0, -5}, /* 0x2A asterisk */
|
||||
{43, 8, 3, 4, 0, -4}, /* 0x2B plus */
|
||||
{46, 8, 2, 3, 0, -2}, /* 0x2C comma */
|
||||
{48, 8, 1, 4, 0, -3}, /* 0x2D hyphen */
|
||||
{49, 8, 1, 2, 0, -1}, /* 0x2E period */
|
||||
{50, 8, 5, 4, 0, -5}, /* 0x2F slash */
|
||||
{55, 8, 5, 4, 0, -5}, /* 0x30 zero */
|
||||
{60, 8, 5, 3, 0, -5}, /* 0x31 one */
|
||||
{65, 8, 5, 4, 0, -5}, /* 0x32 two */
|
||||
{70, 8, 5, 4, 0, -5}, /* 0x33 three */
|
||||
{75, 8, 5, 4, 0, -5}, /* 0x34 four */
|
||||
{80, 8, 5, 4, 0, -5}, /* 0x35 five */
|
||||
{85, 8, 5, 4, 0, -5}, /* 0x36 six */
|
||||
{90, 8, 5, 4, 0, -5}, /* 0x37 seven */
|
||||
{95, 8, 5, 4, 0, -5}, /* 0x38 eight */
|
||||
{100, 8, 5, 4, 0, -5}, /* 0x39 nine */
|
||||
{105, 8, 3, 2, 0, -4}, /* 0x3A colon */
|
||||
{108, 8, 4, 3, 0, -4}, /* 0x3B semicolon */
|
||||
{112, 8, 5, 4, 0, -5}, /* 0x3C less */
|
||||
{117, 8, 3, 4, 0, -4}, /* 0x3D equal */
|
||||
{120, 8, 5, 4, 0, -5}, /* 0x3E greater */
|
||||
{125, 8, 5, 4, 0, -5}, /* 0x3F question */
|
||||
{130, 8, 5, 4, 0, -5}, /* 0x40 at */
|
||||
{135, 8, 5, 4, 0, -5}, /* 0x41 A */
|
||||
{140, 8, 5, 4, 0, -5}, /* 0x42 B */
|
||||
{145, 8, 5, 4, 0, -5}, /* 0x43 C */
|
||||
{150, 8, 5, 4, 0, -5}, /* 0x44 D */
|
||||
{155, 8, 5, 4, 0, -5}, /* 0x45 E */
|
||||
{160, 8, 5, 4, 0, -5}, /* 0x46 F */
|
||||
{165, 8, 5, 4, 0, -5}, /* 0x47 G */
|
||||
{170, 8, 5, 4, 0, -5}, /* 0x48 H */
|
||||
{175, 8, 5, 4, 0, -5}, /* 0x49 I */
|
||||
{180, 8, 5, 4, 0, -5}, /* 0x4A J */
|
||||
{185, 8, 5, 4, 0, -5}, /* 0x4B K */
|
||||
{190, 8, 5, 4, 0, -5}, /* 0x4C L */
|
||||
{195, 8, 5, 4, 0, -5}, /* 0x4D M */
|
||||
{200, 8, 5, 4, 0, -5}, /* 0x4E N */
|
||||
{205, 8, 5, 4, 0, -5}, /* 0x4F O */
|
||||
{210, 8, 5, 4, 0, -5}, /* 0x50 P */
|
||||
{215, 8, 5, 4, 0, -5}, /* 0x51 Q */
|
||||
{220, 8, 5, 4, 0, -5}, /* 0x52 R */
|
||||
{225, 8, 5, 4, 0, -5}, /* 0x53 S */
|
||||
{230, 8, 5, 4, 0, -5}, /* 0x54 T */
|
||||
{235, 8, 5, 4, 0, -5}, /* 0x55 U */
|
||||
{240, 8, 5, 4, 0, -5}, /* 0x56 V */
|
||||
{245, 8, 5, 4, 0, -5}, /* 0x57 W */
|
||||
{250, 8, 5, 4, 0, -5}, /* 0x58 X */
|
||||
{255, 8, 5, 4, 0, -5}, /* 0x59 Y */
|
||||
{260, 8, 5, 4, 0, -5}, /* 0x5A Z */
|
||||
{265, 8, 5, 4, 0, -5}, /* 0x5B bracketleft */
|
||||
{270, 8, 3, 4, 0, -4}, /* 0x5C backslash */
|
||||
{273, 8, 5, 4, 0, -5}, /* 0x5D bracketright */
|
||||
{278, 8, 2, 4, 0, -5}, /* 0x5E asciicircum */
|
||||
{280, 8, 1, 4, 0, -1}, /* 0x5F underscore */
|
||||
{281, 8, 2, 3, 0, -5}, /* 0x60 grave */
|
||||
{283, 8, 4, 4, 0, -4}, /* 0x61 a */
|
||||
{287, 8, 5, 4, 0, -5}, /* 0x62 b */
|
||||
{292, 8, 4, 4, 0, -4}, /* 0x63 c */
|
||||
{296, 8, 5, 4, 0, -5}, /* 0x64 d */
|
||||
{301, 8, 4, 4, 0, -4}, /* 0x65 e */
|
||||
{305, 8, 5, 4, 0, -5}, /* 0x66 f */
|
||||
{310, 8, 5, 4, 0, -4}, /* 0x67 g */
|
||||
{315, 8, 5, 4, 0, -5}, /* 0x68 h */
|
||||
{320, 8, 5, 2, 0, -5}, /* 0x69 i */
|
||||
{325, 8, 6, 4, 0, -5}, /* 0x6A j */
|
||||
{331, 8, 5, 4, 0, -5}, /* 0x6B k */
|
||||
{336, 8, 5, 4, 0, -5}, /* 0x6C l */
|
||||
{341, 8, 4, 4, 0, -4}, /* 0x6D m */
|
||||
{345, 8, 4, 4, 0, -4}, /* 0x6E n */
|
||||
{349, 8, 4, 4, 0, -4}, /* 0x6F o */
|
||||
{353, 8, 5, 4, 0, -4}, /* 0x70 p */
|
||||
{358, 8, 5, 4, 0, -4}, /* 0x71 q */
|
||||
{363, 8, 4, 4, 0, -4}, /* 0x72 r */
|
||||
{367, 8, 4, 4, 0, -4}, /* 0x73 s */
|
||||
{371, 8, 5, 4, 0, -5}, /* 0x74 t */
|
||||
{376, 8, 4, 4, 0, -4}, /* 0x75 u */
|
||||
{380, 8, 4, 4, 0, -4}, /* 0x76 v */
|
||||
{384, 8, 4, 4, 0, -4}, /* 0x77 w */
|
||||
{388, 8, 4, 4, 0, -4}, /* 0x78 x */
|
||||
{392, 8, 5, 4, 0, -4}, /* 0x79 y */
|
||||
{397, 8, 4, 4, 0, -4}, /* 0x7A z */
|
||||
{401, 8, 5, 4, 0, -5}, /* 0x7B braceleft */
|
||||
{406, 8, 5, 2, 0, -5}, /* 0x7C bar */
|
||||
{411, 8, 5, 4, 0, -5}, /* 0x7D braceright */
|
||||
{416, 8, 2, 4, 0, -5}, /* 0x7E asciitilde */
|
||||
#if (TOMTHUMB_USE_EXTENDED)
|
||||
{418, 8, 5, 2, 0, -5}, /* 0xA1 exclamdown */
|
||||
{423, 8, 5, 4, 0, -5}, /* 0xA2 cent */
|
||||
{428, 8, 5, 4, 0, -5}, /* 0xA3 sterling */
|
||||
{433, 8, 5, 4, 0, -5}, /* 0xA4 currency */
|
||||
{438, 8, 5, 4, 0, -5}, /* 0xA5 yen */
|
||||
{443, 8, 5, 2, 0, -5}, /* 0xA6 brokenbar */
|
||||
{448, 8, 5, 4, 0, -5}, /* 0xA7 section */
|
||||
{453, 8, 1, 4, 0, -5}, /* 0xA8 dieresis */
|
||||
{454, 8, 3, 4, 0, -5}, /* 0xA9 copyright */
|
||||
{457, 8, 5, 4, 0, -5}, /* 0xAA ordfeminine */
|
||||
{462, 8, 3, 3, 0, -5}, /* 0xAB guillemotleft */
|
||||
{465, 8, 2, 4, 0, -4}, /* 0xAC logicalnot */
|
||||
{467, 8, 1, 3, 0, -3}, /* 0xAD softhyphen */
|
||||
{468, 8, 3, 4, 0, -5}, /* 0xAE registered */
|
||||
{471, 8, 1, 4, 0, -5}, /* 0xAF macron */
|
||||
{472, 8, 3, 4, 0, -5}, /* 0xB0 degree */
|
||||
{475, 8, 5, 4, 0, -5}, /* 0xB1 plusminus */
|
||||
{480, 8, 3, 4, 0, -5}, /* 0xB2 twosuperior */
|
||||
{483, 8, 3, 4, 0, -5}, /* 0xB3 threesuperior */
|
||||
{486, 8, 2, 3, 0, -5}, /* 0xB4 acute */
|
||||
{488, 8, 5, 4, 0, -5}, /* 0xB5 mu */
|
||||
{493, 8, 5, 4, 0, -5}, /* 0xB6 paragraph */
|
||||
{498, 8, 3, 4, 0, -4}, /* 0xB7 periodcentered */
|
||||
{501, 8, 3, 4, 0, -3}, /* 0xB8 cedilla */
|
||||
{504, 8, 3, 2, 0, -5}, /* 0xB9 onesuperior */
|
||||
{507, 8, 5, 4, 0, -5}, /* 0xBA ordmasculine */
|
||||
{512, 8, 3, 3, 0, -5}, /* 0xBB guillemotright */
|
||||
{515, 8, 5, 4, 0, -5}, /* 0xBC onequarter */
|
||||
{520, 8, 5, 4, 0, -5}, /* 0xBD onehalf */
|
||||
{525, 8, 5, 4, 0, -5}, /* 0xBE threequarters */
|
||||
{530, 8, 5, 4, 0, -5}, /* 0xBF questiondown */
|
||||
{535, 8, 5, 4, 0, -5}, /* 0xC0 Agrave */
|
||||
{540, 8, 5, 4, 0, -5}, /* 0xC1 Aacute */
|
||||
{545, 8, 5, 4, 0, -5}, /* 0xC2 Acircumflex */
|
||||
{550, 8, 5, 4, 0, -5}, /* 0xC3 Atilde */
|
||||
{555, 8, 5, 4, 0, -5}, /* 0xC4 Adieresis */
|
||||
{560, 8, 5, 4, 0, -5}, /* 0xC5 Aring */
|
||||
{565, 8, 5, 4, 0, -5}, /* 0xC6 AE */
|
||||
{570, 8, 6, 4, 0, -5}, /* 0xC7 Ccedilla */
|
||||
{576, 8, 5, 4, 0, -5}, /* 0xC8 Egrave */
|
||||
{581, 8, 5, 4, 0, -5}, /* 0xC9 Eacute */
|
||||
{586, 8, 5, 4, 0, -5}, /* 0xCA Ecircumflex */
|
||||
{591, 8, 5, 4, 0, -5}, /* 0xCB Edieresis */
|
||||
{596, 8, 5, 4, 0, -5}, /* 0xCC Igrave */
|
||||
{601, 8, 5, 4, 0, -5}, /* 0xCD Iacute */
|
||||
{606, 8, 5, 4, 0, -5}, /* 0xCE Icircumflex */
|
||||
{611, 8, 5, 4, 0, -5}, /* 0xCF Idieresis */
|
||||
{616, 8, 5, 4, 0, -5}, /* 0xD0 Eth */
|
||||
{621, 8, 5, 4, 0, -5}, /* 0xD1 Ntilde */
|
||||
{626, 8, 5, 4, 0, -5}, /* 0xD2 Ograve */
|
||||
{631, 8, 5, 4, 0, -5}, /* 0xD3 Oacute */
|
||||
{636, 8, 5, 4, 0, -5}, /* 0xD4 Ocircumflex */
|
||||
{641, 8, 5, 4, 0, -5}, /* 0xD5 Otilde */
|
||||
{646, 8, 5, 4, 0, -5}, /* 0xD6 Odieresis */
|
||||
{651, 8, 3, 4, 0, -4}, /* 0xD7 multiply */
|
||||
{654, 8, 5, 4, 0, -5}, /* 0xD8 Oslash */
|
||||
{659, 8, 5, 4, 0, -5}, /* 0xD9 Ugrave */
|
||||
{664, 8, 5, 4, 0, -5}, /* 0xDA Uacute */
|
||||
{669, 8, 5, 4, 0, -5}, /* 0xDB Ucircumflex */
|
||||
{674, 8, 5, 4, 0, -5}, /* 0xDC Udieresis */
|
||||
{679, 8, 5, 4, 0, -5}, /* 0xDD Yacute */
|
||||
{684, 8, 5, 4, 0, -5}, /* 0xDE Thorn */
|
||||
{689, 8, 6, 4, 0, -5}, /* 0xDF germandbls */
|
||||
{695, 8, 5, 4, 0, -5}, /* 0xE0 agrave */
|
||||
{700, 8, 5, 4, 0, -5}, /* 0xE1 aacute */
|
||||
{705, 8, 5, 4, 0, -5}, /* 0xE2 acircumflex */
|
||||
{710, 8, 5, 4, 0, -5}, /* 0xE3 atilde */
|
||||
{715, 8, 5, 4, 0, -5}, /* 0xE4 adieresis */
|
||||
{720, 8, 5, 4, 0, -5}, /* 0xE5 aring */
|
||||
{725, 8, 4, 4, 0, -4}, /* 0xE6 ae */
|
||||
{729, 8, 5, 4, 0, -4}, /* 0xE7 ccedilla */
|
||||
{734, 8, 5, 4, 0, -5}, /* 0xE8 egrave */
|
||||
{739, 8, 5, 4, 0, -5}, /* 0xE9 eacute */
|
||||
{744, 8, 5, 4, 0, -5}, /* 0xEA ecircumflex */
|
||||
{749, 8, 5, 4, 0, -5}, /* 0xEB edieresis */
|
||||
{754, 8, 5, 3, 0, -5}, /* 0xEC igrave */
|
||||
{759, 8, 5, 3, 0, -5}, /* 0xED iacute */
|
||||
{764, 8, 5, 4, 0, -5}, /* 0xEE icircumflex */
|
||||
{769, 8, 5, 4, 0, -5}, /* 0xEF idieresis */
|
||||
{774, 8, 5, 4, 0, -5}, /* 0xF0 eth */
|
||||
{779, 8, 5, 4, 0, -5}, /* 0xF1 ntilde */
|
||||
{784, 8, 5, 4, 0, -5}, /* 0xF2 ograve */
|
||||
{789, 8, 5, 4, 0, -5}, /* 0xF3 oacute */
|
||||
{794, 8, 5, 4, 0, -5}, /* 0xF4 ocircumflex */
|
||||
{799, 8, 5, 4, 0, -5}, /* 0xF5 otilde */
|
||||
{804, 8, 5, 4, 0, -5}, /* 0xF6 odieresis */
|
||||
{809, 8, 5, 4, 0, -5}, /* 0xF7 divide */
|
||||
{814, 8, 4, 4, 0, -4}, /* 0xF8 oslash */
|
||||
{818, 8, 5, 4, 0, -5}, /* 0xF9 ugrave */
|
||||
{823, 8, 5, 4, 0, -5}, /* 0xFA uacute */
|
||||
{828, 8, 5, 4, 0, -5}, /* 0xFB ucircumflex */
|
||||
{833, 8, 5, 4, 0, -5}, /* 0xFC udieresis */
|
||||
{838, 8, 6, 4, 0, -5}, /* 0xFD yacute */
|
||||
{844, 8, 5, 4, 0, -4}, /* 0xFE thorn */
|
||||
{849, 8, 6, 4, 0, -5}, /* 0xFF ydieresis */
|
||||
{855, 8, 1, 2, 0, -1}, /* 0x11D gcircumflex */
|
||||
{856, 8, 5, 4, 0, -5}, /* 0x152 OE */
|
||||
{861, 8, 4, 4, 0, -4}, /* 0x153 oe */
|
||||
{865, 8, 5, 4, 0, -5}, /* 0x160 Scaron */
|
||||
{870, 8, 5, 4, 0, -5}, /* 0x161 scaron */
|
||||
{875, 8, 5, 4, 0, -5}, /* 0x178 Ydieresis */
|
||||
{880, 8, 5, 4, 0, -5}, /* 0x17D Zcaron */
|
||||
{885, 8, 5, 4, 0, -5}, /* 0x17E zcaron */
|
||||
{890, 8, 1, 2, 0, -1}, /* 0xEA4 uni0EA4 */
|
||||
{891, 8, 1, 2, 0, -1}, /* 0x13A0 uni13A0 */
|
||||
{892, 8, 1, 2, 0, -3}, /* 0x2022 bullet */
|
||||
{893, 8, 1, 4, 0, -1}, /* 0x2026 ellipsis */
|
||||
{894, 8, 5, 4, 0, -5}, /* 0x20AC Euro */
|
||||
{899, 8, 5, 4, 0, -5}, /* 0xFFFD uniFFFD */
|
||||
#endif /* (TOMTHUMB_USE_EXTENDED) */
|
||||
};
|
||||
|
||||
const GFXfont TomThumb PROGMEM = {(uint8_t *)TomThumbBitmaps,
|
||||
(GFXglyph *)TomThumbGlyphs, 0x20, 0x7E, 6};
|
128
PDQ_MinLib/org_01.h
Normal file
128
PDQ_MinLib/org_01.h
Normal file
@ -0,0 +1,128 @@
|
||||
// Org_v01 by Orgdot (www.orgdot.com/aliasfonts). A tiny,
|
||||
// stylized font with all characters within a 6 pixel height.
|
||||
|
||||
const uint8_t Org_01Bitmaps[] PROGMEM = {
|
||||
0xE8, 0xA0, 0x57, 0xD5, 0xF5, 0x00, 0xFD, 0x3E, 0x5F, 0x80, 0x88, 0x88,
|
||||
0x88, 0x80, 0xF4, 0xBF, 0x2E, 0x80, 0x80, 0x6A, 0x40, 0x95, 0x80, 0xAA,
|
||||
0x80, 0x5D, 0x00, 0xC0, 0xF0, 0x80, 0x08, 0x88, 0x88, 0x00, 0xFC, 0x63,
|
||||
0x1F, 0x80, 0xF8, 0xF8, 0x7F, 0x0F, 0x80, 0xF8, 0x7E, 0x1F, 0x80, 0x8C,
|
||||
0x7E, 0x10, 0x80, 0xFC, 0x3E, 0x1F, 0x80, 0xFC, 0x3F, 0x1F, 0x80, 0xF8,
|
||||
0x42, 0x10, 0x80, 0xFC, 0x7F, 0x1F, 0x80, 0xFC, 0x7E, 0x1F, 0x80, 0x90,
|
||||
0xB0, 0x2A, 0x22, 0xF0, 0xF0, 0x88, 0xA8, 0xF8, 0x4E, 0x02, 0x00, 0xFD,
|
||||
0x6F, 0x0F, 0x80, 0xFC, 0x7F, 0x18, 0x80, 0xF4, 0x7D, 0x1F, 0x00, 0xFC,
|
||||
0x21, 0x0F, 0x80, 0xF4, 0x63, 0x1F, 0x00, 0xFC, 0x3F, 0x0F, 0x80, 0xFC,
|
||||
0x3F, 0x08, 0x00, 0xFC, 0x2F, 0x1F, 0x80, 0x8C, 0x7F, 0x18, 0x80, 0xF9,
|
||||
0x08, 0x4F, 0x80, 0x78, 0x85, 0x2F, 0x80, 0x8D, 0xB1, 0x68, 0x80, 0x84,
|
||||
0x21, 0x0F, 0x80, 0xFD, 0x6B, 0x5A, 0x80, 0xFC, 0x63, 0x18, 0x80, 0xFC,
|
||||
0x63, 0x1F, 0x80, 0xFC, 0x7F, 0x08, 0x00, 0xFC, 0x63, 0x3F, 0x80, 0xFC,
|
||||
0x7F, 0x29, 0x00, 0xFC, 0x3E, 0x1F, 0x80, 0xF9, 0x08, 0x42, 0x00, 0x8C,
|
||||
0x63, 0x1F, 0x80, 0x8C, 0x62, 0xA2, 0x00, 0xAD, 0x6B, 0x5F, 0x80, 0x8A,
|
||||
0x88, 0xA8, 0x80, 0x8C, 0x54, 0x42, 0x00, 0xF8, 0x7F, 0x0F, 0x80, 0xEA,
|
||||
0xC0, 0x82, 0x08, 0x20, 0x80, 0xD5, 0xC0, 0x54, 0xF8, 0x80, 0xF1, 0xFF,
|
||||
0x8F, 0x99, 0xF0, 0xF8, 0x8F, 0x1F, 0x99, 0xF0, 0xFF, 0x8F, 0x6B, 0xA4,
|
||||
0xF9, 0x9F, 0x10, 0x8F, 0x99, 0x90, 0xF0, 0x55, 0xC0, 0x8A, 0xF9, 0x90,
|
||||
0xF8, 0xFD, 0x63, 0x10, 0xF9, 0x99, 0xF9, 0x9F, 0xF9, 0x9F, 0x80, 0xF9,
|
||||
0x9F, 0x20, 0xF8, 0x88, 0x47, 0x1F, 0x27, 0xC8, 0x42, 0x00, 0x99, 0x9F,
|
||||
0x99, 0x97, 0x8C, 0x6B, 0xF0, 0x96, 0x69, 0x99, 0x9F, 0x10, 0x2E, 0x8F,
|
||||
0x2B, 0x22, 0xF8, 0x89, 0xA8, 0x0F, 0xE0};
|
||||
|
||||
const GFXglyph Org_01Glyphs[] PROGMEM = {{0, 0, 0, 6, 0, 1}, // 0x20 ' '
|
||||
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
|
||||
{1, 3, 1, 4, 0, -4}, // 0x22 '"'
|
||||
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
|
||||
{6, 5, 5, 6, 0, -4}, // 0x24 '$'
|
||||
{10, 5, 5, 6, 0, -4}, // 0x25 '%'
|
||||
{14, 5, 5, 6, 0, -4}, // 0x26 '&'
|
||||
{18, 1, 1, 2, 0, -4}, // 0x27 '''
|
||||
{19, 2, 5, 3, 0, -4}, // 0x28 '('
|
||||
{21, 2, 5, 3, 0, -4}, // 0x29 ')'
|
||||
{23, 3, 3, 4, 0, -3}, // 0x2A '*'
|
||||
{25, 3, 3, 4, 0, -3}, // 0x2B '+'
|
||||
{27, 1, 2, 2, 0, 0}, // 0x2C ','
|
||||
{28, 4, 1, 5, 0, -2}, // 0x2D '-'
|
||||
{29, 1, 1, 2, 0, 0}, // 0x2E '.'
|
||||
{30, 5, 5, 6, 0, -4}, // 0x2F '/'
|
||||
{34, 5, 5, 6, 0, -4}, // 0x30 '0'
|
||||
{38, 1, 5, 2, 0, -4}, // 0x31 '1'
|
||||
{39, 5, 5, 6, 0, -4}, // 0x32 '2'
|
||||
{43, 5, 5, 6, 0, -4}, // 0x33 '3'
|
||||
{47, 5, 5, 6, 0, -4}, // 0x34 '4'
|
||||
{51, 5, 5, 6, 0, -4}, // 0x35 '5'
|
||||
{55, 5, 5, 6, 0, -4}, // 0x36 '6'
|
||||
{59, 5, 5, 6, 0, -4}, // 0x37 '7'
|
||||
{63, 5, 5, 6, 0, -4}, // 0x38 '8'
|
||||
{67, 5, 5, 6, 0, -4}, // 0x39 '9'
|
||||
{71, 1, 4, 2, 0, -3}, // 0x3A ':'
|
||||
{72, 1, 4, 2, 0, -3}, // 0x3B ';'
|
||||
{73, 3, 5, 4, 0, -4}, // 0x3C '<'
|
||||
{75, 4, 3, 5, 0, -3}, // 0x3D '='
|
||||
{77, 3, 5, 4, 0, -4}, // 0x3E '>'
|
||||
{79, 5, 5, 6, 0, -4}, // 0x3F '?'
|
||||
{83, 5, 5, 6, 0, -4}, // 0x40 '@'
|
||||
{87, 5, 5, 6, 0, -4}, // 0x41 'A'
|
||||
{91, 5, 5, 6, 0, -4}, // 0x42 'B'
|
||||
{95, 5, 5, 6, 0, -4}, // 0x43 'C'
|
||||
{99, 5, 5, 6, 0, -4}, // 0x44 'D'
|
||||
{103, 5, 5, 6, 0, -4}, // 0x45 'E'
|
||||
{107, 5, 5, 6, 0, -4}, // 0x46 'F'
|
||||
{111, 5, 5, 6, 0, -4}, // 0x47 'G'
|
||||
{115, 5, 5, 6, 0, -4}, // 0x48 'H'
|
||||
{119, 5, 5, 6, 0, -4}, // 0x49 'I'
|
||||
{123, 5, 5, 6, 0, -4}, // 0x4A 'J'
|
||||
{127, 5, 5, 6, 0, -4}, // 0x4B 'K'
|
||||
{131, 5, 5, 6, 0, -4}, // 0x4C 'L'
|
||||
{135, 5, 5, 6, 0, -4}, // 0x4D 'M'
|
||||
{139, 5, 5, 6, 0, -4}, // 0x4E 'N'
|
||||
{143, 5, 5, 6, 0, -4}, // 0x4F 'O'
|
||||
{147, 5, 5, 6, 0, -4}, // 0x50 'P'
|
||||
{151, 5, 5, 6, 0, -4}, // 0x51 'Q'
|
||||
{155, 5, 5, 6, 0, -4}, // 0x52 'R'
|
||||
{159, 5, 5, 6, 0, -4}, // 0x53 'S'
|
||||
{163, 5, 5, 6, 0, -4}, // 0x54 'T'
|
||||
{167, 5, 5, 6, 0, -4}, // 0x55 'U'
|
||||
{171, 5, 5, 6, 0, -4}, // 0x56 'V'
|
||||
{175, 5, 5, 6, 0, -4}, // 0x57 'W'
|
||||
{179, 5, 5, 6, 0, -4}, // 0x58 'X'
|
||||
{183, 5, 5, 6, 0, -4}, // 0x59 'Y'
|
||||
{187, 5, 5, 6, 0, -4}, // 0x5A 'Z'
|
||||
{191, 2, 5, 3, 0, -4}, // 0x5B '['
|
||||
{193, 5, 5, 6, 0, -4}, // 0x5C '\'
|
||||
{197, 2, 5, 3, 0, -4}, // 0x5D ']'
|
||||
{199, 3, 2, 4, 0, -4}, // 0x5E '^'
|
||||
{200, 5, 1, 6, 0, 1}, // 0x5F '_'
|
||||
{201, 1, 1, 2, 0, -4}, // 0x60 '`'
|
||||
{202, 4, 4, 5, 0, -3}, // 0x61 'a'
|
||||
{204, 4, 5, 5, 0, -4}, // 0x62 'b'
|
||||
{207, 4, 4, 5, 0, -3}, // 0x63 'c'
|
||||
{209, 4, 5, 5, 0, -4}, // 0x64 'd'
|
||||
{212, 4, 4, 5, 0, -3}, // 0x65 'e'
|
||||
{214, 3, 5, 4, 0, -4}, // 0x66 'f'
|
||||
{216, 4, 5, 5, 0, -3}, // 0x67 'g'
|
||||
{219, 4, 5, 5, 0, -4}, // 0x68 'h'
|
||||
{222, 1, 4, 2, 0, -3}, // 0x69 'i'
|
||||
{223, 2, 5, 3, 0, -3}, // 0x6A 'j'
|
||||
{225, 4, 5, 5, 0, -4}, // 0x6B 'k'
|
||||
{228, 1, 5, 2, 0, -4}, // 0x6C 'l'
|
||||
{229, 5, 4, 6, 0, -3}, // 0x6D 'm'
|
||||
{232, 4, 4, 5, 0, -3}, // 0x6E 'n'
|
||||
{234, 4, 4, 5, 0, -3}, // 0x6F 'o'
|
||||
{236, 4, 5, 5, 0, -3}, // 0x70 'p'
|
||||
{239, 4, 5, 5, 0, -3}, // 0x71 'q'
|
||||
{242, 4, 4, 5, 0, -3}, // 0x72 'r'
|
||||
{244, 4, 4, 5, 0, -3}, // 0x73 's'
|
||||
{246, 5, 5, 6, 0, -4}, // 0x74 't'
|
||||
{250, 4, 4, 5, 0, -3}, // 0x75 'u'
|
||||
{252, 4, 4, 5, 0, -3}, // 0x76 'v'
|
||||
{254, 5, 4, 6, 0, -3}, // 0x77 'w'
|
||||
{257, 4, 4, 5, 0, -3}, // 0x78 'x'
|
||||
{259, 4, 5, 5, 0, -3}, // 0x79 'y'
|
||||
{262, 4, 4, 5, 0, -3}, // 0x7A 'z'
|
||||
{264, 3, 5, 4, 0, -4}, // 0x7B '{'
|
||||
{266, 1, 5, 2, 0, -4}, // 0x7C '|'
|
||||
{267, 3, 5, 4, 0, -4}, // 0x7D '}'
|
||||
{269, 5, 3, 6, 0, -3}}; // 0x7E '~'
|
||||
|
||||
const GFXfont Org_01 PROGMEM = {(uint8_t *)Org_01Bitmaps,
|
||||
(GFXglyph *)Org_01Glyphs, 0x20, 0x7E, 7};
|
||||
|
||||
// Approx. 943 bytes
|
77
README.md
77
README.md
@ -1,9 +1,72 @@
|
||||
# ubitxv6
|
||||
uBitx v6.3.1 Arduino sketch
|
||||
IMPORTANT: It will compile only if you place this in the Arduino's own sketch directory! This is because of the restricted places that the Arduino searches for it's include files (the headers).
|
||||
# uBiTXv6
|
||||
|
||||
- This is refactored to remove dependencies on any library except the standard Arduino libraries of SPI, I2C, EEPROM, etc.
|
||||
- This works with ILI9341 display controller. The pins used by the TFT display are the same as that of the 16x2 LCD display of the previous versions.
|
||||
- As the files are now split into .cpp files, the nano gui, morse reader, etc. can be reused in other projects as well
|
||||
This project is found at https://github.com/reedbn/ubitxv6/
|
||||
|
||||
This is released under GPL v3 license.
|
||||
It was forked from https://github.com/afarhan/ubitxv6/
|
||||
|
||||
The purpose of this project is to clean up (modularize) the source code, and add features that were not present
|
||||
in Ashhar's original version of the project, without requiring any hardware modifications to a stock uBiTXv6.
|
||||
|
||||
New features include:
|
||||
|
||||
* Much faster screen refresh (vs Ashhar's 6.3.1 aka 6.0 release)
|
||||
* Morse code readback for sightless operation
|
||||
* Save/recall your favorite frequencies
|
||||
* When adjusting settings, the existing/current setting is shown as reference
|
||||
* Cancel touch recalibration
|
||||
|
||||
User Manual: https://docs.google.com/document/d/1jlllZbvFMCzO1MJLzlJDGb10HXSehlFNMDPsxGJZtvY/edit?usp=drivesdk
|
||||
|
||||
# Installing on Your Radio
|
||||
|
||||
There are plenty of tutorials on how to upload sketches to Arduino Nanos. Just search for them. Addtionally,
|
||||
Ashhar created a video explaining the process specifically for uBiTX v6: https://www.youtube.com/watch?v=3n_V3prSJ_E
|
||||
|
||||
I developed this code using the Arduino IDE 1.8.9 toolchain, with -Wall and -Wextra compiler options turned on.
|
||||
Arduino IDE 1.8.13 was reported to compile too big (see https://groups.io/g/BITX20/topic/75008576), but this
|
||||
should be resolved in this project's tag R1.5.1.
|
||||
|
||||
# Personalized Callsign
|
||||
|
||||
To edit the callsign displayed, open the file `callsign.cpp` and change the string. Then re-compile and upload.
|
||||
|
||||
# Future Features/Modifications
|
||||
|
||||
There are some features that would be good to add, but I just didn't get around to.
|
||||
|
||||
* Setting to choose the tuning step size
|
||||
* Setting to choose whether or not the knob tuning should accelerate (current behavior) or have a fixed interval
|
||||
* Provide an option in each menu screen to load the default option for each setting
|
||||
|
||||
While the current code (as of 2020-05-05) is ~100 bytes shy of the full 30720 available on the nano, there's still
|
||||
opportunity to add new features by "creating" room. Below is a list of places you might create room:
|
||||
|
||||
I added lots of bounds checking, especially on string writes, that, if removed, could free a good number of bytes.
|
||||
While keeping them is best practice, for a non-IoT, non-critical piece of hardware, it shouldn't be a huge issue.
|
||||
|
||||
I added the RACK to the CAT to better emulate the FT-817 (I hope, at least!). Removing the RACK's and just leaving
|
||||
the default ACK's will also free up bytes.
|
||||
|
||||
I added a bunch of strings to the menuing with the intention of helping people understand their functions, but
|
||||
technically they're not necessary, and could all be removed.
|
||||
|
||||
I switched to a smaller footprint font than Ashhar's original code, but there are MUCH smaller fonts out there.
|
||||
Changing to a lower resolution, scaled up font can save hundreds or thousands of bytes, but won't look as pretty.
|
||||
Also, the star, gear, and numpad icons will need to be either added to the new font, or replaced with characters.
|
||||
|
||||
The first change I made to this fork was to replace Ashhar's original (incredibly slow) screen drawing routines
|
||||
with PDQ. Since that change, Ashhar has updated his drawing routine to be MUCH faster than his original, but
|
||||
still slightly slower than PDQ. It may be that Ashhar's new routines are smaller that PDQ, but I don't actually
|
||||
know that for certain.
|
||||
|
||||
There are a good number of instances of back-to-back calls of strncpy_P and displayText. Creating a single
|
||||
function that performs these operations together, and then calling that new function instead of the
|
||||
back-to-back calls everywhere may save space.
|
||||
|
||||
# License
|
||||
|
||||
The majority of this code is released under GPL v3 license, per Ashhar's original code.
|
||||
|
||||
The files in the PDQ_MinLib subdirectory were copied from https://github.com/XarkLabs/PDQ_GFX_Libs, and have Adafruit's BSD License.
|
||||
|
||||
The files in the toneAC2 were copied from https://bitbucket.org/teckel12/arduino-toneac2/wiki/Home, and are GPL v3 licensed.
|
||||
|
129
bands.cpp
Normal file
129
bands.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "bands.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* These are the bands for USA. Your bands may vary
|
||||
*/
|
||||
struct Band_t {
|
||||
uint32_t min;
|
||||
uint32_t max;
|
||||
uint8_t band_meters;
|
||||
char name[3];//Two characters + null terminator. Fixed width so we don't need to build separate pointers
|
||||
};
|
||||
|
||||
const char UNKNOWN_BAND_NAME [] PROGMEM = "??";
|
||||
|
||||
constexpr Band_t bands [] PROGMEM {
|
||||
// { 0UL, 255UL, 255, "U8"},//Utility conversion option
|
||||
// { 0UL, 65535UL, 254, "UF"},//Utility conversion option
|
||||
// { 530000UL, 1700000UL, 253, "AM"},//Broadcast AM, actually centers at 268, but uint8 can't do that
|
||||
// { 1800000UL, 2000000UL, 160, "A0"},//0xA0 is 160
|
||||
{ 3500000UL, 3800000UL, 80, "80"},
|
||||
// { 5330500UL, 5403500UL, 60, "60"},
|
||||
{ 7000000UL, 7200000UL, 40, "40"},
|
||||
{10100000UL, 10150000UL, 30, "30"},
|
||||
{14000000UL, 14350000UL, 20, "20"},
|
||||
{18068000UL, 18168000UL, 17, "17"},
|
||||
{21000000UL, 21450000UL, 15, "15"},
|
||||
{24890000UL, 24990000UL, 12, "12"},
|
||||
// {26965000UL, 27405000UL, 11, "CB"},//Citizen's Band
|
||||
{28000000UL, 29700000UL, 10, "10"},
|
||||
};
|
||||
constexpr uint8_t NUM_BANDS = sizeof(bands)/sizeof(bands[0]);
|
||||
|
||||
int8_t findBandIndexFromBand(const uint8_t target_band)
|
||||
{
|
||||
Band_t band;
|
||||
for(uint8_t i = 0; i < NUM_BANDS; ++i){
|
||||
memcpy_P(&band,&bands[i],sizeof(band));
|
||||
if(target_band == band.band_meters){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t findBandIndexFromFreq(uint32_t frequency)
|
||||
{
|
||||
Band_t band;
|
||||
for(uint8_t i = 0; i < NUM_BANDS; ++i){
|
||||
memcpy_P(&band,&bands[i],sizeof(band));
|
||||
if(frequency <= band.max){
|
||||
if(band.min <= frequency){
|
||||
return i;
|
||||
}
|
||||
//No bands overlap, and they are ordered in strictly increasing frequency, so we need search no further
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void getBandString(const unsigned long frequency,
|
||||
char* band_string_out,
|
||||
uint16_t max_string_length)
|
||||
{
|
||||
int8_t band_index = findBandIndexFromFreq(frequency);
|
||||
if(-1 == band_index){
|
||||
strncpy_P(band_string_out,UNKNOWN_BAND_NAME,max_string_length);
|
||||
}
|
||||
else{
|
||||
Band_t band;
|
||||
memcpy_P(&band,&bands[band_index],sizeof(band));
|
||||
strncpy_P(band_string_out,band.name,max_string_length);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getFreqInBand(const uint32_t frequency,
|
||||
const uint8_t target_band)
|
||||
{
|
||||
int8_t target_band_index = findBandIndexFromBand(target_band);
|
||||
if(-1 == target_band_index){
|
||||
//Hard to target a band we don't know about...
|
||||
return frequency;
|
||||
}
|
||||
|
||||
//See if we're currrently in a valid band
|
||||
int8_t current_band_index = findBandIndexFromFreq(frequency);
|
||||
|
||||
if(-1 == current_band_index){
|
||||
//We're not in a known band - just go to the center of the target band
|
||||
Band_t band;
|
||||
memcpy_P(&band,&bands[target_band_index],sizeof(band));
|
||||
return band.min + ((band.max - band.min)/2/100)*100;//midpoint truncated to 100Hz resolution
|
||||
}
|
||||
else{
|
||||
//We're in a known band. Match the relative position in the target band.
|
||||
Band_t current_band;
|
||||
memcpy_P(¤t_band,&bands[current_band_index],sizeof(current_band));
|
||||
Band_t target_band;
|
||||
memcpy_P(&target_band,&bands[target_band_index],sizeof(target_band));
|
||||
const uint32_t range_current = current_band.max - current_band.min;
|
||||
const uint32_t range_target = target_band.max - target_band.min;
|
||||
return (((frequency - current_band.min) * (uint64_t)range_target / range_current + target_band.min)/100)*100;//truncated 100Hz
|
||||
}
|
||||
}
|
||||
|
||||
bool isFreqInBand(const uint32_t frequency,
|
||||
const uint8_t check_band)
|
||||
{
|
||||
int8_t band_index = findBandIndexFromBand(check_band);
|
||||
|
||||
if(-1 == band_index){
|
||||
//Unknown band - can't be in it
|
||||
return false;
|
||||
}
|
||||
|
||||
Band_t band;
|
||||
memcpy_P(&band,&bands[band_index],sizeof(band));
|
||||
if( (frequency <= band.max)
|
||||
&& (band.min <= frequency)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
11
bands.h
Normal file
11
bands.h
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdint.h>
|
||||
|
||||
void getBandString(const uint32_t frequency,
|
||||
char* band_string_out,
|
||||
const uint16_t max_string_length);
|
||||
|
||||
uint32_t getFreqInBand(const uint32_t frequency,
|
||||
const uint8_t target_band);
|
||||
|
||||
bool isFreqInBand(const uint32_t frequency,
|
||||
const uint8_t band);
|
50
button.cpp
Normal file
50
button.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "button.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "color_theme.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
|
||||
void drawButton(Button* button)
|
||||
{
|
||||
uint16_t tc = COLOR_INACTIVE_TEXT;
|
||||
uint16_t bgc = COLOR_INACTIVE_BACKGROUND;
|
||||
const uint16_t bdc = COLOR_INACTIVE_BORDER;
|
||||
switch(button->status())
|
||||
{
|
||||
case ButtonStatus_e::Stateless://Fallthrough intended
|
||||
case ButtonStatus_e::Inactive://Fallthrough intended
|
||||
default:
|
||||
{
|
||||
//Colors are initialized for this, so we're done
|
||||
break;
|
||||
}
|
||||
case ButtonStatus_e::Active:
|
||||
{
|
||||
tc = COLOR_ACTIVE_TEXT;
|
||||
bgc = COLOR_ACTIVE_BACKGROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(nullptr != button->text){
|
||||
strncpy_P(b,button->text,sizeof(b));
|
||||
}
|
||||
else if(nullptr != button->text_override){
|
||||
button->text_override(b,sizeof(b));
|
||||
}
|
||||
else{
|
||||
//Something's messed up
|
||||
//Serial.println(F("No text for button!"));
|
||||
return;
|
||||
}
|
||||
displayText(b, button->x, button->y, button->w, button->h, tc, bgc, bdc);
|
||||
}
|
||||
|
||||
void extractAndDrawButton(Button* button_out, const Button* button_P)
|
||||
{
|
||||
memcpy_P(button_out,button_P,sizeof(*button_out));
|
||||
drawButton(button_out);
|
||||
}
|
20
button.h
Normal file
20
button.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum ButtonStatus_e : uint8_t {
|
||||
Stateless,
|
||||
Inactive,
|
||||
Active
|
||||
};
|
||||
|
||||
struct Button {
|
||||
int16_t x, y, w, h;
|
||||
const char* text;//nullptr if text_override should be used
|
||||
void (*text_override)(char* text_out, const uint16_t max_text_size);//nullptr if text should be used
|
||||
ButtonStatus_e (*status)();//Used for coloring and morse menu
|
||||
void (*on_select)();//Action to take when selected
|
||||
char morse;
|
||||
};
|
||||
|
||||
void extractAndDrawButton(Button* button_out, const Button* button_P);
|
7
button_press_e.h
Normal file
7
button_press_e.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
enum ButtonPress_e : uint8_t {
|
||||
NotPressed,
|
||||
ShortPress,
|
||||
LongPress
|
||||
};
|
5
button_timing.h
Normal file
5
button_timing.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
static const uint8_t DEBOUNCE_DELAY_MS = 50;
|
||||
static const uint16_t LONG_PRESS_TIME_MS = 3000;
|
||||
static const uint8_t LONG_PRESS_POLL_TIME_MS = 10;
|
4
callsign.cpp
Normal file
4
callsign.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
#include "callsign.h"
|
||||
|
||||
const char CALLSIGN_STRING_PRIVATE [] PROGMEM = "DK1MI";
|
||||
const char* const CALLSIGN_STRING = CALLSIGN_STRING_PRIVATE;
|
9
callsign.h
Normal file
9
callsign.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
/*
|
||||
* VERSION_STRING is a PROGMEM string, so extract it before use, e.g.
|
||||
* strncpy_P(char_buffer_out,VERSION_STRING,size_of_char_buffer_out);
|
||||
*/
|
||||
extern const char* const CALLSIGN_STRING;
|
22
color_theme.h
Normal file
22
color_theme.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "colors.h"
|
||||
|
||||
static const unsigned int COLOR_TEXT = DISPLAY_WHITE;
|
||||
static const unsigned int COLOR_BACKGROUND = DISPLAY_BLACK;
|
||||
|
||||
static const unsigned int COLOR_ACTIVE_VFO_TEXT = DISPLAY_WHITE;
|
||||
static const unsigned int COLOR_ACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
|
||||
|
||||
static const unsigned int COLOR_INACTIVE_VFO_TEXT = DISPLAY_GREEN;
|
||||
static const unsigned int COLOR_INACTIVE_VFO_BACKGROUND = DISPLAY_BLACK;
|
||||
|
||||
static const unsigned int COLOR_INACTIVE_TEXT = DISPLAY_GREEN;
|
||||
static const unsigned int COLOR_INACTIVE_BACKGROUND = DISPLAY_BLACK;
|
||||
static const unsigned int COLOR_INACTIVE_BORDER = DISPLAY_DARKGREY;
|
||||
|
||||
static const unsigned int COLOR_ACTIVE_TEXT = DISPLAY_BLACK;
|
||||
static const unsigned int COLOR_ACTIVE_BACKGROUND = DISPLAY_GREEN;
|
||||
static const unsigned int COLOR_ACTIVE_BORDER = DISPLAY_WHITE;
|
||||
|
||||
static const unsigned int COLOR_VERSION_TEXT = DISPLAY_GREEN;
|
20
colors.h
Normal file
20
colors.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Color definitions
|
||||
static const uint16_t DISPLAY_BLACK = 0x0000; ///< 0, 0, 0
|
||||
static const uint16_t DISPLAY_NAVY = 0x000F; ///< 0, 0, 123
|
||||
static const uint16_t DISPLAY_DARKGREEN = 0x03E0; ///< 0, 125, 0
|
||||
static const uint16_t DISPLAY_DARKCYAN = 0x03EF; ///< 0, 125, 123
|
||||
static const uint16_t DISPLAY_MAROON = 0x7800; ///< 123, 0, 0
|
||||
static const uint16_t DISPLAY_PURPLE = 0x780F; ///< 123, 0, 123
|
||||
static const uint16_t DISPLAY_OLIVE = 0x7BE0; ///< 123, 125, 0
|
||||
static const uint16_t DISPLAY_LIGHTGREY = 0xC618; ///< 198, 195, 198
|
||||
static const uint16_t DISPLAY_DARKGREY = 0x7BEF; ///< 123, 125, 123
|
||||
static const uint16_t DISPLAY_BLUE = 0x001F; ///< 0, 0, 255
|
||||
static const uint16_t DISPLAY_GREEN = 0x07E0; ///< 0, 255, 0
|
||||
static const uint16_t DISPLAY_CYAN = 0x07FF; ///< 0, 255, 255
|
||||
static const uint16_t DISPLAY_RED = 0xF800; ///< 255, 0, 0
|
||||
static const uint16_t DISPLAY_MAGENTA = 0xF81F; ///< 255, 0, 255
|
||||
static const uint16_t DISPLAY_YELLOW = 0xFFE0; ///< 255, 255, 0
|
||||
static const uint16_t DISPLAY_WHITE = 0xFFFF; ///< 255, 255, 255
|
||||
static const uint16_t DISPLAY_ORANGE = 0xFD20; ///< 255, 165, 0
|
||||
static const uint16_t DISPLAY_GREENYELLOW = 0xAFE5; ///< 173, 255, 41
|
||||
static const uint16_t DISPLAY_PINK = 0xFC18; ///< 255, 130, 198
|
125
encoder.cpp
Normal file
125
encoder.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "encoder.h"
|
||||
#include "pin_definitions.h"
|
||||
|
||||
//Normal encoder state
|
||||
uint8_t prev_enc = 0;
|
||||
int8_t enc_count = 0;
|
||||
|
||||
//Momentum encoder state
|
||||
int16_t enc_count_periodic = 0;
|
||||
int8_t momentum[3] = {0};
|
||||
static const uint16_t CALLBACK_PERIOD_MS = 200;
|
||||
static const uint8_t MOMENTUM_MULTIPLIER = 1;
|
||||
|
||||
uint8_t enc_state (void)
|
||||
{
|
||||
return (digitalRead(PIN_ENC_B) << 1) + (digitalRead(PIN_ENC_A) << 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SmittyHalibut's encoder handling, using interrupts. Should be quicker, smoother handling.
|
||||
* The Interrupt Service Routine for Pin Change Interrupts on A0-A5.
|
||||
*/
|
||||
ISR (PCINT1_vect)
|
||||
{
|
||||
uint8_t cur_enc = enc_state();
|
||||
if (prev_enc == cur_enc) {
|
||||
//Serial.println("unnecessary ISR");
|
||||
return;
|
||||
}
|
||||
//Serial.print(prev_enc);
|
||||
//Serial.println(cur_enc);
|
||||
|
||||
//these transitions point to the enccoder being rotated anti-clockwise
|
||||
if ((prev_enc == 0 && cur_enc == 2) ||
|
||||
(prev_enc == 2 && cur_enc == 3) ||
|
||||
(prev_enc == 3 && cur_enc == 1) ||
|
||||
(prev_enc == 1 && cur_enc == 0))
|
||||
{
|
||||
enc_count -= 1;
|
||||
enc_count_periodic -= 1;
|
||||
}
|
||||
//these transitions point to the enccoder being rotated clockwise
|
||||
else if ((prev_enc == 0 && cur_enc == 1) ||
|
||||
(prev_enc == 1 && cur_enc == 3) ||
|
||||
(prev_enc == 3 && cur_enc == 2) ||
|
||||
(prev_enc == 2 && cur_enc == 0))
|
||||
{
|
||||
enc_count += 1;
|
||||
enc_count_periodic += 1;
|
||||
}
|
||||
else {
|
||||
// A change to two states, we can't tell whether it was forward or backward, so we skip it.
|
||||
//Serial.println("skip");
|
||||
}
|
||||
prev_enc = cur_enc; // Record state for next pulse interpretation
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the encoder interrupts and global variables.
|
||||
*/
|
||||
void pci_setup(byte pin) {
|
||||
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
|
||||
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
|
||||
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
|
||||
}
|
||||
|
||||
void enc_setup(void)
|
||||
{
|
||||
enc_count = 0;
|
||||
// This is already done in setup() ?
|
||||
//pinMode(PIN_ENC_A, INPUT);
|
||||
//pinMode(PIN_ENC_B, INPUT);
|
||||
prev_enc = enc_state();
|
||||
|
||||
// Setup Pin Change Interrupts for the encoder inputs
|
||||
pci_setup(PIN_ENC_A);
|
||||
pci_setup(PIN_ENC_B);
|
||||
|
||||
//Set up timer interrupt for momentum
|
||||
TCCR1A = 0;//"normal" mode
|
||||
TCCR1B = 3;//clock divider of 64
|
||||
TCNT1 = 0;//start counting at 0
|
||||
OCR1A = F_CPU * (unsigned long)CALLBACK_PERIOD_MS / 1000 / 64;//set target number
|
||||
TIMSK1 |= (1 << OCIE1A);//enable interrupt
|
||||
}
|
||||
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
momentum[2] = momentum[1];
|
||||
momentum[1] = momentum[0];
|
||||
momentum[0] = enc_count_periodic;
|
||||
enc_count_periodic = 0;
|
||||
}
|
||||
|
||||
int8_t min_momentum_mag()
|
||||
{
|
||||
int8_t min_mag = 127;
|
||||
for(uint8_t i = 0; i < sizeof(momentum)/sizeof(momentum[0]); ++i){
|
||||
int8_t mag = abs(momentum[i]);
|
||||
if(mag < min_mag){
|
||||
min_mag = mag;
|
||||
}
|
||||
}
|
||||
return min_mag;
|
||||
}
|
||||
|
||||
int enc_read(void) {
|
||||
if(0 != enc_count){
|
||||
int16_t ret = enc_count;
|
||||
int8_t s = (enc_count < 0) ? -1 : 1;
|
||||
int8_t momentum_mag = min_momentum_mag();
|
||||
if(momentum_mag >= 20){
|
||||
ret += s*40;
|
||||
}
|
||||
else if(momentum_mag >= 5){
|
||||
ret += s*(20 + momentum_mag)/(20 - momentum_mag);
|
||||
}
|
||||
enc_count = 0;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
4
encoder.h
Normal file
4
encoder.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void enc_setup(void);
|
||||
int enc_read(void);
|
232
keyer.cpp
232
keyer.cpp
@ -1,5 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "ubitx.h"
|
||||
#include "toneAC2/toneAC2.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "settings.h"
|
||||
#include "tuner.h"
|
||||
|
||||
/**
|
||||
CW Keyer
|
||||
@ -28,72 +30,25 @@
|
||||
*/
|
||||
|
||||
//CW ADC Range
|
||||
int cwAdcSTFrom = 0;
|
||||
int cwAdcSTTo = 50;
|
||||
int cwAdcBothFrom = 51;
|
||||
int cwAdcBothTo = 300;
|
||||
int cwAdcDotFrom = 301;
|
||||
int cwAdcDotTo = 600;
|
||||
int cwAdcDashFrom = 601;
|
||||
int cwAdcDashTo = 800;
|
||||
//byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
|
||||
//static const unsigned int cwAdcSTFrom = 0;
|
||||
static const unsigned int cwAdcSTTo = 50;
|
||||
static const unsigned int cwAdcBothFrom = cwAdcSTTo + 1;
|
||||
static const unsigned int cwAdcBothTo = 300;
|
||||
static const unsigned int cwAdcDotFrom = cwAdcBothTo + 1;
|
||||
static const unsigned int cwAdcDotTo = 600;
|
||||
static const unsigned int cwAdcDashFrom = cwAdcDotTo + 1;
|
||||
static const unsigned int cwAdcDashTo = 800;
|
||||
|
||||
byte delayBeforeCWStartTime = 50;
|
||||
|
||||
|
||||
|
||||
|
||||
// in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs
|
||||
//#define CW_TIMEOUT (600l) //Change to CW Delaytime for value save to eeprom
|
||||
#define PADDLE_DOT 1
|
||||
#define PADDLE_DASH 2
|
||||
#define PADDLE_BOTH 3
|
||||
#define PADDLE_STRAIGHT 4
|
||||
|
||||
//we store the last padde's character
|
||||
//to alternatively send dots and dashes
|
||||
//when both are simultaneously pressed
|
||||
char lastPaddle = 0;
|
||||
|
||||
/*
|
||||
//reads the analog keyer pin and reports the paddle
|
||||
byte getPaddle(){
|
||||
int paddle = analogRead(ANALOG_KEYER);
|
||||
//handle the ptt as the straight key
|
||||
|
||||
if (digitalRead(PTT) == 0)
|
||||
return PADDLE_STRAIGHT;
|
||||
|
||||
if (paddle > 800) // above 4v is up
|
||||
return 0;
|
||||
|
||||
if (!Iambic_Key)
|
||||
return PADDLE_STRAIGHT;
|
||||
|
||||
if (paddle > 600) // 4-3v is dot
|
||||
return PADDLE_DASH;
|
||||
else if (paddle > 300) //1-2v is dash
|
||||
return PADDLE_DOT;
|
||||
else if (paddle > 50)
|
||||
return PADDLE_BOTH; //both are between 1 and 2v
|
||||
else
|
||||
return PADDLE_STRAIGHT; //less than 1v is the straight key
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Starts transmitting the carrier with the sidetone
|
||||
* It assumes that we have called cwTxStart and not called cwTxStop
|
||||
* each time it is called, the cwTimeOut is pushed further into the future
|
||||
*/
|
||||
void cwKeydown(){
|
||||
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq);
|
||||
digitalWrite(PIN_CW_KEY, 1);
|
||||
|
||||
keyDown = 1; //tracks the CW_KEY
|
||||
tone(CW_TONE, (int)sideTone);
|
||||
digitalWrite(CW_KEY, 1);
|
||||
|
||||
//Modified by KD8CEC, for CW Delay Time save to eeprom
|
||||
//cwTimeout = millis() + CW_TIMEOUT;
|
||||
cwTimeout = millis() + cwDelayTime * 10;
|
||||
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,13 +56,10 @@ void cwKeydown(){
|
||||
* Pushes the cwTimeout further into the future
|
||||
*/
|
||||
void cwKeyUp(){
|
||||
keyDown = 0; //tracks the CW_KEY
|
||||
noTone(CW_TONE);
|
||||
digitalWrite(CW_KEY, 0);
|
||||
noToneAC2();
|
||||
digitalWrite(PIN_CW_KEY, 0);
|
||||
|
||||
//Modified by KD8CEC, for CW Delay Time save to eeprom
|
||||
//cwTimeout = millis() + CW_TIMEOUT;
|
||||
cwTimeout = millis() + cwDelayTime * 10;
|
||||
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
|
||||
}
|
||||
|
||||
//Variables for Ron's new logic
|
||||
@ -119,37 +71,29 @@ void cwKeyUp(){
|
||||
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
|
||||
static unsigned long ktimer;
|
||||
unsigned char keyerState = IDLE;
|
||||
uint8_t keyerControl = 0;
|
||||
|
||||
//Below is a test to reduce the keying error. do not delete lines
|
||||
//create by KD8CEC for compatible with new CW Logic
|
||||
char update_PaddleLatch(byte isUpdateKeyState) {
|
||||
char update_PaddleLatch(bool isUpdateKeyState) {
|
||||
unsigned char tmpKeyerControl = 0;
|
||||
|
||||
int paddle = analogRead(ANALOG_KEYER);
|
||||
//diagnostic, VU2ESE
|
||||
//itoa(paddle, b, 10);
|
||||
//printLine2(b);
|
||||
|
||||
//use the PTT as the key for tune up, quick QSOs
|
||||
if (digitalRead(PTT) == 0)
|
||||
tmpKeyerControl |= DIT_L;
|
||||
else if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
|
||||
unsigned int paddle = analogRead(PIN_ANALOG_KEYER);
|
||||
if (paddle >= cwAdcDashFrom && paddle <= cwAdcDashTo)
|
||||
tmpKeyerControl |= DAH_L;
|
||||
else if (paddle >= cwAdcDotFrom && paddle <= cwAdcDotTo)
|
||||
tmpKeyerControl |= DIT_L;
|
||||
else if (paddle >= cwAdcBothFrom && paddle <= cwAdcBothTo)
|
||||
tmpKeyerControl |= (DAH_L | DIT_L) ;
|
||||
else
|
||||
{
|
||||
if (Iambic_Key)
|
||||
tmpKeyerControl |= (DAH_L | DIT_L) ;
|
||||
else{
|
||||
if (KeyerMode_e::KEYER_STRAIGHT != globalSettings.keyerMode)
|
||||
tmpKeyerControl = 0 ;
|
||||
else if (paddle >= cwAdcSTFrom && paddle <= cwAdcSTTo)
|
||||
else if (paddle <= cwAdcDashTo)
|
||||
tmpKeyerControl = DIT_L ;
|
||||
else
|
||||
tmpKeyerControl = 0 ;
|
||||
tmpKeyerControl = 0 ;
|
||||
}
|
||||
|
||||
if (isUpdateKeyState == 1)
|
||||
if (isUpdateKeyState)
|
||||
keyerControl |= tmpKeyerControl;
|
||||
|
||||
return tmpKeyerControl;
|
||||
@ -160,22 +104,56 @@ char update_PaddleLatch(byte isUpdateKeyState) {
|
||||
// modified by KD8CEC
|
||||
******************************************************************************/
|
||||
void cwKeyer(void){
|
||||
lastPaddle = 0;
|
||||
bool continue_loop = true;
|
||||
unsigned tmpKeyControl = 0;
|
||||
char tmpKeyControl = 0;
|
||||
|
||||
if( Iambic_Key ) {
|
||||
while(continue_loop) {
|
||||
switch (keyerState) {
|
||||
if((KeyerMode_e::KEYER_STRAIGHT == globalSettings.keyerMode)
|
||||
|| (digitalRead(PIN_PTT) == 0)){//use the PTT as the key for tune up, quick QSOs
|
||||
while(1){
|
||||
tmpKeyControl = update_PaddleLatch(0) | (digitalRead(PIN_PTT)?0:DIT_L);
|
||||
//Serial.println((int)tmpKeyControl);
|
||||
if ((tmpKeyControl & DIT_L) == DIT_L) {
|
||||
// if we are here, it is only because the key is pressed
|
||||
if (!globalSettings.txActive){
|
||||
startTx(TuningMode_e::TUNE_CW);
|
||||
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
|
||||
}
|
||||
cwKeydown();
|
||||
|
||||
while ( tmpKeyControl & DIT_L == DIT_L){
|
||||
tmpKeyControl = update_PaddleLatch(0) | (digitalRead(PIN_PTT)?0:DIT_L);
|
||||
//Serial.println((int)tmpKeyControl);
|
||||
}
|
||||
|
||||
cwKeyUp();
|
||||
}
|
||||
else{
|
||||
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
|
||||
globalSettings.cwExpirationTimeMs = 0;
|
||||
stopTx();
|
||||
}
|
||||
return;//Tx stop control by Main Loop
|
||||
}
|
||||
|
||||
checkCAT();
|
||||
} //end of while
|
||||
|
||||
}
|
||||
else{//KEYER_IAMBIC_*
|
||||
while(continue_loop){
|
||||
switch(keyerState){
|
||||
case IDLE:
|
||||
tmpKeyControl = update_PaddleLatch(0);
|
||||
if ( tmpKeyControl == DAH_L || tmpKeyControl == DIT_L ||
|
||||
tmpKeyControl == (DAH_L | DIT_L) || (keyerControl & 0x03)) {
|
||||
update_PaddleLatch(1);
|
||||
if((tmpKeyControl == DAH_L)//Currently dah
|
||||
||(tmpKeyControl == DIT_L)//Currently dit
|
||||
||(tmpKeyControl == (DAH_L | DIT_L))//Currently both
|
||||
||( keyerControl & (DAH_L | DIT_L))){//Resolving either
|
||||
update_PaddleLatch(true);
|
||||
keyerState = CHK_DIT;
|
||||
}else{
|
||||
if (0 < cwTimeout && cwTimeout < millis()){
|
||||
cwTimeout = 0;
|
||||
}
|
||||
else{
|
||||
if (0 < globalSettings.cwExpirationTimeMs && globalSettings.cwExpirationTimeMs < millis()){
|
||||
globalSettings.cwExpirationTimeMs = 0;
|
||||
stopTx();
|
||||
}
|
||||
continue_loop = false;
|
||||
@ -185,7 +163,7 @@ void cwKeyer(void){
|
||||
case CHK_DIT:
|
||||
if (keyerControl & DIT_L) {
|
||||
keyerControl |= DIT_PROC;
|
||||
ktimer = cwSpeed;
|
||||
ktimer = globalSettings.cwDitDurationMs;
|
||||
keyerState = KEYED_PREP;
|
||||
}else{
|
||||
keyerState = CHK_DAH;
|
||||
@ -194,7 +172,7 @@ void cwKeyer(void){
|
||||
|
||||
case CHK_DAH:
|
||||
if (keyerControl & DAH_L) {
|
||||
ktimer = cwSpeed*3;
|
||||
ktimer = 3*globalSettings.cwDitDurationMs;
|
||||
keyerState = KEYED_PREP;
|
||||
}else{
|
||||
keyerState = IDLE;
|
||||
@ -203,13 +181,9 @@ void cwKeyer(void){
|
||||
|
||||
case KEYED_PREP:
|
||||
//modified KD8CEC
|
||||
if (!inTx){
|
||||
//DelayTime Option
|
||||
active_delay(delayBeforeCWStartTime * 2);
|
||||
|
||||
keyDown = 0;
|
||||
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
|
||||
startTx(TX_CW);
|
||||
if (!globalSettings.txActive){
|
||||
globalSettings.cwExpirationTimeMs = millis() + globalSettings.cwActiveTimeoutMs;
|
||||
startTx(TuningMode_e::TUNE_CW);
|
||||
}
|
||||
ktimer += millis(); // set ktimer to interval end time
|
||||
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
|
||||
@ -220,10 +194,11 @@ void cwKeyer(void){
|
||||
|
||||
case KEYED:
|
||||
if (millis() > ktimer) { // are we at end of key down ?
|
||||
cwKeyUp();
|
||||
ktimer = millis() + cwSpeed; // inter-element time
|
||||
cwKeyUp();
|
||||
ktimer = millis() + globalSettings.cwDitDurationMs; // inter-element time
|
||||
keyerState = INTER_ELEMENT; // next state
|
||||
}else if (keyerControl & IAMBICB) {
|
||||
}
|
||||
else if(KeyerMode_e::KEYER_IAMBIC_B == globalSettings.keyerMode){
|
||||
update_PaddleLatch(1); // early paddle latch in Iambic B mode
|
||||
}
|
||||
break;
|
||||
@ -245,48 +220,7 @@ void cwKeyer(void){
|
||||
|
||||
checkCAT();
|
||||
} //end of while
|
||||
}
|
||||
else{
|
||||
while(1){
|
||||
char state = update_PaddleLatch(0);
|
||||
// Serial.println((int)state);
|
||||
if (state == DIT_L) {
|
||||
// if we are here, it is only because the key is pressed
|
||||
if (!inTx){
|
||||
startTx(TX_CW);
|
||||
|
||||
//DelayTime Option
|
||||
active_delay(delayBeforeCWStartTime * 2);
|
||||
|
||||
keyDown = 0;
|
||||
cwTimeout = millis() + cwDelayTime * 10; //+ CW_TIMEOUT;
|
||||
}
|
||||
cwKeydown();
|
||||
|
||||
while ( update_PaddleLatch(0) == DIT_L )
|
||||
active_delay(1);
|
||||
|
||||
cwKeyUp();
|
||||
}
|
||||
else{
|
||||
if (0 < cwTimeout && cwTimeout < millis()){
|
||||
cwTimeout = 0;
|
||||
keyDown = 0;
|
||||
stopTx();
|
||||
}
|
||||
//if (!cwTimeout) //removed by KD8CEC
|
||||
// return;
|
||||
// got back to the beginning of the loop, if no further activity happens on straight key
|
||||
// we will time out, and return out of this routine
|
||||
//delay(5);
|
||||
//delay_background(5, 3); //removed by KD8CEC
|
||||
//continue; //removed by KD8CEC
|
||||
return; //Tx stop control by Main Loop
|
||||
}
|
||||
|
||||
checkCAT();
|
||||
} //end of while
|
||||
} //end of elese
|
||||
}//end of KEYER_IAMBIC_*
|
||||
}
|
||||
|
||||
|
||||
|
43
menu.cpp
Normal file
43
menu.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "menu.h"
|
||||
#include "menu_main.h"
|
||||
|
||||
void runActiveMenu(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob)
|
||||
{
|
||||
Menu_t* parent_menu = rootMenu;//rootMenu is it's own parent
|
||||
Menu_t* active_menu = rootMenu;
|
||||
while(nullptr != active_menu->active_submenu){
|
||||
parent_menu = active_menu;
|
||||
active_menu = parent_menu->active_submenu;
|
||||
}
|
||||
MenuReturn_e mr = active_menu->runMenu(tuner_button,touch_button,touch_point,knob);
|
||||
switch(mr){
|
||||
case MenuReturn_e::StillActive://Fallthrough intended
|
||||
case MenuReturn_e::ExitedNoRedraw:
|
||||
{
|
||||
//Nothing to do here - just return
|
||||
break;
|
||||
}
|
||||
default://Fallthrough intended. Default to this menu being active
|
||||
case MenuReturn_e::ExitedRedraw:
|
||||
{
|
||||
//Turn off submenu, redraw, then return
|
||||
parent_menu->active_submenu = nullptr;
|
||||
parent_menu->initMenu();
|
||||
break;
|
||||
}
|
||||
}//end switch
|
||||
}
|
||||
|
||||
void enterSubmenu(Menu_t *const submenu)
|
||||
{
|
||||
Menu_t* current_menu = rootMenu;
|
||||
while(nullptr != current_menu->active_submenu){
|
||||
current_menu = current_menu->active_submenu;
|
||||
}
|
||||
current_menu->active_submenu = submenu;
|
||||
submenu->initMenu();
|
||||
}
|
||||
|
29
menu.h
Normal file
29
menu.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "button_press_e.h"
|
||||
#include "point.h"
|
||||
|
||||
enum MenuReturn_e : uint8_t {
|
||||
StillActive,
|
||||
ExitedRedraw,
|
||||
ExitedNoRedraw
|
||||
};
|
||||
|
||||
struct Menu_t {
|
||||
void (*const initMenu)();//Any initial draw routines or state initialization
|
||||
MenuReturn_e (*const runMenu)(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob);
|
||||
Menu_t* active_submenu;
|
||||
};
|
||||
|
||||
static const uint8_t MENU_KNOB_COUNTS_PER_ITEM = 10;
|
||||
|
||||
void runActiveMenu(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob);
|
||||
|
||||
void enterSubmenu(Menu_t *const submenu);
|
192
menu_main.cpp
Normal file
192
menu_main.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
#include "menu_main.h"
|
||||
#include "menu_main_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "color_theme.h"
|
||||
#include "menu_utils.h"
|
||||
#include "morse.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "tuner.h"//THRESHOLD_USB_LSB
|
||||
#include "utils.h"
|
||||
|
||||
void drawMainMenu(void);
|
||||
MenuReturn_e runMainMenu(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob);
|
||||
Menu_t mainMenu = {
|
||||
drawMainMenu,
|
||||
runMainMenu,
|
||||
nullptr
|
||||
};
|
||||
|
||||
Menu_t* const rootMenu = &mainMenu;
|
||||
|
||||
bool mainMenuSelecting = false;//Tracks if we're selecting buttons with knob, or adjusting frequency
|
||||
int16_t mainMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
|
||||
|
||||
void drawMainMenu(void)
|
||||
{
|
||||
displayClear(COLOR_BACKGROUND);
|
||||
Button button;
|
||||
Button* bp;
|
||||
for(uint8_t i = 0; i < MAIN_MENU_NUM_BUTTONS; ++i){
|
||||
memcpy_P(&bp, &(mainMenuButtons[i]), sizeof(bp));
|
||||
extractAndDrawButton(&button,bp);
|
||||
}
|
||||
drawVersion();
|
||||
drawCallsign();
|
||||
|
||||
ltoa(GetActiveVfoFreq(),b,10);
|
||||
morseText(b);
|
||||
}
|
||||
|
||||
void drawMainMenuIncrement()
|
||||
{
|
||||
//State variables
|
||||
static uint32_t last_freq = 0;
|
||||
static Vfo_e last_vfo = Vfo_e::VFO_A;
|
||||
static VfoMode_e last_mode = VfoMode_e::VFO_MODE_LSB;
|
||||
static bool last_split = false;
|
||||
static bool last_rit = false;
|
||||
static TuningMode_e last_tuning = TuningMode_e::TUNE_SSB;
|
||||
|
||||
Button button;
|
||||
|
||||
if((last_freq != GetActiveVfoFreq())
|
||||
||(last_vfo != globalSettings.activeVfo)){
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
updateBandButtons(last_freq);
|
||||
last_freq = GetActiveVfoFreq();
|
||||
last_vfo = globalSettings.activeVfo;
|
||||
|
||||
//We set this here so that we're always hearing what's displayed
|
||||
setFrequency(last_freq);
|
||||
}
|
||||
|
||||
if(last_mode != GetActiveVfoMode()){
|
||||
updateSidebandButtons();
|
||||
last_mode = GetActiveVfoMode();
|
||||
}
|
||||
|
||||
if(last_split != globalSettings.splitOn){
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
extractAndDrawButton(&button,&bSpl);
|
||||
last_split = globalSettings.splitOn;
|
||||
}
|
||||
|
||||
if(last_rit != globalSettings.ritOn){
|
||||
extractAndDrawButton(&button,&bRit);
|
||||
last_rit = globalSettings.ritOn;
|
||||
}
|
||||
|
||||
if(last_tuning != globalSettings.tuningMode){
|
||||
extractAndDrawButton(&button,&bCw);
|
||||
last_tuning = globalSettings.tuningMode;
|
||||
}
|
||||
}
|
||||
|
||||
void mainMenuTune(int16_t knob)
|
||||
{
|
||||
if(0 == knob){
|
||||
//Nothing to do - we're already set!
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t current_freq = GetActiveVfoFreq();
|
||||
const uint32_t new_freq = current_freq + (50 * knob);
|
||||
|
||||
SetActiveVfoFreq(new_freq);
|
||||
autoSelectSidebandChanged(current_freq);
|
||||
}
|
||||
|
||||
MenuReturn_e runMainMenu(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob)
|
||||
{
|
||||
//Check tuner_button
|
||||
if(ButtonPress_e::NotPressed != tuner_button){
|
||||
switch(tuner_button){
|
||||
default://Fallthrough intended
|
||||
case ButtonPress_e::NotPressed:
|
||||
{
|
||||
//Nothing to do
|
||||
break;
|
||||
}
|
||||
case ButtonPress_e::ShortPress:
|
||||
{
|
||||
if(mainMenuSelecting){
|
||||
uint8_t menu_index = mainMenuSelectedItemRaw/MENU_KNOB_COUNTS_PER_ITEM;
|
||||
Button button;
|
||||
Button* bp;
|
||||
memcpy_P(&bp,&(mainMenuButtons[menu_index]),sizeof(bp));
|
||||
memcpy_P(&button,bp,sizeof(button));
|
||||
endSelector(&button);
|
||||
button.on_select();
|
||||
}
|
||||
else{
|
||||
initSelector(&mainMenuSelectedItemRaw,
|
||||
mainMenuButtons,
|
||||
MAIN_MENU_NUM_BUTTONS,
|
||||
MorsePlaybackType_e::PlayChar);
|
||||
}
|
||||
mainMenuSelecting = !mainMenuSelecting;
|
||||
|
||||
//Don't handle touch or knob on this run
|
||||
return MenuReturn_e::StillActive;//main menu always returns StillActive
|
||||
break;
|
||||
}
|
||||
case ButtonPress_e::LongPress:
|
||||
{
|
||||
if(!globalSettings.morseMenuOn){
|
||||
globalSettings.morseMenuOn = true;//set before playing
|
||||
morseLetter(2);
|
||||
}
|
||||
else{
|
||||
morseLetter(4);
|
||||
globalSettings.morseMenuOn = false;//unset after playing
|
||||
}
|
||||
SaveSettingsToEeprom();
|
||||
//Don't handle touch or knob on this run
|
||||
return MenuReturn_e::StillActive;//main menu always returns StillActive
|
||||
break;
|
||||
}
|
||||
}//switch
|
||||
}//tuner_button
|
||||
|
||||
else if(ButtonPress_e::NotPressed != touch_button){
|
||||
//We treat long and short presses the same, so no need to have a switch
|
||||
Button button;
|
||||
if(findPressedButton(mainMenuButtons,MAIN_MENU_NUM_BUTTONS,&button,touch_point)){
|
||||
button.on_select();
|
||||
}
|
||||
else{
|
||||
//Touch detected, but not on our buttons, so ignore
|
||||
}
|
||||
}//touch_button
|
||||
|
||||
else{//Neither button input type found, so handle the knob
|
||||
if(mainMenuSelecting){
|
||||
adjustSelector(&mainMenuSelectedItemRaw,
|
||||
knob,
|
||||
mainMenuButtons,
|
||||
MAIN_MENU_NUM_BUTTONS,
|
||||
MorsePlaybackType_e::PlayChar);
|
||||
}
|
||||
else{
|
||||
mainMenuTune(knob);
|
||||
}
|
||||
}
|
||||
|
||||
drawMainMenuIncrement();
|
||||
|
||||
return MenuReturn_e::StillActive;//main menu always returns StillActive
|
||||
}
|
2
menu_main.h
Normal file
2
menu_main.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "menu.h"
|
||||
extern Menu_t* const rootMenu;
|
637
menu_main_buttons.cpp
Normal file
637
menu_main_buttons.cpp
Normal file
@ -0,0 +1,637 @@
|
||||
#include "menu_main_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <string.h>
|
||||
#include <WString.h>//F()
|
||||
|
||||
#include "bands.h"
|
||||
#include "button.h"
|
||||
#include "callsign.h"
|
||||
#include "color_theme.h"
|
||||
#include "menu_main.h"
|
||||
#include "menu_numpad.h"
|
||||
#include "menu_quicklist.h"
|
||||
#include "morse.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "setup.h"
|
||||
#include "tuner.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
|
||||
static const unsigned int LAYOUT_VFO_LABEL_X = 0;
|
||||
static const unsigned int LAYOUT_VFO_LABEL_Y = 10;
|
||||
static const unsigned int LAYOUT_VFO_LABEL_WIDTH = 159;
|
||||
static const unsigned int LAYOUT_VFO_LABEL_HEIGHT = 36;
|
||||
static const unsigned int LAYOUT_VFO_LABEL_PITCH_X = 160;
|
||||
|
||||
static const unsigned int LAYOUT_MODE_TEXT_X = 0;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_Y = LAYOUT_VFO_LABEL_Y + LAYOUT_VFO_LABEL_HEIGHT + 1;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_WIDTH = 320;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_HEIGHT = 36;
|
||||
|
||||
static const unsigned int LAYOUT_BUTTON_X = 2;
|
||||
static const unsigned int LAYOUT_BUTTON_Y = LAYOUT_MODE_TEXT_Y + LAYOUT_MODE_TEXT_HEIGHT + 1;
|
||||
static const unsigned int LAYOUT_BUTTON_WIDTH = 60;
|
||||
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_X = 64;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
|
||||
|
||||
static const unsigned int LAYOUT_CW_TEXT_X = 0;
|
||||
static const unsigned int LAYOUT_CW_TEXT_Y = LAYOUT_BUTTON_Y + 3*LAYOUT_BUTTON_PITCH_Y + 1;
|
||||
static const unsigned int LAYOUT_CW_TEXT_WIDTH = 220;
|
||||
static const unsigned int LAYOUT_CW_TEXT_HEIGHT = 36;
|
||||
|
||||
static const unsigned int LAYOUT_VERSION_TEXT_X = LAYOUT_CW_TEXT_X + LAYOUT_CW_TEXT_WIDTH + 1;
|
||||
static const unsigned int LAYOUT_VERSION_TEXT_Y = LAYOUT_CW_TEXT_Y;
|
||||
static const unsigned int LAYOUT_VERSION_TEXT_WIDTH = 320 - LAYOUT_CW_TEXT_WIDTH - 1;
|
||||
static const unsigned int LAYOUT_VERSION_TEXT_HEIGHT = LAYOUT_CW_TEXT_HEIGHT;
|
||||
|
||||
static const unsigned int LAYOUT_TX_X = 280;
|
||||
static const unsigned int LAYOUT_TX_Y = LAYOUT_MODE_TEXT_Y;
|
||||
static const unsigned int LAYOUT_TX_WIDTH = 40;
|
||||
static const unsigned int LAYOUT_TX_HEIGHT = 36;
|
||||
|
||||
void drawTx()
|
||||
{
|
||||
if(globalSettings.txActive){
|
||||
strncpy_P(b,(const char*)F("TX"),sizeof(b));
|
||||
displayText(b,LAYOUT_TX_X,LAYOUT_TX_Y,LAYOUT_TX_WIDTH,LAYOUT_TX_HEIGHT,COLOR_ACTIVE_TEXT,COLOR_ACTIVE_BACKGROUND,COLOR_BACKGROUND);
|
||||
}
|
||||
else{
|
||||
displayFillrect(LAYOUT_TX_X,LAYOUT_TX_Y,LAYOUT_TX_WIDTH,LAYOUT_TX_HEIGHT,COLOR_BACKGROUND);
|
||||
}
|
||||
}
|
||||
|
||||
void drawVersion()
|
||||
{
|
||||
strncpy_P(b,VERSION_STRING,sizeof(b));
|
||||
displayText(b,LAYOUT_VERSION_TEXT_X,LAYOUT_VERSION_TEXT_Y,LAYOUT_VERSION_TEXT_WIDTH,LAYOUT_VERSION_TEXT_HEIGHT,COLOR_VERSION_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
|
||||
}
|
||||
|
||||
void drawCallsign()
|
||||
{
|
||||
strncpy_P(b,CALLSIGN_STRING,sizeof(b));
|
||||
displayText(b,LAYOUT_CW_TEXT_X,LAYOUT_CW_TEXT_Y,LAYOUT_CW_TEXT_WIDTH,LAYOUT_CW_TEXT_HEIGHT,COLOR_VERSION_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
|
||||
}
|
||||
|
||||
void toVfoA(char* text_out, const uint16_t max_text_size);
|
||||
ButtonStatus_e bsVfoA();
|
||||
void osVfoA();
|
||||
constexpr Button bVfoA PROGMEM = {
|
||||
LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X,
|
||||
LAYOUT_VFO_LABEL_Y,
|
||||
LAYOUT_VFO_LABEL_WIDTH,
|
||||
LAYOUT_VFO_LABEL_HEIGHT,
|
||||
nullptr,
|
||||
toVfoA,
|
||||
bsVfoA,
|
||||
osVfoA,
|
||||
'A'
|
||||
};
|
||||
|
||||
void toVfoB(char* text_out, const uint16_t max_text_size);
|
||||
ButtonStatus_e bsVfoB();
|
||||
void osVfoB();
|
||||
constexpr Button bVfoB PROGMEM = {
|
||||
LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X,
|
||||
LAYOUT_VFO_LABEL_Y,
|
||||
LAYOUT_VFO_LABEL_WIDTH,
|
||||
LAYOUT_VFO_LABEL_HEIGHT,
|
||||
nullptr,
|
||||
toVfoB,
|
||||
bsVfoB,
|
||||
osVfoB,
|
||||
'B'
|
||||
};
|
||||
|
||||
constexpr char txtRit [] PROGMEM = "RIT";
|
||||
ButtonStatus_e bsRit();
|
||||
void osRit();
|
||||
constexpr Button bRit PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtRit,
|
||||
nullptr,
|
||||
bsRit,
|
||||
osRit,
|
||||
'R'
|
||||
};
|
||||
|
||||
constexpr char txtUsb [] PROGMEM = "USB";
|
||||
ButtonStatus_e bsUsb();
|
||||
void osUsb();
|
||||
constexpr Button bUsb PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtUsb,
|
||||
nullptr,
|
||||
bsUsb,
|
||||
osUsb,
|
||||
'U'
|
||||
};
|
||||
|
||||
constexpr char txtLsb [] PROGMEM = "LSB";
|
||||
ButtonStatus_e bsLsb();
|
||||
void osLsb();
|
||||
constexpr Button bLsb PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtLsb,
|
||||
nullptr,
|
||||
bsLsb,
|
||||
osLsb,
|
||||
'L'
|
||||
};
|
||||
|
||||
constexpr char txtCw [] PROGMEM = "CW";
|
||||
ButtonStatus_e bsCw();
|
||||
void osCw();
|
||||
constexpr Button bCw PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtCw,
|
||||
nullptr,
|
||||
bsCw,
|
||||
osCw,
|
||||
'C'
|
||||
};
|
||||
|
||||
constexpr char txtSpl [] PROGMEM = "SPL";
|
||||
ButtonStatus_e bsSpl();
|
||||
void osSpl();
|
||||
constexpr Button bSpl PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtSpl,
|
||||
nullptr,
|
||||
bsSpl,
|
||||
osSpl,
|
||||
'S'
|
||||
};
|
||||
|
||||
constexpr char txt80 [] PROGMEM = "80";
|
||||
ButtonStatus_e bs80();
|
||||
void os80();
|
||||
constexpr Button b80 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt80,
|
||||
nullptr,
|
||||
bs80,
|
||||
os80,
|
||||
'8'
|
||||
};
|
||||
|
||||
constexpr char txt40 [] PROGMEM = "40";
|
||||
ButtonStatus_e bs40();
|
||||
void os40();
|
||||
constexpr Button b40 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt40,
|
||||
nullptr,
|
||||
bs40,
|
||||
os40,
|
||||
'4'
|
||||
};
|
||||
|
||||
constexpr char txt30 [] PROGMEM = "30";
|
||||
ButtonStatus_e bs30();
|
||||
void os30();
|
||||
constexpr Button b30 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt30,
|
||||
nullptr,
|
||||
bs30,
|
||||
os30,
|
||||
'3'
|
||||
};
|
||||
|
||||
constexpr char txt20 [] PROGMEM = "20";
|
||||
ButtonStatus_e bs20();
|
||||
void os20();
|
||||
constexpr Button b20 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt20,
|
||||
nullptr,
|
||||
bs20,
|
||||
os20,
|
||||
'2'
|
||||
};
|
||||
|
||||
constexpr char txt17 [] PROGMEM = "17";
|
||||
ButtonStatus_e bs17();
|
||||
void os17();
|
||||
constexpr Button b17 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt17,
|
||||
nullptr,
|
||||
bs17,
|
||||
os17,
|
||||
'7'
|
||||
};
|
||||
|
||||
constexpr char txt15 [] PROGMEM = "15";
|
||||
ButtonStatus_e bs15();
|
||||
void os15();
|
||||
constexpr Button b15 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 0*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt15,
|
||||
nullptr,
|
||||
bs15,
|
||||
os15,
|
||||
'5'
|
||||
};
|
||||
|
||||
constexpr char txt10 [] PROGMEM = "10";
|
||||
ButtonStatus_e bs10();
|
||||
void os10();
|
||||
constexpr Button b10 PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 1*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txt10,
|
||||
nullptr,
|
||||
bs10,
|
||||
os10,
|
||||
'1'
|
||||
};
|
||||
|
||||
constexpr char txtQuickList [] PROGMEM = "\x83";//star icon
|
||||
ButtonStatus_e bsIgnore();
|
||||
void osQuickList();
|
||||
constexpr Button bQuickList PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 2*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtQuickList,
|
||||
nullptr,
|
||||
bsIgnore,
|
||||
osQuickList,
|
||||
'Q'
|
||||
};
|
||||
|
||||
constexpr char txtMenu [] PROGMEM = "\x7F";//gear icon
|
||||
ButtonStatus_e bsIgnore();
|
||||
void osMenu();
|
||||
constexpr Button bMenu PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 3*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtMenu,
|
||||
nullptr,
|
||||
bsIgnore,
|
||||
osMenu,
|
||||
'M'
|
||||
};
|
||||
|
||||
constexpr char txtNumpad [] PROGMEM = "\x82";//numpad icon
|
||||
ButtonStatus_e bsIgnore();
|
||||
void osNumpad();
|
||||
constexpr Button bNumpad PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtNumpad,
|
||||
nullptr,
|
||||
bsIgnore,
|
||||
osNumpad,
|
||||
'F'
|
||||
};
|
||||
|
||||
const Button* const mainMenuButtons [] PROGMEM = {
|
||||
&bVfoA, &bVfoB,
|
||||
|
||||
&bRit, &bUsb, &bLsb, &bCw, &bSpl,
|
||||
&b80, &b40, &b30, &b20, &b17,
|
||||
&b15, &b10, &bQuickList, &bMenu, &bNumpad
|
||||
};
|
||||
|
||||
const uint8_t MAIN_MENU_NUM_BUTTONS = sizeof(mainMenuButtons) / sizeof(mainMenuButtons[0]);
|
||||
|
||||
void updateBandButtons(const uint32_t old_freq)
|
||||
{
|
||||
const Button* band_buttons[] = {&b80,&b40,&b30,&b20,&b17,&b15,&b10};
|
||||
const uint8_t bands [] = { 80, 40, 30, 20, 17, 15, 10};
|
||||
const uint32_t curr_freq = GetActiveVfoFreq();
|
||||
|
||||
Button button;
|
||||
for(uint8_t i = 0; i < sizeof(bands)/sizeof(bands[0]); ++i){
|
||||
if(isFreqInBand(old_freq,bands[i]) != isFreqInBand(curr_freq,bands[i])){
|
||||
extractAndDrawButton(&button,band_buttons[i]);
|
||||
morseBool(ButtonStatus_e::Active == button.status());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void toVfo(char* text_out, const uint16_t max_text_size, const Vfo_e vfo)
|
||||
{
|
||||
if(max_text_size < 2){
|
||||
return;//Can't do much with that space
|
||||
}
|
||||
if(max_text_size < (3+10+1)){
|
||||
//Give an indicator that's debuggable
|
||||
text_out[0] = 'X';
|
||||
text_out[1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
//Normal operation
|
||||
if (globalSettings.splitOn){
|
||||
if(vfo == globalSettings.activeVfo){
|
||||
text_out[0] = 'R';
|
||||
}
|
||||
else{
|
||||
text_out[0] = 'T';
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(VFO_A == vfo){
|
||||
text_out[0] = 'A';
|
||||
}
|
||||
else if(VFO_B == vfo){
|
||||
text_out[0] = 'B';
|
||||
}
|
||||
else{
|
||||
text_out[0] = '?';
|
||||
}
|
||||
}
|
||||
text_out[1] = ':';
|
||||
text_out[2] = ' ';
|
||||
if(VFO_A == vfo){
|
||||
formatFreq(globalSettings.vfoA.frequency, text_out+3, max_text_size-3, 10);
|
||||
}
|
||||
else if(VFO_B == vfo){
|
||||
formatFreq(globalSettings.vfoB.frequency, text_out+3, max_text_size-3, 10);
|
||||
}
|
||||
else{
|
||||
text_out[3] = '?';
|
||||
text_out[4] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ButtonStatus_e bsVfo(const Vfo_e vfo){
|
||||
return (vfo == globalSettings.activeVfo) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osVfo(const Vfo_e vfo){
|
||||
const uint32_t old_freq = GetActiveVfoFreq();
|
||||
|
||||
if(globalSettings.ritOn){
|
||||
osRit();//Turn off RIT
|
||||
}
|
||||
|
||||
globalSettings.activeVfo = vfo;
|
||||
SaveSettingsToEeprom();
|
||||
|
||||
ltoa(GetActiveVfoFreq(),b,10);
|
||||
morseText(b);
|
||||
|
||||
Button button;
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
updateBandButtons(old_freq);
|
||||
updateSidebandButtons();
|
||||
}
|
||||
|
||||
void toVfoA(char* text_out, const uint16_t max_text_size){
|
||||
toVfo(text_out,max_text_size,Vfo_e::VFO_A);
|
||||
}
|
||||
ButtonStatus_e bsVfoA(){
|
||||
return bsVfo(Vfo_e::VFO_A);
|
||||
}
|
||||
void osVfoA(){
|
||||
osVfo(Vfo_e::VFO_A);
|
||||
}
|
||||
|
||||
void toVfoB(char* text_out, const uint16_t max_text_size){
|
||||
toVfo(text_out,max_text_size,Vfo_e::VFO_B);
|
||||
}
|
||||
ButtonStatus_e bsVfoB(){
|
||||
return bsVfo(Vfo_e::VFO_B);
|
||||
}
|
||||
void osVfoB(){
|
||||
osVfo(Vfo_e::VFO_B);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsRit(){
|
||||
return globalSettings.ritOn ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
void osRit(){
|
||||
Button button;
|
||||
if(!globalSettings.ritOn){
|
||||
globalSettings.ritOn = true;
|
||||
globalSettings.ritFrequency = GetActiveVfoFreq();
|
||||
|
||||
strncpy_P(b,(const char*)F("TX: "),sizeof(b));
|
||||
formatFreq(globalSettings.ritFrequency, b + strlen(b), sizeof(b)-strlen(b));
|
||||
if (VFO_A == globalSettings.activeVfo){
|
||||
displayText(b, LAYOUT_VFO_LABEL_X + 0*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND, TextJustification_e::Left);
|
||||
}
|
||||
else{
|
||||
displayText(b, LAYOUT_VFO_LABEL_X + 1*LAYOUT_VFO_LABEL_PITCH_X, LAYOUT_MODE_TEXT_Y, LAYOUT_VFO_LABEL_WIDTH, LAYOUT_MODE_TEXT_HEIGHT, COLOR_TEXT, COLOR_BACKGROUND, COLOR_BACKGROUND, TextJustification_e::Left);
|
||||
}
|
||||
}
|
||||
else{
|
||||
globalSettings.ritOn = false;
|
||||
setFrequency(globalSettings.ritFrequency);
|
||||
|
||||
displayFillrect(LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT, COLOR_BACKGROUND);
|
||||
if(Vfo_e::VFO_A == globalSettings.activeVfo){
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
}
|
||||
else{
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
}
|
||||
}
|
||||
|
||||
extractAndDrawButton(&button,&bRit);
|
||||
}
|
||||
|
||||
void osSidebandMode(VfoMode_e mode){
|
||||
SetActiveVfoMode(mode);
|
||||
setFrequency(GetActiveVfoFreq());
|
||||
SaveSettingsToEeprom();
|
||||
|
||||
Button button;
|
||||
extractAndDrawButton(&button,&bUsb);
|
||||
extractAndDrawButton(&button,&bLsb);
|
||||
}
|
||||
|
||||
void updateSidebandButtons()
|
||||
{
|
||||
osSidebandMode(GetActiveVfoMode());
|
||||
}
|
||||
|
||||
ButtonStatus_e bsUsb(){
|
||||
return (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osUsb(){
|
||||
osSidebandMode(VfoMode_e::VFO_MODE_USB);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsLsb(){
|
||||
return (VfoMode_e::VFO_MODE_LSB == GetActiveVfoMode()) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osLsb(){
|
||||
osSidebandMode(VfoMode_e::VFO_MODE_LSB);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsCw(){
|
||||
return (TuningMode_e::TUNE_CW == globalSettings.tuningMode) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osCw(){
|
||||
if(TuningMode_e::TUNE_CW != globalSettings.tuningMode){
|
||||
globalSettings.tuningMode = TuningMode_e::TUNE_CW;
|
||||
}
|
||||
else{
|
||||
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
|
||||
}
|
||||
|
||||
setFrequency(GetActiveVfoFreq());
|
||||
|
||||
Button button;
|
||||
extractAndDrawButton(&button,&bCw);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsSpl(){
|
||||
return globalSettings.splitOn ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osSpl(){
|
||||
globalSettings.splitOn = !globalSettings.splitOn;
|
||||
|
||||
Button button;
|
||||
extractAndDrawButton(&button,&bSpl);
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsBand(const uint8_t band){
|
||||
return isFreqInBand(GetActiveVfoFreq(),band) ? ButtonStatus_e::Active : ButtonStatus_e::Inactive;
|
||||
}
|
||||
|
||||
void osBand(const uint8_t band){
|
||||
const uint32_t old_freq = GetActiveVfoFreq();
|
||||
SetActiveVfoFreq(getFreqInBand(old_freq,band));
|
||||
if(autoSelectSidebandChanged(old_freq)){
|
||||
updateSidebandButtons();
|
||||
}
|
||||
|
||||
Button button;
|
||||
if(Vfo_e::VFO_A == globalSettings.activeVfo){
|
||||
extractAndDrawButton(&button,&bVfoA);
|
||||
}
|
||||
else if(Vfo_e::VFO_B == globalSettings.activeVfo){
|
||||
extractAndDrawButton(&button,&bVfoB);
|
||||
}
|
||||
|
||||
updateBandButtons(old_freq);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs80(){
|
||||
return bsBand(80);
|
||||
}
|
||||
|
||||
void os80(){
|
||||
osBand(80);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs40(){
|
||||
return bsBand(40);
|
||||
}
|
||||
|
||||
void os40(){
|
||||
osBand(40);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs30(){
|
||||
return bsBand(30);
|
||||
}
|
||||
|
||||
void os30(){
|
||||
osBand(30);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs20(){
|
||||
return bsBand(20);
|
||||
}
|
||||
|
||||
void os20(){
|
||||
osBand(20);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs17(){
|
||||
return bsBand(17);
|
||||
}
|
||||
|
||||
void os17(){
|
||||
osBand(17);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs15(){
|
||||
return bsBand(15);
|
||||
}
|
||||
|
||||
void os15(){
|
||||
osBand(15);
|
||||
}
|
||||
|
||||
ButtonStatus_e bs10(){
|
||||
return bsBand(10);
|
||||
}
|
||||
|
||||
void os10(){
|
||||
osBand(10);
|
||||
}
|
||||
|
||||
ButtonStatus_e bsIgnore(){
|
||||
return ButtonStatus_e::Stateless;
|
||||
}
|
||||
|
||||
void osQuickList(){
|
||||
enterSubmenu(quickListMenu);
|
||||
}
|
||||
|
||||
void osMenu(){
|
||||
enterSubmenu(setupMenu);
|
||||
}
|
||||
|
||||
void osNumpad(){
|
||||
enterSubmenu(numpadMenu);
|
||||
}
|
19
menu_main_buttons.h
Normal file
19
menu_main_buttons.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "button.h"
|
||||
|
||||
extern const Button* const mainMenuButtons[];
|
||||
extern const uint8_t MAIN_MENU_NUM_BUTTONS;
|
||||
|
||||
extern const Button bVfoA;
|
||||
extern const Button bVfoB;
|
||||
extern const Button bRit;
|
||||
extern const Button bCw;
|
||||
extern const Button bSpl;
|
||||
void updateBandButtons(const uint32_t old_freq);
|
||||
void updateSidebandButtons();
|
||||
void drawTx();
|
||||
void drawVersion();
|
||||
void drawCallsign();
|
52
menu_np_ql_shared.cpp
Normal file
52
menu_np_ql_shared.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "menu_np_ql_shared.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "menu_utils.h"
|
||||
|
||||
MenuReturn_e runNpQlShared(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob,
|
||||
int16_t *const menuSelectedItemRaw,
|
||||
const Button *const *const menu_buttons,
|
||||
const uint8_t menu_num_buttons,
|
||||
ButtonPress_e *const selection_mode)
|
||||
{
|
||||
if(ButtonPress_e::NotPressed != tuner_button){
|
||||
uint8_t menu_index = *menuSelectedItemRaw/MENU_KNOB_COUNTS_PER_ITEM;
|
||||
Button button;
|
||||
Button* bp;
|
||||
memcpy_P(&bp,&(menu_buttons[menu_index]),sizeof(bp));
|
||||
memcpy_P(&button,bp,sizeof(button));
|
||||
*selection_mode = tuner_button;
|
||||
button.on_select();
|
||||
}//tuner_button
|
||||
|
||||
else if(ButtonPress_e::NotPressed != touch_button){
|
||||
Button button;
|
||||
if(findPressedButton(menu_buttons,menu_num_buttons,&button,touch_point)){
|
||||
*selection_mode = touch_button;
|
||||
button.on_select();
|
||||
}
|
||||
else{
|
||||
//Touch detected, but not on our buttons, so ignore
|
||||
}
|
||||
}//touch_button
|
||||
|
||||
else{//Neither button input type found, so handle the knob
|
||||
adjustSelector(menuSelectedItemRaw,
|
||||
knob,
|
||||
menu_buttons,
|
||||
menu_num_buttons,
|
||||
MorsePlaybackType_e::PlayChar);
|
||||
}
|
||||
|
||||
if(ButtonPress_e::NotPressed == *selection_mode){
|
||||
return MenuReturn_e::ExitedRedraw;
|
||||
}
|
||||
else{
|
||||
return MenuReturn_e::StillActive;
|
||||
}
|
||||
}
|
14
menu_np_ql_shared.h
Normal file
14
menu_np_ql_shared.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "button.h"
|
||||
#include "menu.h"
|
||||
|
||||
MenuReturn_e runNpQlShared(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob,
|
||||
int16_t *const menuSelectedItemRaw,
|
||||
const Button *const *const menu_buttons,
|
||||
const uint8_t menu_num_buttons,
|
||||
ButtonPress_e *const selection_mode);
|
||||
|
61
menu_numpad.cpp
Normal file
61
menu_numpad.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "menu_numpad.h"
|
||||
#include "menu_numpad_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "color_theme.h"
|
||||
#include "menu_np_ql_shared.h"
|
||||
#include "menu_utils.h"
|
||||
#include "nano_gui.h"
|
||||
|
||||
void initNumpad(void);
|
||||
MenuReturn_e runNumpad(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob);
|
||||
Menu_t numpad_menu = {
|
||||
initNumpad,
|
||||
runNumpad,
|
||||
nullptr
|
||||
};
|
||||
|
||||
Menu_t *const numpadMenu = &numpad_menu;
|
||||
|
||||
int16_t numpadMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
|
||||
|
||||
void drawNumpad(void)
|
||||
{
|
||||
displayFillrect(0,47,320,200,COLOR_BACKGROUND);
|
||||
Button button;
|
||||
Button* bp;
|
||||
for(uint8_t i = 0; i < NUMPAD_MENU_NUM_BUTTONS; ++i){
|
||||
memcpy_P(&bp, &(numpadMenuButtons[i]), sizeof(bp));
|
||||
extractAndDrawButton(&button,bp);
|
||||
}
|
||||
}
|
||||
|
||||
void initNumpad(void)
|
||||
{
|
||||
numpadMenuFrequency = 0;
|
||||
numpadSelectionMode = ButtonPress_e::LongPress;//Anything except NotPressed
|
||||
drawNumpad();
|
||||
initSelector(&numpadMenuSelectedItemRaw,
|
||||
numpadMenuButtons,
|
||||
NUMPAD_MENU_NUM_BUTTONS,
|
||||
MorsePlaybackType_e::PlayChar);
|
||||
}
|
||||
|
||||
MenuReturn_e runNumpad(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob)
|
||||
{
|
||||
return runNpQlShared(tuner_button,
|
||||
touch_button,
|
||||
touch_point,
|
||||
knob,
|
||||
&numpadMenuSelectedItemRaw,
|
||||
numpadMenuButtons,
|
||||
NUMPAD_MENU_NUM_BUTTONS,
|
||||
&numpadSelectionMode);
|
||||
}
|
2
menu_numpad.h
Normal file
2
menu_numpad.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "menu.h"
|
||||
extern Menu_t* const numpadMenu;
|
161
menu_numpad_buttons.cpp
Normal file
161
menu_numpad_buttons.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include "menu_numpad_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <WString.h>//F()
|
||||
|
||||
#include "color_theme.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "tuner.h"//setFrequency
|
||||
#include "utils.h"
|
||||
|
||||
static const unsigned int LAYOUT_MODE_TEXT_X = 60;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_Y = 47;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_WIDTH = 140;
|
||||
static const unsigned int LAYOUT_MODE_TEXT_HEIGHT = 36;
|
||||
|
||||
static const unsigned int LAYOUT_BUTTON_X = 2;
|
||||
static const unsigned int LAYOUT_BUTTON_Y = LAYOUT_MODE_TEXT_Y + LAYOUT_MODE_TEXT_HEIGHT + 1;
|
||||
static const unsigned int LAYOUT_BUTTON_WIDTH = 60;
|
||||
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_X = 64;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
|
||||
|
||||
uint32_t numpadMenuFrequency;
|
||||
ButtonPress_e numpadSelectionMode;
|
||||
|
||||
#define D_STRINGIFY(x) #x
|
||||
#define D_STRING(x) D_STRINGIFY(x)
|
||||
#define NUMBER_BUTTON_GENERATE(number,x,y) \
|
||||
constexpr char txt##number [] PROGMEM = "" D_STRING(number);\
|
||||
ButtonStatus_e bs##number();\
|
||||
void os##number();\
|
||||
constexpr Button b##number PROGMEM = {\
|
||||
LAYOUT_BUTTON_X + x*LAYOUT_BUTTON_PITCH_X,\
|
||||
LAYOUT_BUTTON_Y + y*LAYOUT_BUTTON_PITCH_Y,\
|
||||
LAYOUT_BUTTON_WIDTH,\
|
||||
LAYOUT_BUTTON_HEIGHT,\
|
||||
txt##number,\
|
||||
nullptr,\
|
||||
bsNumpad,\
|
||||
os##number,\
|
||||
'0'+number\
|
||||
}
|
||||
|
||||
ButtonStatus_e bsNumpad(void){
|
||||
return ButtonStatus_e::Stateless;
|
||||
}
|
||||
|
||||
|
||||
// 1 2 3 Ok
|
||||
// 4 5 6 0 <-
|
||||
// 7 8 9 Can
|
||||
NUMBER_BUTTON_GENERATE(1,0,0);
|
||||
NUMBER_BUTTON_GENERATE(2,1,0);
|
||||
NUMBER_BUTTON_GENERATE(3,2,0);
|
||||
NUMBER_BUTTON_GENERATE(4,0,1);
|
||||
NUMBER_BUTTON_GENERATE(5,1,1);
|
||||
NUMBER_BUTTON_GENERATE(6,2,1);
|
||||
NUMBER_BUTTON_GENERATE(7,0,2);
|
||||
NUMBER_BUTTON_GENERATE(8,1,2);
|
||||
NUMBER_BUTTON_GENERATE(9,2,2);
|
||||
NUMBER_BUTTON_GENERATE(0,3,1);
|
||||
|
||||
constexpr char txtOk [] PROGMEM = "OK";
|
||||
void osOk();
|
||||
constexpr Button bOk PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 0*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtOk,
|
||||
nullptr,
|
||||
bsNumpad,
|
||||
osOk,
|
||||
'K'
|
||||
};
|
||||
|
||||
constexpr char txtBackspace [] PROGMEM = "<-";
|
||||
void osBackspace();
|
||||
constexpr Button bBackspace PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 1*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtBackspace,
|
||||
nullptr,
|
||||
bsNumpad,
|
||||
osBackspace,
|
||||
'B'
|
||||
};
|
||||
|
||||
constexpr char txtCancel [] PROGMEM = "Can";
|
||||
void osCancel();
|
||||
constexpr Button bCancel PROGMEM = {
|
||||
LAYOUT_BUTTON_X + 4*LAYOUT_BUTTON_PITCH_X,
|
||||
LAYOUT_BUTTON_Y + 2*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtCancel,
|
||||
nullptr,
|
||||
bsNumpad,
|
||||
osCancel,
|
||||
'C'
|
||||
};
|
||||
|
||||
//Declare in menu select order, not graphical order
|
||||
const Button* const numpadMenuButtons [] PROGMEM = {
|
||||
&b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &b9, &b0,
|
||||
&bOk, &bBackspace, &bCancel
|
||||
};
|
||||
const uint8_t NUMPAD_MENU_NUM_BUTTONS = sizeof(numpadMenuButtons)/sizeof(numpadMenuButtons[0]);
|
||||
|
||||
void updateCurrentEnteredFrequency(void)
|
||||
{
|
||||
formatFreq(numpadMenuFrequency,b,sizeof(b),0);
|
||||
strncat_P(b,(const char*)F(" Hz"),sizeof(b)-strlen(b));
|
||||
displayText(b,LAYOUT_MODE_TEXT_X,LAYOUT_MODE_TEXT_Y,LAYOUT_MODE_TEXT_WIDTH,LAYOUT_MODE_TEXT_HEIGHT,COLOR_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND,TextJustification_e::Right);
|
||||
}
|
||||
|
||||
void osNumpad(uint8_t new_digit)
|
||||
{
|
||||
numpadMenuFrequency *= 10;
|
||||
numpadMenuFrequency += new_digit;
|
||||
updateCurrentEnteredFrequency();
|
||||
}
|
||||
|
||||
void osBackspace(void)
|
||||
{
|
||||
numpadMenuFrequency /= 10;
|
||||
updateCurrentEnteredFrequency();
|
||||
}
|
||||
|
||||
#define NUMBER_ONSELECT_GENERATE(number) \
|
||||
void os##number(void){\
|
||||
osNumpad(number);\
|
||||
}
|
||||
|
||||
NUMBER_ONSELECT_GENERATE(0);
|
||||
NUMBER_ONSELECT_GENERATE(1);
|
||||
NUMBER_ONSELECT_GENERATE(2);
|
||||
NUMBER_ONSELECT_GENERATE(3);
|
||||
NUMBER_ONSELECT_GENERATE(4);
|
||||
NUMBER_ONSELECT_GENERATE(5);
|
||||
NUMBER_ONSELECT_GENERATE(6);
|
||||
NUMBER_ONSELECT_GENERATE(7);
|
||||
NUMBER_ONSELECT_GENERATE(8);
|
||||
NUMBER_ONSELECT_GENERATE(9);
|
||||
|
||||
void osOk(void)
|
||||
{
|
||||
SetActiveVfoFreq(numpadMenuFrequency);
|
||||
SaveSettingsToEeprom();
|
||||
setFrequency(numpadMenuFrequency);
|
||||
numpadSelectionMode = ButtonPress_e::NotPressed;
|
||||
}
|
||||
|
||||
void osCancel(void)
|
||||
{
|
||||
numpadSelectionMode = ButtonPress_e::NotPressed;
|
||||
}
|
13
menu_numpad_buttons.h
Normal file
13
menu_numpad_buttons.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "button_press_e.h"
|
||||
|
||||
extern const Button* const numpadMenuButtons[];
|
||||
extern const uint8_t NUMPAD_MENU_NUM_BUTTONS;
|
||||
|
||||
extern const uint32_t NUMPAD_MENU_EXIT_FREQ;
|
||||
extern uint32_t numpadMenuFrequency;
|
||||
extern ButtonPress_e numpadSelectionMode;//NotPressed means exit menu. Other press types are consumed by selectors
|
64
menu_quicklist.cpp
Normal file
64
menu_quicklist.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "menu_quicklist.h"
|
||||
#include "menu_quicklist_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <WString.h>//F()
|
||||
|
||||
#include "color_theme.h"
|
||||
#include "menu_np_ql_shared.h"
|
||||
#include "menu_utils.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
|
||||
void initQuickList(void);
|
||||
MenuReturn_e runQuickList(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob);
|
||||
Menu_t quickList_menu = {
|
||||
initQuickList,
|
||||
runQuickList,
|
||||
nullptr
|
||||
};
|
||||
|
||||
Menu_t *const quickListMenu = &quickList_menu;
|
||||
|
||||
int16_t quickListMenuSelectedItemRaw = 0;//Allow negative only for easier checks on wrap around
|
||||
|
||||
void drawQuickList(void)
|
||||
{
|
||||
displayFillrect(0,47,320,200,COLOR_BACKGROUND);
|
||||
Button button;
|
||||
Button* bp;
|
||||
for(uint8_t i = 0; i < QUICKLIST_MENU_NUM_BUTTONS; ++i){
|
||||
memcpy_P(&bp, &(quickListMenuButtons[i]), sizeof(bp));
|
||||
extractAndDrawButton(&button,bp);
|
||||
}
|
||||
strncpy_P(b,(const char*)F("Short press = load\nLong press = save"),sizeof(b));
|
||||
displayText(b,10,47,170,200,COLOR_TEXT,COLOR_BACKGROUND,COLOR_BACKGROUND);
|
||||
}
|
||||
|
||||
void initQuickList(void)
|
||||
{
|
||||
quickListSelectionMode = ButtonPress_e::ShortPress;//Anything except NotPressed
|
||||
drawQuickList();
|
||||
initSelector(&quickListMenuSelectedItemRaw,
|
||||
quickListMenuButtons,
|
||||
QUICKLIST_MENU_NUM_BUTTONS,
|
||||
MorsePlaybackType_e::PlayChar);
|
||||
}
|
||||
|
||||
MenuReturn_e runQuickList(const ButtonPress_e tuner_button,
|
||||
const ButtonPress_e touch_button,
|
||||
const Point touch_point,
|
||||
const int16_t knob)
|
||||
{
|
||||
return runNpQlShared(tuner_button,
|
||||
touch_button,
|
||||
touch_point,
|
||||
knob,
|
||||
&quickListMenuSelectedItemRaw,
|
||||
quickListMenuButtons,
|
||||
QUICKLIST_MENU_NUM_BUTTONS,
|
||||
&quickListSelectionMode);
|
||||
}
|
2
menu_quicklist.h
Normal file
2
menu_quicklist.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "menu.h"
|
||||
extern Menu_t* const quickListMenu;
|
137
menu_quicklist_buttons.cpp
Normal file
137
menu_quicklist_buttons.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#include "menu_quicklist_buttons.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <WString.h>//F()
|
||||
|
||||
#include "color_theme.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const unsigned int LAYOUT_BUTTON_X = 180;
|
||||
static const unsigned int LAYOUT_BUTTON_Y = 47;
|
||||
static const unsigned int LAYOUT_BUTTON_WIDTH = 140;
|
||||
static const unsigned int LAYOUT_BUTTON_HEIGHT = 36;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_X = LAYOUT_BUTTON_WIDTH + 1;
|
||||
static const unsigned int LAYOUT_BUTTON_PITCH_Y = 40;
|
||||
|
||||
ButtonPress_e quickListSelectionMode;
|
||||
|
||||
#define D_STRINGIFY(x) #x
|
||||
#define D_STRING(x) D_STRINGIFY(x)
|
||||
#define QUICKLIST_BUTTON_GENERATE(number) \
|
||||
void toQL##number(char* text_out, const uint16_t max_text_size);\
|
||||
void osQL##number();\
|
||||
constexpr Button bQL##number PROGMEM = {\
|
||||
LAYOUT_BUTTON_X,\
|
||||
LAYOUT_BUTTON_Y + number*LAYOUT_BUTTON_PITCH_Y,\
|
||||
LAYOUT_BUTTON_WIDTH,\
|
||||
LAYOUT_BUTTON_HEIGHT,\
|
||||
nullptr,\
|
||||
toQL##number,\
|
||||
bsQuickList,\
|
||||
osQL##number,\
|
||||
'0'+number\
|
||||
}
|
||||
|
||||
ButtonStatus_e bsQuickList(void){
|
||||
return ButtonStatus_e::Stateless;
|
||||
}
|
||||
|
||||
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
|
||||
QUICKLIST_BUTTON_GENERATE(0);
|
||||
QUICKLIST_BUTTON_GENERATE(1);
|
||||
QUICKLIST_BUTTON_GENERATE(2);
|
||||
QUICKLIST_BUTTON_GENERATE(3);
|
||||
|
||||
constexpr char txtQLCancel [] PROGMEM = "Can";
|
||||
void osQLCancel();
|
||||
constexpr Button bQLCancel PROGMEM = {
|
||||
LAYOUT_BUTTON_X,
|
||||
LAYOUT_BUTTON_Y + 4*LAYOUT_BUTTON_PITCH_Y,
|
||||
LAYOUT_BUTTON_WIDTH,
|
||||
LAYOUT_BUTTON_HEIGHT,
|
||||
txtQLCancel,
|
||||
nullptr,
|
||||
bsQuickList,
|
||||
osQLCancel,
|
||||
'C'
|
||||
};
|
||||
|
||||
//Declare in menu select order, not graphical order
|
||||
const Button* const quickListMenuButtons [] PROGMEM = {
|
||||
&bQL0, &bQL1, &bQL2, &bQL3, &bQLCancel
|
||||
};
|
||||
const uint8_t QUICKLIST_MENU_NUM_BUTTONS = sizeof(quickListMenuButtons)/sizeof(quickListMenuButtons[0]);
|
||||
|
||||
void osQuickListRecall(uint8_t index)
|
||||
{
|
||||
SetActiveVfoFreq(globalSettings.quickList[index].frequency);
|
||||
SetActiveVfoMode(globalSettings.quickList[index].mode);
|
||||
}
|
||||
|
||||
void osQuickListSave(uint8_t index)
|
||||
{
|
||||
globalSettings.quickList[index].frequency = GetActiveVfoFreq();
|
||||
globalSettings.quickList[index].mode = GetActiveVfoMode();
|
||||
SaveSettingsToEeprom();
|
||||
}
|
||||
|
||||
void osQuickList(uint8_t index)
|
||||
{
|
||||
if(ButtonPress_e::ShortPress == quickListSelectionMode){
|
||||
osQuickListRecall(index);
|
||||
}
|
||||
else if(ButtonPress_e::LongPress == quickListSelectionMode){
|
||||
osQuickListSave(index);
|
||||
}
|
||||
quickListSelectionMode = ButtonPress_e::NotPressed;//Selection was made. Exit.
|
||||
}
|
||||
|
||||
#define QUICKLIST_ONSELECT_GENERATE(number) \
|
||||
void osQL##number(void){\
|
||||
osQuickList(number);\
|
||||
}
|
||||
|
||||
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
|
||||
QUICKLIST_ONSELECT_GENERATE(0);
|
||||
QUICKLIST_ONSELECT_GENERATE(1);
|
||||
QUICKLIST_ONSELECT_GENERATE(2);
|
||||
QUICKLIST_ONSELECT_GENERATE(3);
|
||||
|
||||
#define QUICKLIST_TEXTOVERRIDE_GENERATE(number) \
|
||||
void toQL##number(char* text_out, const uint16_t max_text_size){\
|
||||
toQuickList(number, text_out, max_text_size);\
|
||||
}
|
||||
|
||||
void toQuickList(uint8_t index, char* text_out, const uint16_t max_text_size)
|
||||
{
|
||||
|
||||
if(max_text_size < 2){
|
||||
return;//Can't do much with that space
|
||||
}
|
||||
if(max_text_size < (3+10+1)){
|
||||
//Give an indicator that's debuggable
|
||||
text_out[0] = 'X';
|
||||
text_out[1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
//Normal operation
|
||||
text_out[0] = '0'+index;//Assume a 1-digit number for now
|
||||
text_out[1] = ':';
|
||||
text_out[2] = ' ';
|
||||
formatFreq(globalSettings.quickList[index].frequency, text_out+3, max_text_size-3, 10);
|
||||
}
|
||||
|
||||
//Unfortunately there's no easy way to auto-generate NUM_QUICKLIST_SETTINGS copies of this
|
||||
QUICKLIST_TEXTOVERRIDE_GENERATE(0);
|
||||
QUICKLIST_TEXTOVERRIDE_GENERATE(1);
|
||||
QUICKLIST_TEXTOVERRIDE_GENERATE(2);
|
||||
QUICKLIST_TEXTOVERRIDE_GENERATE(3);
|
||||
|
||||
void osQLCancel()
|
||||
{
|
||||
quickListSelectionMode = ButtonPress_e::NotPressed;
|
||||
}
|
11
menu_quicklist_buttons.h
Normal file
11
menu_quicklist_buttons.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "button_press_e.h"
|
||||
|
||||
extern const Button* const quickListMenuButtons[];
|
||||
extern const uint8_t QUICKLIST_MENU_NUM_BUTTONS;
|
||||
|
||||
extern ButtonPress_e quickListSelectionMode;//NotPressed means exit menu. Other press types are consumed by selectors
|
103
menu_utils.cpp
Normal file
103
menu_utils.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include "menu_utils.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "color_theme.h"
|
||||
#include "morse.h"
|
||||
#include "nano_gui.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool findPressedButton(const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
Button *const button_out,
|
||||
const Point touch_point)
|
||||
{
|
||||
Button* bp;
|
||||
for(uint16_t i = 0; i < num_buttons; ++i){
|
||||
memcpy_P(&bp,&(buttons[i]),sizeof(bp));
|
||||
memcpy_P(button_out,bp,sizeof(*button_out));
|
||||
if((button_out->x <= touch_point.x)
|
||||
&&(touch_point.x <= button_out->x + button_out->w)
|
||||
&&(button_out->y <= touch_point.y)
|
||||
&&(touch_point.y <= button_out->y + button_out->h)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void movePuck(const Button *const b_old,
|
||||
const Button *const b_new)
|
||||
{
|
||||
if(nullptr != b_old){
|
||||
displayRect(b_old->x,b_old->y,b_old->w,b_old->h,COLOR_INACTIVE_BORDER);
|
||||
}
|
||||
if(nullptr != b_new){
|
||||
displayRect(b_new->x,b_new->y,b_new->w,b_new->h,COLOR_ACTIVE_BORDER);
|
||||
}
|
||||
}
|
||||
|
||||
void playButtonMorse(const Button *const button,
|
||||
const MorsePlaybackType_e play_type)
|
||||
{
|
||||
if(MorsePlaybackType_e::PlayText == play_type){
|
||||
morseText(button->text);
|
||||
}
|
||||
else{
|
||||
morseLetter(button->morse);
|
||||
}
|
||||
|
||||
const ButtonStatus_e bs = button->status();
|
||||
if(ButtonStatus_e::Inactive == bs){
|
||||
morseBool(false);
|
||||
}
|
||||
else if(ButtonStatus_e::Active == bs){
|
||||
morseBool(true);
|
||||
}
|
||||
}
|
||||
|
||||
void initSelector(int16_t *const raw_select_val_in_out,
|
||||
const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
const MorsePlaybackType_e play_type)
|
||||
{
|
||||
*raw_select_val_in_out = 0;
|
||||
if(0 < num_buttons){
|
||||
Button button;
|
||||
Button* bp;
|
||||
memcpy_P(&bp,&(buttons[0]),sizeof(bp));
|
||||
memcpy_P(&button,bp,sizeof(button));
|
||||
movePuck(nullptr,&button);
|
||||
playButtonMorse(&button,play_type);
|
||||
}
|
||||
}
|
||||
|
||||
void adjustSelector(int16_t *const raw_select_val_in_out,
|
||||
const int16_t knob,
|
||||
const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
const MorsePlaybackType_e play_type)
|
||||
{
|
||||
const uint8_t prev_select = (*raw_select_val_in_out)/MENU_KNOB_COUNTS_PER_ITEM;
|
||||
*raw_select_val_in_out = LIMIT((*raw_select_val_in_out)+knob,0,num_buttons*MENU_KNOB_COUNTS_PER_ITEM - 1);
|
||||
const uint8_t new_select = (*raw_select_val_in_out)/MENU_KNOB_COUNTS_PER_ITEM;
|
||||
if(prev_select != new_select){
|
||||
Button prev_button;
|
||||
Button* bp;
|
||||
memcpy_P(&bp,&(buttons[prev_select]),sizeof(bp));
|
||||
memcpy_P(&prev_button,bp,sizeof(prev_button));
|
||||
Button new_button;
|
||||
memcpy_P(&bp,&(buttons[new_select]),sizeof(bp));
|
||||
memcpy_P(&new_button,bp,sizeof(new_button));
|
||||
|
||||
movePuck(&prev_button,&new_button);
|
||||
playButtonMorse(&new_button,play_type);
|
||||
}
|
||||
}
|
||||
|
||||
void endSelector(const Button *const button)
|
||||
{
|
||||
movePuck(button,nullptr);
|
||||
}
|
27
menu_utils.h
Normal file
27
menu_utils.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "button.h"
|
||||
#include "menu.h"
|
||||
|
||||
//Returns true if button was found, false otherwise
|
||||
bool findPressedButton(const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
Button *const button_out,
|
||||
const Point touch_point);
|
||||
|
||||
enum MorsePlaybackType_e : uint8_t {
|
||||
PlayChar,
|
||||
PlayText
|
||||
};
|
||||
void initSelector(int16_t *const raw_select_val_in_out,
|
||||
const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
const MorsePlaybackType_e);
|
||||
|
||||
void adjustSelector(int16_t *const raw_select_val_in_out,
|
||||
int16_t knob,
|
||||
const Button* const* buttons,
|
||||
const uint8_t num_buttons,
|
||||
const MorsePlaybackType_e);
|
||||
|
||||
void endSelector(const Button *const button);
|
152
morse.cpp
152
morse.cpp
@ -1,72 +1,79 @@
|
||||
#include <Arduino.h>
|
||||
#include "ubitx.h"
|
||||
#include "toneAC2/toneAC2.h"
|
||||
|
||||
#include "encoder.h"
|
||||
#include "morse.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "settings.h"
|
||||
|
||||
struct Morse {
|
||||
char letter;
|
||||
unsigned char code;
|
||||
};
|
||||
|
||||
/*
|
||||
* Each byte of the morse table stores one letter.
|
||||
* The 0 is a dot, a 1 is a dash
|
||||
* From the Most significant byte onwards, the letter is padded with 1s.
|
||||
* The first zero after the 1s indicates the start of the letter, it MUST be discarded
|
||||
*/
|
||||
|
||||
extern int cwSpeed;
|
||||
struct Morse {
|
||||
char letter;
|
||||
unsigned char code;
|
||||
};
|
||||
|
||||
static const PROGMEM struct Morse morse_table[] = {
|
||||
{'a', 0xf9}, // 11111001
|
||||
{'b', 0xe8}, // 11101000
|
||||
{'c', 0xea}, // 11101010
|
||||
{'d', 0xf4}, // 11110100
|
||||
{'e', 0xfc}, // 11111100
|
||||
{'f', 0xe4}, // 11100100
|
||||
{'g', 0xf6}, // 11110110
|
||||
{'h', 0xe0}, // 11100000
|
||||
{'i', 0xf8}, // 11111000
|
||||
{'j', 0xe7}, // 11100111
|
||||
{'k', 0xf6}, // 11110101
|
||||
{'l', 0xe4}, // 11100100
|
||||
{'m', 0xfb}, // 11111011
|
||||
{'n', 0xfa}, // 11111010
|
||||
{'o', 0xf7}, // 11110111
|
||||
{'p', 0xe6}, // 11100110
|
||||
{'q', 0xed}, // 11101101
|
||||
{'r', 0xf2}, // 11110010
|
||||
{'s', 0xf0}, // 11110000
|
||||
{'t', 0xfd}, // 11111101
|
||||
{'u', 0xf1}, // 11110001
|
||||
{'v', 0xe1}, // 11100001
|
||||
{'w', 0xf3}, // 11110011
|
||||
{'x', 0xe9}, // 11101001
|
||||
{'y', 0xe3}, // 11101011
|
||||
{'z', 0xec}, // 11101100
|
||||
{'1', 0xcf}, // 11001111
|
||||
{'2', 0xc7}, // 11000111
|
||||
{'3', 0xc3}, // 11000011
|
||||
{'4', 0xc1}, // 11000001
|
||||
{'5', 0xc0}, // 11000000
|
||||
{'6', 0xd0}, // 11010000
|
||||
{'7', 0xd8}, // 11011000
|
||||
{'8', 0xdc}, // 11011100
|
||||
{'9', 0xde}, // 11011110
|
||||
{'0', 0xdf}, // 11011111
|
||||
{'.', 0xd5}, // 110010101
|
||||
{',', 0xd3}, // 110110011 //AD7U 20191217
|
||||
{'?', 0xcc}, // 11001100 //AD7U 20191217 - Added
|
||||
{'a', 0b11111001},
|
||||
{'b', 0b11101000},
|
||||
{'c', 0b11101010},
|
||||
{'d', 0b11110100},
|
||||
{'e', 0b11111100},
|
||||
{'f', 0b11100010},
|
||||
{'g', 0b11110110},
|
||||
{'h', 0b11100000},
|
||||
{'i', 0b11111000},
|
||||
{'j', 0b11100111},
|
||||
{'k', 0b11110101},
|
||||
{'l', 0b11100100},
|
||||
{'m', 0b11111011},
|
||||
{'n', 0b11111010},
|
||||
{'o', 0b11110111},
|
||||
{'p', 0b11100110},
|
||||
{'q', 0b11101101},
|
||||
{'r', 0b11110010},
|
||||
{'s', 0b11110000},
|
||||
{'t', 0b11111101},
|
||||
{'u', 0b11110001},
|
||||
{'v', 0b11100001},
|
||||
{'w', 0b11110011},
|
||||
{'x', 0b11101001},
|
||||
{'y', 0b11101011},
|
||||
{'z', 0b11101100},
|
||||
{'1', 0b11001111},
|
||||
{'2', 0b11000111},
|
||||
{'3', 0b11000011},
|
||||
{'4', 0b11000001},
|
||||
{'5', 0b11000000},
|
||||
{'6', 0b11010000},
|
||||
{'7', 0b11011000},
|
||||
{'8', 0b11011100},
|
||||
{'9', 0b11011110},
|
||||
{'0', 0b11011111},
|
||||
{'.', 0b10010101},
|
||||
{',', 0b10110011},
|
||||
{'?', 0b10001100},
|
||||
{ 2 , 0b11010101}, // ASCII 0x02 is Start of Text - <CT>
|
||||
{ 4 , 0b10000101}, // ASCII 0x04 is End of Transmission - <CL> is too long for our encoding scheme in 8 bits, but <SK> fits
|
||||
};
|
||||
|
||||
static void morseLetter(char c){
|
||||
void morseLetter(char c, uint16_t dit_duration_ms){
|
||||
if(!globalSettings.morseMenuOn){
|
||||
return;
|
||||
}
|
||||
unsigned char mask = 0x80;
|
||||
|
||||
//handle space character as three dashes
|
||||
if (c == ' '){
|
||||
active_delay(cwSpeed * 9);
|
||||
Serial.print(' ');
|
||||
delay(7 * dit_duration_ms);
|
||||
//Serial.print(' ');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(morse_table)/ sizeof(struct Morse); i++){
|
||||
for (unsigned int i = 0; i < sizeof(morse_table)/ sizeof(struct Morse); i++){
|
||||
struct Morse m;
|
||||
memcpy_P(&m, morse_table + i, sizeof(struct Morse));
|
||||
|
||||
@ -79,37 +86,44 @@ static void morseLetter(char c){
|
||||
//now we are at the first zero, skip and carry on
|
||||
mask = mask >> 1;
|
||||
while(mask){
|
||||
tone(CW_TONE, sideTone,10000);
|
||||
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq);
|
||||
if (mask & code){
|
||||
delay(3 * (int)cwSpeed);
|
||||
delay(3 * dit_duration_ms);
|
||||
//Serial.print('-');
|
||||
}
|
||||
else{
|
||||
delay((int)cwSpeed);
|
||||
delay(dit_duration_ms);
|
||||
//Serial.print('.');
|
||||
}
|
||||
//Serial.print('#');
|
||||
noTone(CW_TONE);
|
||||
delay((int)cwSpeed); // space between dots and dashes
|
||||
noToneAC2();
|
||||
delay(dit_duration_ms); // space between dots and dashes
|
||||
mask = mask >> 1;
|
||||
}
|
||||
//Serial.println('@');
|
||||
delay(200); // space between letters is a dash (3 dots), one dot's space has already been sent
|
||||
delay(2*dit_duration_ms); // space between letters is a dash (3 dots), one dot's space has already been sent
|
||||
break;//We've played the letter, so don't bother checking the rest of the list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void morseText(char *text){
|
||||
// while (1){
|
||||
noTone(CW_TONE);
|
||||
delay(1000);
|
||||
tone(CW_TONE, 600);
|
||||
delay(1000);
|
||||
// }
|
||||
|
||||
Serial.println(sideTone);
|
||||
while(*text){
|
||||
morseLetter(*text++);
|
||||
static const uint8_t RELATIVE_OFFSET_HZ = 100;
|
||||
void morseText(const char *text, uint16_t dit_duration_ms){
|
||||
int16_t total_counts = 0;
|
||||
morseBool(false);
|
||||
enc_read();//Don't count initial tone against total_counts
|
||||
while(*text && (abs(total_counts) < 10)){
|
||||
morseLetter(*text++, dit_duration_ms);
|
||||
total_counts += enc_read();
|
||||
}
|
||||
}
|
||||
|
||||
void morseBool(bool val){
|
||||
if(!globalSettings.morseMenuOn){
|
||||
return;
|
||||
}
|
||||
toneAC2(PIN_CW_TONE, globalSettings.cwSideToneFreq + (val ? RELATIVE_OFFSET_HZ : -RELATIVE_OFFSET_HZ));
|
||||
delay(3*globalSettings.cwDitDurationMs);
|
||||
noToneAC2();
|
||||
delay(3*globalSettings.cwDitDurationMs);
|
||||
}
|
||||
|
8
morse.h
8
morse.h
@ -1,3 +1,7 @@
|
||||
#include "settings.h"
|
||||
//sends out morse code at the speed set by cwSpeed
|
||||
extern int cwSpeed; //this is actuall the dot period in milliseconds
|
||||
void morseText(char *text);
|
||||
void morseLetter(char c, uint16_t dit_duration_ms = globalSettings.cwDitDurationMs);
|
||||
void morseText(const char *text, uint16_t dit_duration_ms = globalSettings.cwDitDurationMs);
|
||||
|
||||
//Plays either a higher or lower tone to indicate a boolean value
|
||||
void morseBool(bool val);
|
||||
|
@ -2,5 +2,11 @@
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "PDQ_MinLib/FreeSansBold9pt7b.h"
|
||||
//#include "PDQ_MinLib/Picopixel.h"
|
||||
//#include "PDQ_MinLib/org_01.h"
|
||||
//#include "PDQ_MinLib/TomThumb.h"
|
||||
|
||||
const GFXfont* ubitx_font = &FreeSansBold9pt7b;
|
||||
const GFXfont* ubitx_font = &FreeSansBold9pt7b;
|
||||
//const GFXfont* ubitx_font = &Picopixel;
|
||||
//const GFXfont* ubitx_font = &Org_01;
|
||||
//const GFXfont* ubitx_font = &TomThumb;
|
||||
|
329
nano_gui.cpp
329
nano_gui.cpp
@ -1,195 +1,65 @@
|
||||
#include <Arduino.h>
|
||||
#include <EEPROM.h>
|
||||
#include "ubitx.h"
|
||||
#include "nano_gui.h"
|
||||
#include "colors.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "push_button.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "touch.h"
|
||||
|
||||
#include <SPI.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
struct Point ts_point;
|
||||
|
||||
//filled from a test run of calibration routine
|
||||
int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
|
||||
|
||||
void readTouchCalibration(){
|
||||
EEPROM.get(SLOPE_X, slope_x);
|
||||
EEPROM.get(SLOPE_Y, slope_y);
|
||||
EEPROM.get(OFFSET_X, offset_x);
|
||||
EEPROM.get(OFFSET_Y, offset_y);
|
||||
|
||||
/*
|
||||
//for debugging
|
||||
Serial.print(slope_x); Serial.print(' ');
|
||||
Serial.print(slope_y); Serial.print(' ');
|
||||
Serial.print(offset_x); Serial.print(' ');
|
||||
Serial.println(offset_y); Serial.println(' ');
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void writeTouchCalibration(){
|
||||
EEPROM.put(SLOPE_X, slope_x);
|
||||
EEPROM.put(SLOPE_Y, slope_y);
|
||||
EEPROM.put(OFFSET_X, offset_x);
|
||||
EEPROM.put(OFFSET_Y, offset_y);
|
||||
}
|
||||
|
||||
#define Z_THRESHOLD 400
|
||||
#define Z_THRESHOLD_INT 75
|
||||
#define MSEC_THRESHOLD 3
|
||||
|
||||
static uint32_t msraw=0x80000000;
|
||||
static int16_t xraw=0, yraw=0, zraw=0;
|
||||
static uint8_t rotation = 1;
|
||||
|
||||
static int16_t touch_besttwoavg( int16_t x , int16_t y , int16_t z ) {
|
||||
int16_t da, db, dc;
|
||||
int16_t reta = 0;
|
||||
if ( x > y ) da = x - y; else da = y - x;
|
||||
if ( x > z ) db = x - z; else db = z - x;
|
||||
if ( z > y ) dc = z - y; else dc = y - z;
|
||||
|
||||
if ( da <= db && da <= dc ) reta = (x + y) >> 1;
|
||||
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
|
||||
else reta = (y + z) >> 1; // else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;
|
||||
|
||||
return (reta);
|
||||
}
|
||||
|
||||
static void touch_update(){
|
||||
int16_t data[6];
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - msraw < MSEC_THRESHOLD) return;
|
||||
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV8);//2MHz
|
||||
digitalWrite(CS_PIN, LOW);
|
||||
SPI.transfer(0xB1 /* Z1 */);
|
||||
int16_t z1 = SPI.transfer16(0xC1 /* Z2 */) >> 3;
|
||||
int z = z1 + 4095;
|
||||
int16_t z2 = SPI.transfer16(0x91 /* X */) >> 3;
|
||||
z -= z2;
|
||||
if (z >= Z_THRESHOLD) {
|
||||
SPI.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy
|
||||
data[0] = SPI.transfer16(0xD1 /* Y */) >> 3;
|
||||
data[1] = SPI.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
|
||||
data[2] = SPI.transfer16(0xD1 /* Y */) >> 3;
|
||||
data[3] = SPI.transfer16(0x91 /* X */) >> 3;
|
||||
}
|
||||
else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit.
|
||||
data[4] = SPI.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down
|
||||
data[5] = SPI.transfer16(0) >> 3;
|
||||
digitalWrite(CS_PIN, HIGH);
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV2);//Return to full speed for TFT
|
||||
|
||||
if (z < 0) z = 0;
|
||||
if (z < Z_THRESHOLD) { // if ( !touched ) {
|
||||
// Serial.println();
|
||||
zraw = 0;
|
||||
return;
|
||||
}
|
||||
zraw = z;
|
||||
|
||||
int16_t x = touch_besttwoavg( data[0], data[2], data[4] );
|
||||
int16_t y = touch_besttwoavg( data[1], data[3], data[5] );
|
||||
|
||||
//Serial.printf(" %d,%d", x, y);
|
||||
//Serial.println();
|
||||
if (z >= Z_THRESHOLD) {
|
||||
msraw = now; // good read completed, set wait
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
xraw = 4095 - y;
|
||||
yraw = x;
|
||||
break;
|
||||
case 1:
|
||||
xraw = x;
|
||||
yraw = y;
|
||||
break;
|
||||
case 2:
|
||||
xraw = y;
|
||||
yraw = 4095 - x;
|
||||
break;
|
||||
default: // 3
|
||||
xraw = 4095 - x;
|
||||
yraw = 4095 - y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean readTouch(){
|
||||
touch_update();
|
||||
if (zraw >= Z_THRESHOLD) {
|
||||
ts_point.x = xraw;
|
||||
ts_point.y = yraw;
|
||||
//Serial.print(ts_point.x); Serial.print(",");Serial.println(ts_point.y);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void scaleTouch(struct Point *p){
|
||||
p->x = ((long)(p->x - offset_x) * 10l)/ (long)slope_x;
|
||||
p->y = ((long)(p->y - offset_y) * 10l)/ (long)slope_y;
|
||||
|
||||
//Serial.print(p->x); Serial.print(",");Serial.println(p->y);
|
||||
}
|
||||
|
||||
/*****************
|
||||
* Begin TFT functions
|
||||
*****************/
|
||||
#define ILI9341_CS_PIN TFT_CS
|
||||
#define ILI9341_DC_PIN TFT_DC
|
||||
#define ILI9341_CS_PIN PIN_TFT_CS
|
||||
#define ILI9341_DC_PIN PIN_TFT_DC
|
||||
#define ILI9341_SAVE_SPCR (1) //Save state before/after to play nice with the touch screen
|
||||
#include "PDQ_MinLib/PDQ_ILI9341.h"
|
||||
PDQ_ILI9341 tft;
|
||||
|
||||
#include "nano_font.h"
|
||||
|
||||
|
||||
bool xpt2046_Init(){
|
||||
pinMode(CS_PIN, OUTPUT);
|
||||
digitalWrite(CS_PIN, HIGH);
|
||||
}
|
||||
|
||||
void displayInit(void){
|
||||
//Pulling this low 6 times should exit deep sleep mode
|
||||
pinMode(TFT_CS,OUTPUT);
|
||||
pinMode(PIN_TFT_CS,OUTPUT);
|
||||
for(uint8_t i = 0; i < 6; ++i){
|
||||
digitalWrite(TFT_CS,HIGH);
|
||||
digitalWrite(TFT_CS,LOW);
|
||||
digitalWrite(PIN_TFT_CS,HIGH);
|
||||
digitalWrite(PIN_TFT_CS,LOW);
|
||||
}
|
||||
digitalWrite(TFT_CS,HIGH);//Disable writing for now
|
||||
digitalWrite(PIN_TFT_CS,HIGH);//Disable writing for now
|
||||
|
||||
tft.begin();
|
||||
tft.setFont(ubitx_font);
|
||||
tft.setTextWrap(false);
|
||||
tft.setTextWrap(true);
|
||||
tft.setTextColor(DISPLAY_GREEN,DISPLAY_BLACK);
|
||||
tft.setTextSize(1);
|
||||
tft.setRotation(1);
|
||||
|
||||
xpt2046_Init();
|
||||
readTouchCalibration();
|
||||
}
|
||||
|
||||
void displayPixel(unsigned int x, unsigned int y, unsigned int c){
|
||||
tft.drawPixel(x,y,c);
|
||||
tft.fillRect(x,y,1,1,c);
|
||||
}
|
||||
|
||||
void displayHline(unsigned int x, unsigned int y, unsigned int w, unsigned int c){
|
||||
tft.drawFastHLine(x,y,w,c);
|
||||
tft.fillRect(x,y,w,1,c);
|
||||
}
|
||||
|
||||
void displayVline(unsigned int x, unsigned int y, unsigned int l, unsigned int c){
|
||||
tft.drawFastVLine(x,y,l,c);
|
||||
tft.fillRect(x,y,1,l,c);
|
||||
}
|
||||
|
||||
void displayClear(unsigned int color){
|
||||
void displayClear(unsigned int color){
|
||||
tft.fillRect(0,0,320,240,color);
|
||||
}
|
||||
|
||||
void displayRect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c){
|
||||
tft.drawRect(x,y,w,h,c);
|
||||
tft.fillRect(x,y,w,1,c);
|
||||
tft.fillRect(x,y,1,h,c);
|
||||
tft.fillRect(x,y+h-1,w,1,c);
|
||||
tft.fillRect(x+w-1,y,1,h,c);
|
||||
}
|
||||
|
||||
void displayFillrect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c){
|
||||
@ -200,13 +70,15 @@ void displayChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t
|
||||
tft.drawCharGFX(x,y,c,color,bg,1);
|
||||
}
|
||||
|
||||
void displayRawText(char *text, int x1, int y1, int color, int background){
|
||||
void displayRawText(const char *text, int x1, int y1, int w, int color, int background){
|
||||
tft.setTextColor(color,background);
|
||||
tft.setCursor(x1,y1);
|
||||
tft.setBound(x1,x1+w);
|
||||
tft.print(text);
|
||||
}
|
||||
|
||||
void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border) {
|
||||
void displayText(const char *const text, int x1, int y1, int w, int h, int color, int background, int border, TextJustification_e justification)
|
||||
{
|
||||
displayFillrect(x1, y1, w ,h, background);
|
||||
displayRect(x1, y1, w ,h, border);
|
||||
|
||||
@ -214,106 +86,91 @@ void displayText(char *text, int x1, int y1, int w, int h, int color, int backgr
|
||||
int16_t y1_out;
|
||||
uint16_t width_out;
|
||||
uint16_t height_out;
|
||||
tft.getTextBounds(text,x1,y1,&x1_out,&y1_out,&width_out,&height_out);
|
||||
x1 += (w - ( width_out + (x1_out-x1)))/2;
|
||||
y1 += h - (h - height_out)/2;
|
||||
displayRawText(text,x1,y1,color,background);
|
||||
tft.getTextBounds(text,x1,y1,&x1_out,&y1_out,&width_out,&height_out,w);
|
||||
if(TextJustification_e::Center == justification){
|
||||
x1 += (w - ( (int32_t)width_out + (x1_out-x1)))/2;
|
||||
}
|
||||
else if(TextJustification_e::Right == justification){
|
||||
x1 += w - ((int32_t)width_out + (x1_out-x1));
|
||||
}
|
||||
else{
|
||||
x1 += 2;//Give a little bit of padding from the border
|
||||
}
|
||||
y1 += (ubitx_font->yAdvance + h - ( (int32_t)height_out))/2;
|
||||
displayRawText(text,x1,y1,w,color,background);
|
||||
}
|
||||
|
||||
void drawCross(int16_t x_center,int16_t y_center,uint16_t color)
|
||||
{
|
||||
constexpr uint8_t HALF_SIZE = 10;
|
||||
displayHline(x_center-HALF_SIZE,y_center,2*HALF_SIZE,color);
|
||||
displayVline(x_center,y_center-HALF_SIZE,2*HALF_SIZE,color);
|
||||
}
|
||||
|
||||
void setupTouch(){
|
||||
int x1, y1, x2, y2, x3, y3, x4, y4;
|
||||
constexpr int16_t CROSS_CORNER_OFFSET = 20;
|
||||
constexpr Point CROSS_CORNER_POINTS [] = {
|
||||
{CROSS_CORNER_OFFSET,CROSS_CORNER_OFFSET},//Top left
|
||||
{PDQ_ILI9341::ILI9341_TFTHEIGHT-CROSS_CORNER_OFFSET,CROSS_CORNER_OFFSET},//Top right
|
||||
{CROSS_CORNER_OFFSET, PDQ_ILI9341::ILI9341_TFTWIDTH-CROSS_CORNER_OFFSET},//Bottom left
|
||||
{PDQ_ILI9341::ILI9341_TFTHEIGHT-CROSS_CORNER_OFFSET,PDQ_ILI9341::ILI9341_TFTWIDTH-CROSS_CORNER_OFFSET}//Bottom right
|
||||
};
|
||||
|
||||
displayClear(DISPLAY_BLACK);
|
||||
displayText("Click on the cross", 20,100, 200, 50, DISPLAY_WHITE, DISPLAY_BLACK, DISPLAY_BLACK);
|
||||
strncpy_P(b,(const char*)F("Click on the cross\nPush tune to cancel"),sizeof(b));
|
||||
displayText(b, 20,100, 200, 50, DISPLAY_WHITE, DISPLAY_BLACK, DISPLAY_BLACK);
|
||||
|
||||
// TOP-LEFT
|
||||
displayHline(10,20,20,DISPLAY_WHITE);
|
||||
displayVline(20,10,20, DISPLAY_WHITE);
|
||||
Point cal_points[sizeof(CROSS_CORNER_POINTS)/sizeof(CROSS_CORNER_POINTS[0])];
|
||||
|
||||
while(!readTouch())
|
||||
delay(100);
|
||||
while(readTouch())
|
||||
delay(100);
|
||||
x1 = ts_point.x;
|
||||
y1 = ts_point.y;
|
||||
for(uint8_t i = 0; i < sizeof(CROSS_CORNER_POINTS)/sizeof(CROSS_CORNER_POINTS[0]); ++i){
|
||||
drawCross(CROSS_CORNER_POINTS[i].x,CROSS_CORNER_POINTS[i].y,DISPLAY_WHITE);
|
||||
while(!readTouch(&cal_points[i])){
|
||||
if(ButtonPress_e::NotPressed != CheckTunerButton()){
|
||||
return;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
while(readTouch(&cal_points[i])){
|
||||
delay(100);
|
||||
}
|
||||
drawCross(CROSS_CORNER_POINTS[i].x,CROSS_CORNER_POINTS[i].y,DISPLAY_BLACK);
|
||||
delay(1000);//Ensure that nobody is pressing the screen before we do the next point
|
||||
}
|
||||
|
||||
//rubout the previous one
|
||||
displayHline(10,20,20,DISPLAY_BLACK);
|
||||
displayVline(20,10,20, DISPLAY_BLACK);
|
||||
//We can get nicer scaling if we allow more resolution on the divisor
|
||||
constexpr int32_t SCALE_SENSITIVITY_MULTIPLIER = 10;
|
||||
|
||||
delay(1000);
|
||||
|
||||
//TOP RIGHT
|
||||
displayHline(290,20,20,DISPLAY_WHITE);
|
||||
displayVline(300,10,20, DISPLAY_WHITE);
|
||||
const int16_t diff_x_top = cal_points[1].x - cal_points[0].x;
|
||||
const int16_t diff_x_bottom = cal_points[3].x - cal_points[2].x;
|
||||
constexpr int32_t diff_x_target = CROSS_CORNER_POINTS[1].x - CROSS_CORNER_POINTS[0].x;
|
||||
|
||||
while(!readTouch())
|
||||
delay(100);
|
||||
while(readTouch())
|
||||
delay(100);
|
||||
x2 = ts_point.x;
|
||||
y2 = ts_point.y;
|
||||
//Average the measured differences
|
||||
globalSettings.touchSlopeX = SCALE_SENSITIVITY_MULTIPLIER*(diff_x_top + diff_x_bottom) / (2*diff_x_target);
|
||||
|
||||
displayHline(290,20,20,DISPLAY_BLACK);
|
||||
displayVline(300,10,20, DISPLAY_BLACK);
|
||||
const int16_t diff_y_left = cal_points[2].y - cal_points[0].y;
|
||||
const int16_t diff_y_right = cal_points[3].y - cal_points[1].y;
|
||||
constexpr int32_t diff_y_target = CROSS_CORNER_POINTS[2].y - CROSS_CORNER_POINTS[0].y;
|
||||
|
||||
delay(1000);
|
||||
//Average the measured differences
|
||||
globalSettings.touchSlopeY = SCALE_SENSITIVITY_MULTIPLIER*(diff_y_left + diff_y_right) / (2*diff_y_target);
|
||||
|
||||
//BOTTOM LEFT
|
||||
displayHline(10,220,20,DISPLAY_WHITE);
|
||||
displayVline(20,210,20, DISPLAY_WHITE);
|
||||
|
||||
while(!readTouch())
|
||||
delay(100);
|
||||
x3 = ts_point.x;
|
||||
y3 = ts_point.y;
|
||||
|
||||
while(readTouch())
|
||||
delay(100);
|
||||
displayHline(10,220,20,DISPLAY_BLACK);
|
||||
displayVline(20,210,20, DISPLAY_BLACK);
|
||||
globalSettings.touchOffsetX = cal_points[0].x - ((CROSS_CORNER_OFFSET * globalSettings.touchSlopeX)/SCALE_SENSITIVITY_MULTIPLIER);
|
||||
globalSettings.touchOffsetY = cal_points[0].y - ((CROSS_CORNER_OFFSET * globalSettings.touchSlopeY)/SCALE_SENSITIVITY_MULTIPLIER);
|
||||
|
||||
delay(1000);
|
||||
|
||||
//BOTTOM RIGHT
|
||||
displayHline(290,220,20,DISPLAY_WHITE);
|
||||
displayVline(300,210,20, DISPLAY_WHITE);
|
||||
|
||||
while(!readTouch())
|
||||
delay(100);
|
||||
x4 = ts_point.x;
|
||||
y4 = ts_point.y;
|
||||
|
||||
|
||||
displayHline(290,220,20,DISPLAY_BLACK);
|
||||
displayVline(300,210,20, DISPLAY_BLACK);
|
||||
|
||||
// we average two readings and divide them by half and store them as scaled integers 10 times their actual, fractional value
|
||||
//the x points are located at 20 and 300 on x axis, hence, the delta x is 280, we take 28 instead, to preserve fractional value,
|
||||
//there are two readings (x1,x2) and (x3, x4). Hence, we have to divide by 28 * 2 = 56
|
||||
slope_x = ((x4 - x3) + (x2 - x1))/56;
|
||||
//the y points are located at 20 and 220 on the y axis, hence, the delta is 200. we take it as 20 instead, to preserve the fraction value
|
||||
//there are two readings (y1, y2) and (y3, y4). Hence we have to divide by 20 * 2 = 40
|
||||
slope_y = ((y3 - y1) + (y4 - y2))/40;
|
||||
|
||||
//x1, y1 is at 20 pixels
|
||||
offset_x = x1 + -((20 * slope_x)/10);
|
||||
offset_y = y1 + -((20 * slope_y)/10);
|
||||
|
||||
/*
|
||||
Serial.print(x1);Serial.print(':');Serial.println(y1);
|
||||
Serial.print(x2);Serial.print(':');Serial.println(y2);
|
||||
Serial.print(x3);Serial.print(':');Serial.println(y3);
|
||||
Serial.print(x4);Serial.print(':');Serial.println(y4);
|
||||
for(uint8_t i = 0; i < sizeof(cal_points)/sizeof(cal_points[0]); ++i){
|
||||
Serial.print(cal_points[i].x);Serial.print(':');Serial.println(cal_points[i].y);
|
||||
}
|
||||
|
||||
//for debugging
|
||||
Serial.print(slope_x); Serial.print(' ');
|
||||
Serial.print(slope_y); Serial.print(' ');
|
||||
Serial.print(offset_x); Serial.print(' ');
|
||||
Serial.println(offset_y); Serial.println(' ');
|
||||
*/
|
||||
writeTouchCalibration();
|
||||
displayClear(DISPLAY_BLACK);
|
||||
Serial.print(globalSettings.touchSlopeX); Serial.print(' ');
|
||||
Serial.print(globalSettings.touchSlopeY); Serial.print(' ');
|
||||
Serial.print(globalSettings.touchOffsetX); Serial.print(' ');
|
||||
Serial.println(globalSettings.touchOffsetY); Serial.println(' ');
|
||||
*/
|
||||
|
||||
SaveSettingsToEeprom();
|
||||
}
|
||||
|
||||
|
||||
|
40
nano_gui.h
40
nano_gui.h
@ -1,11 +1,12 @@
|
||||
#ifndef _NANO_GUI_H_
|
||||
#define _NANO_GUI_H_
|
||||
|
||||
/* UI functions */
|
||||
struct Point {
|
||||
int x, y;
|
||||
enum TextJustification_e : uint8_t
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Center
|
||||
};
|
||||
extern struct Point ts_point;
|
||||
|
||||
void displayInit();
|
||||
void displayClear(unsigned int color);
|
||||
@ -15,35 +16,10 @@ void displayVline(unsigned int x, unsigned int y, unsigned int l, unsigned int c
|
||||
void displayRect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c);
|
||||
void displayFillrect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c);
|
||||
void displayChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg);
|
||||
void displayRawText(char *text, int x1, int y1, int color, int background);
|
||||
void displayText(char *text, int x1, int y1, int w, int h, int color, int background, int border);
|
||||
void displayText(const char *const text, int x1, int y1, int w, int h, int color, int background, int border, TextJustification_e justification = TextJustification_e::Center);
|
||||
|
||||
/* touch functions */
|
||||
boolean readTouch();
|
||||
|
||||
void setupTouch();
|
||||
void scaleTouch(struct Point *p);
|
||||
|
||||
// Color definitions
|
||||
#define DISPLAY_BLACK 0x0000 ///< 0, 0, 0
|
||||
#define DISPLAY_NAVY 0x000F ///< 0, 0, 123
|
||||
#define DISPLAY_DARKGREEN 0x03E0 ///< 0, 125, 0
|
||||
#define DISPLAY_DARKCYAN 0x03EF ///< 0, 125, 123
|
||||
#define DISPLAY_MAROON 0x7800 ///< 123, 0, 0
|
||||
#define DISPLAY_PURPLE 0x780F ///< 123, 0, 123
|
||||
#define DISPLAY_OLIVE 0x7BE0 ///< 123, 125, 0
|
||||
#define DISPLAY_LIGHTGREY 0xC618 ///< 198, 195, 198
|
||||
#define DISPLAY_DARKGREY 0x7BEF ///< 123, 125, 123
|
||||
#define DISPLAY_BLUE 0x001F ///< 0, 0, 255
|
||||
#define DISPLAY_GREEN 0x07E0 ///< 0, 255, 0
|
||||
#define DISPLAY_CYAN 0x07FF ///< 0, 255, 255
|
||||
#define DISPLAY_RED 0xF800 ///< 255, 0, 0
|
||||
#define DISPLAY_MAGENTA 0xF81F ///< 255, 0, 255
|
||||
#define DISPLAY_YELLOW 0xFFE0 ///< 255, 255, 0
|
||||
#define DISPLAY_WHITE 0xFFFF ///< 255, 255, 255
|
||||
#define DISPLAY_ORANGE 0xFD20 ///< 255, 165, 0
|
||||
#define DISPLAY_GREENYELLOW 0xAFE5 ///< 173, 255, 41
|
||||
#define DISPLAY_PINK 0xFC18 ///< 255, 130, 198
|
||||
/* these functions are called universally to update the display */
|
||||
void drawTx();
|
||||
|
||||
#define TEXT_LINE_HEIGHT 18
|
||||
#define TEXT_LINE_INDENT 5
|
||||
|
45
pin_definitions.h
Normal file
45
pin_definitions.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Pin configurations for uBiTXv6
|
||||
*
|
||||
* Attached devices are:
|
||||
* * A rotory encoder for tuning, which has a built-in push button for selection
|
||||
* * A Push-to-talk button input
|
||||
* * A morse keyer input, using analog voltage divider for paddle support
|
||||
* * A SI5351A 3-output frequency generator
|
||||
* * A model tjctm24028-spi touch screen LCD, which has:
|
||||
* * An ILI9341 display controller
|
||||
* * A XPT2046 touch controller
|
||||
* * A TX/RX output control pin
|
||||
* * A tone output pin to provide audio feedback to the operator when sending CW
|
||||
* * Three filter selection output control pins
|
||||
* * A CW keydown output
|
||||
* * Optionally, a serial CAT interface
|
||||
*/
|
||||
|
||||
static const uint8_t PIN_ENC_A = A0; // Tuning encoder interface
|
||||
static const uint8_t PIN_ENC_B = A1; // Tuning encoder interface
|
||||
static const uint8_t PIN_ENC_PUSH_BUTTON = A2; // Tuning encoder interface
|
||||
static const uint8_t PIN_PTT = A3; // Sense it for ssb and as a straight key for cw operation
|
||||
//A4 is I2C SDA
|
||||
//A5 is I2C SCK
|
||||
static const uint8_t PIN_ANALOG_KEYER = A6; // This is used as keyer. The analog port has 4.7K pull up resistor. Details are in the circuit description on www.hfsignals.com
|
||||
static const uint8_t PIN_ANALOG_SPARE = A7; // Not used yet
|
||||
|
||||
//13 is SPI CLK
|
||||
//12 is SPI MISO
|
||||
//11 is SPI MOSI
|
||||
static const uint8_t PIN_TFT_CS = 10; // Selects the LCD controller on SPI interface (active low)
|
||||
static const uint8_t PIN_TFT_DC = 9; // Tells the LCD controller if it's getting data (D, high) or commands (C, low)
|
||||
static const uint8_t PIN_TOUCH_CS = 8; // Selects the touch controller on SPI interface (active low)
|
||||
static const uint8_t PIN_TX_RXn = 7; // Pin from the Nano to the radio to switch to TX (HIGH) and RX(LOW)
|
||||
static const uint8_t PIN_CW_TONE = 6; // Generates a square wave sidetone while sending the CW
|
||||
static const uint8_t PIN_TX_LPF_A = 5; // The 30 MHz LPF is permanently connected in the output of the PA...
|
||||
static const uint8_t PIN_TX_LPF_B = 4; // ...Alternatively, either 3.5 MHz, 7 MHz or 14 Mhz LPFs are...
|
||||
static const uint8_t PIN_TX_LPF_C = 3; // ...switched inline depending upon the TX frequency
|
||||
static const uint8_t PIN_CW_KEY = 2; // Pin goes high during CW keydown to transmit the carrier.
|
||||
// ... The PIN_CW_KEY is needed in addition to the TX/RX key as the...
|
||||
// ...key can be up within a tx period
|
||||
//1 is UART RX
|
||||
//0 is UART TX
|
8
point.h
Normal file
8
point.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Point {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
};
|
34
push_button.cpp
Normal file
34
push_button.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include <Arduino.h>
|
||||
#include "button_timing.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "push_button.h"
|
||||
|
||||
bool IsButtonPressed()
|
||||
{
|
||||
//Button has a pullup, so it reads high normally,
|
||||
//and reads low when pressed down
|
||||
return !digitalRead(PIN_ENC_PUSH_BUTTON);
|
||||
}
|
||||
|
||||
ButtonPress_e CheckTunerButton(){
|
||||
if (!IsButtonPressed()){
|
||||
return ButtonPress_e::NotPressed;
|
||||
}
|
||||
delay(DEBOUNCE_DELAY_MS);
|
||||
if (!IsButtonPressed()){//debounce
|
||||
return ButtonPress_e::NotPressed;
|
||||
}
|
||||
|
||||
uint16_t down_time = 0;
|
||||
while(IsButtonPressed() && (down_time < LONG_PRESS_TIME_MS)){
|
||||
delay(LONG_PRESS_POLL_TIME_MS);
|
||||
down_time += LONG_PRESS_POLL_TIME_MS;
|
||||
}
|
||||
|
||||
if(down_time < LONG_PRESS_TIME_MS){
|
||||
return ButtonPress_e::ShortPress;
|
||||
}
|
||||
else{
|
||||
return ButtonPress_e::LongPress;
|
||||
}
|
||||
}
|
5
push_button.h
Normal file
5
push_button.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "button_press_e.h"
|
||||
|
||||
ButtonPress_e CheckTunerButton();
|
13
scratch_space.h
Normal file
13
scratch_space.h
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
|
||||
* We have to be very careful with variables that are declared inside the functions as they are
|
||||
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
|
||||
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
|
||||
* and mess up your programs.
|
||||
* We circumvent this by declaring a few global buffers as kitchen counters where we can
|
||||
* slice and dice our strings. These strings are mostly used to control the display or handle
|
||||
* the input and output from the USB port. We must keep a count of the bytes used while reading
|
||||
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
|
||||
*/
|
||||
|
||||
extern char c[30], b[128];
|
193
settings.cpp
Normal file
193
settings.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include <string.h>//memset
|
||||
#include <stdint.h>
|
||||
#include <EEPROM.h>
|
||||
#include <Arduino.h>//only needed for debugging's Serial.print stuff
|
||||
#include "nano_gui.h"//redrawVFOs() function
|
||||
#include "settings.h"
|
||||
#include "si5351.h"
|
||||
|
||||
/**
|
||||
* These are the "magic" indices where these user changable settinngs are stored in the EEPROM
|
||||
*/
|
||||
static const uint16_t EEPROM_ADDR_MASTER_CAL = 0;//int32_t
|
||||
//4 is currently unused, but may have been LSB_CAL on other versions
|
||||
static const uint16_t EEPROM_ADDR_USB_CAL = 8;//uint32_t
|
||||
//12 is currently unused, but may have been CW_SIDETONE on other versions?
|
||||
static const uint16_t EEPROM_ADDR_VFO_A_FREQ = 16;//uint32_t
|
||||
static const uint16_t EEPROM_ADDR_VFO_B_FREQ = 20;//uint32_t
|
||||
static const uint16_t EEPROM_ADDR_CW_SIDETONE = 24;//uint32_t
|
||||
static const uint16_t EEPROM_ADDR_CW_DIT_TIME = 28;//uint32_t
|
||||
static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_X = 32;//int16_t
|
||||
static const uint16_t EEPROM_ADDR_TOUCH_SLOPE_Y = 36;//int16_t
|
||||
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_X = 40;//int16_t
|
||||
static const uint16_t EEPROM_ADDR_TOUCH_OFFSET_Y = 44;//int16_t
|
||||
static const uint16_t EEPROM_ADDR_MORSE_MENU = 46;//uint8_t
|
||||
static const uint16_t EEPROM_ADDR_CW_DELAYTIME = 48;//uint16_t
|
||||
static const uint16_t EEPROM_ADDR_VFO_A_MODE = 256;//uint8_t
|
||||
static const uint16_t EEPROM_ADDR_VFO_B_MODE = 257;//uint8_t
|
||||
static const uint16_t EEPROM_ADDR_CW_KEY_TYPE = 358;//uint8_t
|
||||
static const uint16_t EEPROM_ADDR_QUICKLIST_FREQ = 630;//uint32_t array of size NUM_QUICKLIST_SETTINGS
|
||||
static const uint16_t EEPROM_ADDR_QUICKLIST_MODE = 710;//uint8_t array of size NUM_QUICKLIST_SETTINGS
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool LoadSane(T& dest,uint16_t addr, T min, T max)
|
||||
{
|
||||
T read_value;
|
||||
EEPROM.get(addr,read_value);
|
||||
if((min <= read_value) && (read_value <= max)){
|
||||
dest = read_value;
|
||||
//Serial.print(addr);
|
||||
//Serial.print(F(":"));
|
||||
//Serial.println(dest);
|
||||
return true;
|
||||
}
|
||||
//Serial.print(addr);
|
||||
//Serial.print(F(": Not valid: "));
|
||||
//Serial.print(read_value);
|
||||
//Serial.print(F(" Leaving value at "));
|
||||
//Serial.println(dest);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This is the non-extern version that actually gets built
|
||||
SettingsRam globalSettings;
|
||||
|
||||
void LoadDefaultSettings()
|
||||
{
|
||||
memset(&globalSettings,0x00,sizeof(globalSettings));
|
||||
|
||||
globalSettings.oscillatorCal = 0L;
|
||||
globalSettings.usbCarrierFreq = 11052000UL;
|
||||
|
||||
globalSettings.activeVfo = Vfo_e::VFO_A;
|
||||
globalSettings.vfoA.frequency = 7150000UL;
|
||||
globalSettings.vfoA.mode = VFO_MODE_LSB;
|
||||
globalSettings.vfoB.frequency = 14150000UL;
|
||||
globalSettings.vfoB.mode = VFO_MODE_USB;
|
||||
|
||||
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
|
||||
globalSettings.quickList[i].frequency = i*1000000;
|
||||
globalSettings.quickList[i].mode = VfoMode_e::VFO_MODE_LSB;
|
||||
}
|
||||
|
||||
globalSettings.keyerMode = KEYER_STRAIGHT;
|
||||
globalSettings.cwSideToneFreq = 800;
|
||||
globalSettings.cwDitDurationMs = 100;
|
||||
globalSettings.cwActiveTimeoutMs = 50;
|
||||
|
||||
globalSettings.touchSlopeX = 104;
|
||||
globalSettings.touchSlopeY = 137;
|
||||
globalSettings.touchOffsetX = 28;
|
||||
globalSettings.touchOffsetY = 29;
|
||||
|
||||
globalSettings.ritOn = false;
|
||||
globalSettings.ritFrequency = globalSettings.vfoA.frequency;
|
||||
|
||||
globalSettings.tuningMode = TuningMode_e::TUNE_SSB;
|
||||
|
||||
globalSettings.splitOn = false;
|
||||
|
||||
globalSettings.txActive = false;
|
||||
globalSettings.txCatActive = false;
|
||||
globalSettings.cwExpirationTimeMs = 0;
|
||||
globalSettings.morseMenuOn = false;
|
||||
}
|
||||
|
||||
void LoadSettingsFromEeprom()
|
||||
{
|
||||
LoadSane(globalSettings.usbCarrierFreq,EEPROM_ADDR_USB_CAL,11048000UL,11060000UL);
|
||||
LoadSane(globalSettings.vfoA.frequency,EEPROM_ADDR_VFO_A_FREQ,SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
|
||||
LoadSane(globalSettings.vfoB.frequency,EEPROM_ADDR_VFO_B_FREQ,SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
|
||||
LoadSane(globalSettings.cwSideToneFreq,EEPROM_ADDR_CW_SIDETONE,100UL,2000UL);
|
||||
LoadSane(globalSettings.cwDitDurationMs,EEPROM_ADDR_CW_DIT_TIME,10U,1000U);
|
||||
if(LoadSane(globalSettings.cwActiveTimeoutMs,EEPROM_ADDR_CW_DELAYTIME,10U,100U)){
|
||||
globalSettings.cwActiveTimeoutMs *= 10;//scale by 10 for legacy reasons
|
||||
}
|
||||
LoadSane(globalSettings.vfoA.mode,EEPROM_ADDR_VFO_A_MODE,VFO_MODE_LSB,VFO_MODE_USB);
|
||||
LoadSane(globalSettings.vfoB.mode,EEPROM_ADDR_VFO_B_MODE,VFO_MODE_LSB,VFO_MODE_USB);
|
||||
LoadSane(globalSettings.keyerMode,EEPROM_ADDR_CW_KEY_TYPE,KEYER_STRAIGHT,KEYER_IAMBIC_B);
|
||||
|
||||
uint8_t morse_on = 0;
|
||||
LoadSane(morse_on,EEPROM_ADDR_MORSE_MENU,(uint8_t)0,(uint8_t)1);
|
||||
globalSettings.morseMenuOn = morse_on;
|
||||
|
||||
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
|
||||
LoadSane(globalSettings.quickList[i].frequency,EEPROM_ADDR_QUICKLIST_FREQ+(sizeof(uint32_t)*i),SI5351_MIN_FREQUENCY_HZ,SI5351_MAX_FREQUENCY_HZ);
|
||||
LoadSane(globalSettings.quickList[i].mode,EEPROM_ADDR_QUICKLIST_MODE+(sizeof(uint8_t)*i),VFO_MODE_LSB,VFO_MODE_USB);
|
||||
}
|
||||
|
||||
//No sanity check on these - cal your heart out
|
||||
EEPROM.get(EEPROM_ADDR_MASTER_CAL,globalSettings.oscillatorCal);
|
||||
EEPROM.get(EEPROM_ADDR_TOUCH_SLOPE_X,globalSettings.touchSlopeX);
|
||||
EEPROM.get(EEPROM_ADDR_TOUCH_SLOPE_Y,globalSettings.touchSlopeY);
|
||||
EEPROM.get(EEPROM_ADDR_TOUCH_OFFSET_X,globalSettings.touchOffsetX);
|
||||
EEPROM.get(EEPROM_ADDR_TOUCH_OFFSET_Y,globalSettings.touchOffsetY);
|
||||
}
|
||||
|
||||
void SaveSettingsToEeprom()
|
||||
{
|
||||
//Serial.println(F("Saving..."));
|
||||
EEPROM.put(EEPROM_ADDR_MASTER_CAL,globalSettings.oscillatorCal);
|
||||
EEPROM.put(EEPROM_ADDR_USB_CAL,globalSettings.usbCarrierFreq);
|
||||
EEPROM.put(EEPROM_ADDR_VFO_A_FREQ,globalSettings.vfoA.frequency);
|
||||
EEPROM.put(EEPROM_ADDR_VFO_B_FREQ,globalSettings.vfoB.frequency);
|
||||
EEPROM.put(EEPROM_ADDR_CW_SIDETONE,globalSettings.cwSideToneFreq);
|
||||
EEPROM.put(EEPROM_ADDR_CW_DIT_TIME,globalSettings.cwDitDurationMs);
|
||||
EEPROM.put(EEPROM_ADDR_TOUCH_SLOPE_X,globalSettings.touchSlopeX);
|
||||
EEPROM.put(EEPROM_ADDR_TOUCH_SLOPE_Y,globalSettings.touchSlopeY);
|
||||
EEPROM.put(EEPROM_ADDR_TOUCH_OFFSET_X,globalSettings.touchOffsetX);
|
||||
EEPROM.put(EEPROM_ADDR_TOUCH_OFFSET_Y,globalSettings.touchOffsetY);
|
||||
EEPROM.put(EEPROM_ADDR_CW_DELAYTIME,globalSettings.cwActiveTimeoutMs/10);//scale by 10 for legacy reasons
|
||||
EEPROM.put(EEPROM_ADDR_VFO_A_MODE,globalSettings.vfoA.mode);
|
||||
EEPROM.put(EEPROM_ADDR_VFO_B_MODE,globalSettings.vfoB.mode);
|
||||
EEPROM.put(EEPROM_ADDR_CW_KEY_TYPE,globalSettings.keyerMode);
|
||||
EEPROM.put(EEPROM_ADDR_MORSE_MENU,(uint8_t)globalSettings.morseMenuOn);
|
||||
|
||||
for(uint8_t i = 0; i < NUM_QUICKLIST_SETTINGS; ++i){
|
||||
EEPROM.put(EEPROM_ADDR_QUICKLIST_FREQ+(sizeof(uint32_t)*i),globalSettings.quickList[i].frequency);
|
||||
EEPROM.put(EEPROM_ADDR_QUICKLIST_MODE+(sizeof(uint8_t)*i),globalSettings.quickList[i].mode);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetActiveVfoFreq()
|
||||
{
|
||||
if(VFO_A == globalSettings.activeVfo){
|
||||
return globalSettings.vfoA.frequency;
|
||||
}
|
||||
else{
|
||||
return globalSettings.vfoB.frequency;
|
||||
}
|
||||
}
|
||||
|
||||
void SetActiveVfoFreq(uint32_t frequency)
|
||||
{
|
||||
if(VFO_A == globalSettings.activeVfo)
|
||||
{
|
||||
globalSettings.vfoA.frequency = frequency;
|
||||
}
|
||||
else{
|
||||
globalSettings.vfoB.frequency = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
VfoMode_e GetActiveVfoMode()
|
||||
{
|
||||
if(VFO_A == globalSettings.activeVfo){
|
||||
return globalSettings.vfoA.mode;
|
||||
}
|
||||
else{
|
||||
return globalSettings.vfoB.mode;
|
||||
}
|
||||
}
|
||||
|
||||
void SetActiveVfoMode(VfoMode_e mode)
|
||||
{
|
||||
if(VFO_A == globalSettings.activeVfo)
|
||||
{
|
||||
globalSettings.vfoA.mode = mode;
|
||||
}
|
||||
else{
|
||||
globalSettings.vfoB.mode = mode;
|
||||
}
|
||||
}
|
116
settings.h
Normal file
116
settings.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This class deals with all of the radio settings,
|
||||
* so that other areas of the code doesn't have to
|
||||
*
|
||||
* Example usage:
|
||||
* LoadSettingsFromEeprom();
|
||||
* Serial.println(globalSettings.vfoAFreq);
|
||||
* globalSettings.vfoAFreq = 12345678;
|
||||
* SaveSettingsToEeprom();
|
||||
* Serial.println(globalSettings.vfoAFreq);
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>//uint8_t etc.
|
||||
|
||||
static const uint8_t NUM_QUICKLIST_SETTINGS = 4;
|
||||
|
||||
/*
|
||||
* Loads default values for all settings
|
||||
*/
|
||||
void LoadDefaultSettings();
|
||||
|
||||
/*
|
||||
* Loads all persistent settings from the EEPROM
|
||||
*/
|
||||
void LoadSettingsFromEeprom();
|
||||
|
||||
/*
|
||||
* Saves all persistent settings to the EEPROM
|
||||
*
|
||||
* It's a little CPU-cycle-wasteful to save EVERYTHING
|
||||
* each time, but keeps things simple
|
||||
*/
|
||||
void SaveSettingsToEeprom();
|
||||
|
||||
/*
|
||||
* These are all of the settings
|
||||
* Note that not all settings are saved to the EEPROM
|
||||
*/
|
||||
enum Vfo_e : uint8_t
|
||||
{
|
||||
VFO_A,
|
||||
VFO_B
|
||||
};
|
||||
|
||||
enum VfoMode_e : uint8_t
|
||||
{
|
||||
VFO_MODE_LSB = 2,
|
||||
VFO_MODE_USB = 3
|
||||
};
|
||||
|
||||
struct VfoSettings_t
|
||||
{
|
||||
uint32_t frequency;
|
||||
VfoMode_e mode;
|
||||
};
|
||||
|
||||
enum TuningMode_e : uint8_t
|
||||
{
|
||||
TUNE_SSB,
|
||||
TUNE_CW
|
||||
};
|
||||
|
||||
enum KeyerMode_e : uint8_t
|
||||
{
|
||||
KEYER_STRAIGHT,
|
||||
KEYER_IAMBIC_A,
|
||||
KEYER_IAMBIC_B
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the definition of the settings/state variables
|
||||
*/
|
||||
struct SettingsRam
|
||||
{
|
||||
uint32_t oscillatorCal;
|
||||
uint32_t usbCarrierFreq;
|
||||
|
||||
Vfo_e activeVfo;
|
||||
VfoSettings_t vfoA;
|
||||
VfoSettings_t vfoB;
|
||||
|
||||
VfoSettings_t quickList[4];
|
||||
|
||||
KeyerMode_e keyerMode;
|
||||
uint32_t cwSideToneFreq;
|
||||
uint16_t cwDitDurationMs;
|
||||
uint16_t cwActiveTimeoutMs;
|
||||
|
||||
int16_t touchSlopeX;
|
||||
int16_t touchSlopeY;
|
||||
int16_t touchOffsetX;
|
||||
int16_t touchOffsetY;
|
||||
|
||||
bool ritOn;
|
||||
uint32_t ritFrequency;
|
||||
|
||||
TuningMode_e tuningMode;
|
||||
|
||||
bool splitOn;
|
||||
|
||||
bool txActive;
|
||||
bool txCatActive;
|
||||
uint32_t cwExpirationTimeMs;
|
||||
bool morseMenuOn;
|
||||
};
|
||||
|
||||
//This is the shared declaration
|
||||
extern SettingsRam globalSettings;
|
||||
|
||||
//Some convenience functions
|
||||
uint32_t GetActiveVfoFreq();
|
||||
void SetActiveVfoFreq(uint32_t frequency);
|
||||
VfoMode_e GetActiveVfoMode();
|
||||
void SetActiveVfoMode(VfoMode_e mode);
|
9
setup.h
Normal file
9
setup.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
extern Menu_t* const setupMenu;
|
||||
|
||||
void setupTouch();
|
||||
void runLocalOscSetting();
|
||||
void runBfoSetting();
|
@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "ubitx.h"
|
||||
#include "settings.h"
|
||||
#include "si5351.h"
|
||||
|
||||
// ************* SI5315 routines - tks Jerry Gaffke, KE7ER ***********************
|
||||
|
||||
@ -48,7 +49,6 @@ uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate
|
||||
uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv)
|
||||
uint8_t si5351bx_drive[3] = {3, 3, 3}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2
|
||||
uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off
|
||||
int32_t calibration = 0;
|
||||
|
||||
void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c
|
||||
Wire.beginTransmission(SI5351BX_ADDR);
|
||||
@ -66,12 +66,11 @@ void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array
|
||||
|
||||
|
||||
void si5351bx_init() { // Call once at power-up, start PLLA
|
||||
uint8_t reg; uint32_t msxp1;
|
||||
Wire.begin();
|
||||
i2cWrite(149, 0); // SpreadSpectrum off
|
||||
i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers
|
||||
i2cWrite(183, SI5351BX_XTALPF << 6); // Set 25mhz crystal load capacitance
|
||||
msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional
|
||||
uint32_t msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional
|
||||
uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0};
|
||||
i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs
|
||||
i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB)
|
||||
@ -86,7 +85,7 @@ void si5351bx_init() { // Call once at power-up, start PLLA
|
||||
|
||||
void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz
|
||||
uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top;
|
||||
if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range
|
||||
if ((fout < SI5351_MIN_FREQUENCY_HZ) || (fout > SI5351_MAX_FREQUENCY_HZ)) // If clock freq out of range
|
||||
si5351bx_clken |= 1 << clknum; // shut down the clock
|
||||
else {
|
||||
msa = si5351bx_vcoa / fout; // Integer part of vco/fout
|
||||
@ -115,14 +114,13 @@ void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz
|
||||
|
||||
void si5351_set_calibration(int32_t cal){
|
||||
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + cal; // apply the calibration correction factor
|
||||
si5351bx_setfreq(0, usbCarrier);
|
||||
si5351bx_setfreq(0, globalSettings.usbCarrierFreq);
|
||||
}
|
||||
|
||||
void initOscillators(){
|
||||
//initialize the SI5351
|
||||
si5351bx_init();
|
||||
si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + calibration; // apply the calibration correction factor
|
||||
si5351bx_setfreq(0, usbCarrier);
|
||||
si5351_set_calibration(globalSettings.oscillatorCal);
|
||||
}
|
||||
|
||||
|
10
si5351.h
Normal file
10
si5351.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint32_t SI5351_MIN_FREQUENCY_HZ = 500000;
|
||||
static const uint32_t SI5351_MAX_FREQUENCY_HZ = 109000000;
|
||||
|
||||
void initOscillators();
|
||||
void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
|
||||
void si5351_set_calibration(int32_t cal); //calibration is a small value that is nudged to make up for the inaccuracies of the reference 25 MHz crystal frequency
|
27
toneAC2/examples/toneAC2_demo/toneAC2_demo.pde
Normal file
27
toneAC2/examples/toneAC2_demo/toneAC2_demo.pde
Normal file
@ -0,0 +1,27 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Be sure to include an in-line 100 ohm resistor on one pin as you normally do when connecting a piezo or speaker.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include <toneAC2.h>
|
||||
|
||||
// Melody liberated from the toneMelody Arduino example sketch by Tom Igoe.
|
||||
int melody[] = { 262, 196, 196, 220, 196, 0, 247, 262 };
|
||||
int noteDurations[] = { 4, 8, 8, 4, 4, 4, 4, 4 };
|
||||
|
||||
void setup() {} // Nothing to setup, just start playing!
|
||||
|
||||
void loop() {
|
||||
for (unsigned long freq = 125; freq <= 15000; freq += 10) {
|
||||
toneAC2(2, 3, freq, 1); // Play the frequency (125 Hz to 15 kHz sweep in 10 Hz steps) for 1ms.
|
||||
}
|
||||
|
||||
delay(1000); // Wait a second.
|
||||
|
||||
for (int thisNote = 0; thisNote < 8; thisNote++) {
|
||||
int noteDuration = 1000/noteDurations[thisNote];
|
||||
toneAC2(2, 3, melody[thisNote], noteDuration, true); // Play thisNote at full volume for noteDuration in the background.
|
||||
delay(noteDuration * 4 / 3); // Wait while the tone plays in the background, plus another 33% delay between notes.
|
||||
}
|
||||
|
||||
while(1); // Stop (so it doesn't repeat forever driving you crazy--you're welcome).
|
||||
}
|
18
toneAC2/keywords.txt
Normal file
18
toneAC2/keywords.txt
Normal file
@ -0,0 +1,18 @@
|
||||
###################################
|
||||
# Syntax Coloring Map For toneAC2
|
||||
###################################
|
||||
|
||||
###################################
|
||||
# Datatypes (KEYWORD1)
|
||||
###################################
|
||||
|
||||
###################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
###################################
|
||||
|
||||
toneAC2 KEYWORD2
|
||||
noToneAC2 KEYWORD2
|
||||
|
||||
###################################
|
||||
# Constants (LITERAL1)
|
||||
###################################
|
56
toneAC2/toneAC2.cpp
Normal file
56
toneAC2/toneAC2.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Created by Tim Eckel - teckel@leethost.com
|
||||
// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
//
|
||||
// See "toneAC2.h" for purpose, syntax, version history, links, and more.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include "toneAC2.h"
|
||||
|
||||
unsigned long _tAC2_time; // Used to track end note with timer when playing note in the background.
|
||||
volatile uint8_t *_pinMode1; // Pin modes.
|
||||
uint8_t _pinMask1 = 0; // Bitmask for pins.
|
||||
volatile uint8_t *_pinOutput1; // Output port registers for each pin.
|
||||
int _tAC2_prescale[] = { 2, 16, 64, 128, 256, 512, 2048 }; // Prescaler.
|
||||
|
||||
void toneAC2(uint8_t pin1, unsigned int frequency, unsigned long length, uint8_t background) {
|
||||
long top;
|
||||
uint8_t prescaler;
|
||||
|
||||
for (prescaler = 1; prescaler < 8; prescaler++) { // Find the appropriate prescaler
|
||||
top = F_CPU / (long) frequency / (long) _tAC2_prescale[prescaler - 1] - 1; // Calculate the top.
|
||||
if (top < 256) break; // Fits, break out of for loop.
|
||||
}
|
||||
if (top > 255) { noToneAC2(); return; } // Frequency is out of range, turn off sound and return.
|
||||
|
||||
if (length > 0) _tAC2_time = millis() + length - 1; else _tAC2_time = 0xFFFFFFFF; // Set when the note should end, or play "forever".
|
||||
|
||||
if (_pinMask1 == 0) { // This gets the port registers and bitmaps for the two pins and sets the pins to output mode.
|
||||
_pinMask1 = digitalPinToBitMask(pin1); // Get the port register bitmask for pin 1.
|
||||
_pinOutput1 = portOutputRegister(digitalPinToPort(pin1)); // Get the output port register for pin 1.
|
||||
_pinMode1 = (uint8_t *) portModeRegister(digitalPinToPort(pin1)); // Get the port mode register for pin 1.
|
||||
*_pinMode1 |= _pinMask1; // Set pin 1 to Output mode.
|
||||
}
|
||||
|
||||
OCR2A = top; // Set the top.
|
||||
if (TCNT2 > top) TCNT2 = top; // Counter over the top, put within range.
|
||||
TCCR2B = _BV(WGM22) | prescaler; // Set Fast PWM and prescaler.
|
||||
TCCR2A = _BV(WGM20) | _BV(WGM21); // Fast PWM and normal port operation, OC2A/OC2B disconnected.
|
||||
TIMSK2 &= ~_BV(OCIE2A); // Stop timer 2 interrupt while we set the pin states.
|
||||
TIMSK2 |= _BV(OCIE2A); // Activate the timer interrupt.
|
||||
|
||||
if (length > 0 && !background) { delay(length); noToneAC2(); } // Just a simple delay, doesn't return control till finished.
|
||||
}
|
||||
|
||||
void noToneAC2() {
|
||||
TIMSK2 &= ~_BV(OCIE2A); // Remove the timer interrupt.
|
||||
TCCR2B = _BV(CS22); // Default clock prescaler of 64.
|
||||
TCCR2A = _BV(WGM20); // Set to defaults so PWM can work like normal (PWM, phase corrected, 8bit).
|
||||
*_pinMode1 &= ~_pinMask1; // Set pin 1 to INPUT.
|
||||
_pinMask1 = 0; // Flag so we know note is no longer playing.
|
||||
}
|
||||
|
||||
ISR(TIMER2_COMPA_vect) { // Timer interrupt vector.
|
||||
if (millis() > _tAC2_time) noToneAC2(); // Check to see if it's time for the note to end.
|
||||
*_pinOutput1 ^= _pinMask1; // Toggle the pin 1 state.
|
||||
}
|
73
toneAC2/toneAC2.h
Normal file
73
toneAC2/toneAC2.h
Normal file
@ -0,0 +1,73 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// toneAC2 Library - v1.1 - 09/15/2015
|
||||
//
|
||||
// AUTHOR/LICENSE:
|
||||
// Created by Tim Eckel - teckel@leethost.com
|
||||
// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
//
|
||||
// LINKS:
|
||||
// Project home: https://bitbucket.org/teckel12/arduino-toneac/wiki/Home
|
||||
// Blog: http://forum.arduino.cc/index.php?topic=142097.0
|
||||
//
|
||||
// DISCLAIMER:
|
||||
// This software is furnished "as is", without technical support, and with no
|
||||
// warranty, express or implied, as to its usefulness for any purpose.
|
||||
//
|
||||
// PURPOSE:
|
||||
// Replacement to the standard tone library with the advantage of nearly twice
|
||||
// the volume, 800 bytes smaller compiled code size, and less stress on the
|
||||
// speaker. This alternate version uses timer 2 and allows for flexible pin
|
||||
// assignment. The primary version (toneAC) allows for higher frequencies,
|
||||
// higher quality, and even smaller code size. However, toneAC is fixed to
|
||||
// using the PWM timer 1 pins unlike toneAC2 which can use any two pins. Both
|
||||
// exclusively use port registers for the fast and smallest code possible.
|
||||
//
|
||||
// USAGE:
|
||||
// Connection is very similar to a piezo or standard speaker. Except, instead
|
||||
// of connecting one speaker wire to ground you connect both speaker wires to
|
||||
// Arduino pins. Unlike toneAC, with toneAC2 you can connect to any two pins.
|
||||
// Just as usual when connecting a speaker, make sure you add an in-line 100
|
||||
// ohm resistor between one of the pins and the speaker wire.
|
||||
//
|
||||
// SYNTAX:
|
||||
// toneAC2( pin1, pin2, frequency [, length [, background ]] ) - Play a note.
|
||||
// Parameters:
|
||||
// * pin1 - Pin to attach one of the speaker wires.
|
||||
// * pin2 - Pin to attach the other speaker wire.
|
||||
// * frequency - Play the specified frequency indefinitely, turn off with noToneAC2().
|
||||
// * length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1)
|
||||
// * background - [optional] Play note in background or pause till finished? (default: false, values: true/false)
|
||||
// noToneAC2() - Stop playing.
|
||||
//
|
||||
// HISTORY:
|
||||
// 09/15/2015 v1.1 - Fix a potential race condition with _tAC2_time. Moved
|
||||
// development to Bitbucket.
|
||||
//
|
||||
// 01/27/2013 v1.0 - Initial release.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifndef toneAC2_h
|
||||
#define toneAC2_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
// This doesn't currently work. Would require more work than simply doing this.
|
||||
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
|
||||
#define TCCR2A TCCR2
|
||||
#define TCCR2B TCCR2
|
||||
#define TIMSK2 TIMSK
|
||||
#define COM2A1 COM21
|
||||
#define COM2A0 COM20
|
||||
#define OCIE2A OCIE2
|
||||
#define OCR2A OCR2
|
||||
#define TIMER2_COMPA_vect TIMER2_COMP_vect
|
||||
#endif
|
||||
|
||||
void toneAC2(uint8_t pin1, unsigned int frequency = 0, unsigned long length = 0, uint8_t background = false);
|
||||
void noToneAC2();
|
||||
#endif
|
1
toneAC2Proxy.cpp
Normal file
1
toneAC2Proxy.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "toneAC2/toneAC2.cpp"
|
139
touch.cpp
Normal file
139
touch.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
#include "touch.h"
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include "pin_definitions.h"
|
||||
#include "settings.h"
|
||||
|
||||
constexpr int16_t Z_THRESHOLD = 400;
|
||||
constexpr uint8_t MSEC_THRESHOLD = 3;//Max sample rate is 125kHz, but we'll limit ourselves conservatively
|
||||
|
||||
constexpr uint8_t START_COMMAND = 1 << 7;
|
||||
constexpr uint8_t CHANNEL_Y = 1 << 4;
|
||||
constexpr uint8_t CHANNEL_Z1 = 3 << 4;
|
||||
constexpr uint8_t CHANNEL_Z2 = 4 << 4;
|
||||
constexpr uint8_t CHANNEL_X = 5 << 4;
|
||||
constexpr uint8_t CHANNEL_TEMPERATURE = 7 << 4;
|
||||
constexpr uint8_t USE_8_INSTEAD_OF_12_BIT = 1 << 3;
|
||||
constexpr uint8_t USE_SINGLE_ENDED_MEASUREMENT = 1 << 2;
|
||||
constexpr uint8_t POWER_OFF = 0 << 0;
|
||||
constexpr uint8_t POWER_ADC = 1 << 0;
|
||||
constexpr uint8_t POWER_REF = 2 << 0;
|
||||
constexpr uint8_t POWER_ADC_REF = 3 << 0;
|
||||
|
||||
constexpr uint16_t MEASURE_X = START_COMMAND | POWER_ADC | CHANNEL_Y;//X and Y channel labelling flip due to screen orientation
|
||||
constexpr uint16_t MEASURE_Y = START_COMMAND | POWER_ADC | CHANNEL_X;//X and Y channel labelling flip due to screen orientation
|
||||
constexpr uint16_t MEASURE_Z1 = START_COMMAND | POWER_ADC | CHANNEL_Z1;
|
||||
constexpr uint16_t MEASURE_Z2 = START_COMMAND | POWER_ADC | CHANNEL_Z2;
|
||||
|
||||
constexpr uint8_t RAW_READ_TO_12BIT_VALUE_SHIFT = 3;//16 bits read, zero-padded, but the MSB of the 16 is where the "BUSY" signal is asserted, so only need to shift by 3 instead of 4
|
||||
|
||||
|
||||
uint32_t msraw=0x80000000;
|
||||
int16_t xraw=0, yraw=0, zraw=0;
|
||||
constexpr uint8_t rotation = 1;
|
||||
|
||||
SPISettings spiSettingsTouch(2000000,MSBFIRST,SPI_MODE0);
|
||||
|
||||
int16_t touch_besttwoavg( int16_t x , int16_t y , int16_t z ) {
|
||||
int16_t da, db, dc;
|
||||
int16_t reta = 0;
|
||||
if ( x > y ) da = x - y; else da = y - x;
|
||||
if ( x > z ) db = x - z; else db = z - x;
|
||||
if ( z > y ) dc = z - y; else dc = y - z;
|
||||
|
||||
if ( da <= db && da <= dc ) reta = (x + y) >> 1;
|
||||
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
|
||||
else reta = (y + z) >> 1; // else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;
|
||||
|
||||
return (reta);
|
||||
}
|
||||
|
||||
uint16_t touchReadChannel(uint8_t channel_command){
|
||||
//We assume that SPI.beginTransaction has already been called, and CS is LOW
|
||||
SPI.transfer(channel_command);//Throw away any bytes here
|
||||
const uint16_t tmpH = SPI.transfer(0) & 0x7F;//Leading 0 (during "busy" signal), followed by bits 11-5
|
||||
const uint16_t tmpL = SPI.transfer(0);//Bits 4-0, followed by 0s
|
||||
return tmpH << 5 | tmpL >> 3;
|
||||
}
|
||||
|
||||
void touch_update(){
|
||||
uint32_t now = millis();
|
||||
if (now - msraw < MSEC_THRESHOLD){
|
||||
return;
|
||||
}
|
||||
|
||||
SPI.beginTransaction(spiSettingsTouch);
|
||||
digitalWrite(PIN_TOUCH_CS, LOW);
|
||||
|
||||
int16_t z1 = touchReadChannel(MEASURE_Z1);//~0 when not pressed, increases with pressure
|
||||
int32_t z = z1;
|
||||
int16_t z2 = touchReadChannel(MEASURE_Z2);//~4095 when not pressed, decreases with pressure
|
||||
z += (4095 - z2);
|
||||
//Serial.print(F("z1:"));Serial.print(z1);Serial.print(F(" z2:"));Serial.print(z2);Serial.print(F(" z:"));Serial.println(z);
|
||||
|
||||
zraw = z;
|
||||
if (zraw < Z_THRESHOLD) {//Don't bother reading x/y if we're not being touched
|
||||
digitalWrite(PIN_TOUCH_CS, HIGH);
|
||||
SPI.endTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
// make 3 x-y measurements
|
||||
int16_t data[6];
|
||||
data[0] = touchReadChannel(MEASURE_X);
|
||||
data[1] = touchReadChannel(MEASURE_Y);
|
||||
data[2] = touchReadChannel(MEASURE_X);
|
||||
data[3] = touchReadChannel(MEASURE_Y);
|
||||
data[4] = touchReadChannel(MEASURE_X);
|
||||
data[5] = touchReadChannel(MEASURE_Y & ~POWER_ADC_REF);//Turn off sensor
|
||||
|
||||
digitalWrite(PIN_TOUCH_CS, HIGH);
|
||||
SPI.endTransaction();
|
||||
|
||||
int16_t x = touch_besttwoavg( data[0], data[2], data[4] );
|
||||
int16_t y = touch_besttwoavg( data[1], data[3], data[5] );
|
||||
|
||||
msraw = now; // good read completed, set wait
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
xraw = 4095 - y;
|
||||
yraw = x;
|
||||
break;
|
||||
case 1:
|
||||
xraw = x;
|
||||
yraw = y;
|
||||
break;
|
||||
case 2:
|
||||
xraw = y;
|
||||
yraw = 4095 - x;
|
||||
break;
|
||||
default: // 3
|
||||
xraw = 4095 - x;
|
||||
yraw = 4095 - y;
|
||||
}
|
||||
}
|
||||
|
||||
void initTouch(){
|
||||
pinMode(PIN_TOUCH_CS, OUTPUT);
|
||||
digitalWrite(PIN_TOUCH_CS, HIGH);
|
||||
}
|
||||
|
||||
bool readTouch(Point *const touch_point_out){
|
||||
touch_update();
|
||||
//Serial.print(F("readTouch found zraw of "));Serial.println(zraw);
|
||||
if (zraw >= Z_THRESHOLD) {
|
||||
touch_point_out->x = xraw;
|
||||
touch_point_out->y = yraw;
|
||||
//Serial.print(ts_point.x); Serial.print(",");Serial.println(ts_point.y);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void scaleTouch(Point *const touch_point_in_out){
|
||||
touch_point_in_out->x = ((long)(touch_point_in_out->x - globalSettings.touchOffsetX) * 10L)/ (long)globalSettings.touchSlopeX;
|
||||
touch_point_in_out->y = ((long)(touch_point_in_out->y - globalSettings.touchOffsetY) * 10L)/ (long)globalSettings.touchSlopeY;
|
||||
|
||||
//Serial.print(p->x); Serial.print(",");Serial.println(p->y);
|
||||
}
|
11
touch.h
Normal file
11
touch.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "point.h"
|
||||
|
||||
void initTouch();
|
||||
|
||||
//Returns true if touched, false otherwise
|
||||
bool readTouch(Point *const touch_point_out);
|
||||
|
||||
//Applies the touch calibration the point passed in
|
||||
void scaleTouch(Point *const touch_point_in_out);
|
217
tuner.cpp
Normal file
217
tuner.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
#include "tuner.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "nano_gui.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "si5351.h"
|
||||
|
||||
static const uint32_t THRESHOLD_USB_LSB = 10000000L;
|
||||
|
||||
void saveVFOs()
|
||||
{
|
||||
SaveSettingsToEeprom();
|
||||
}
|
||||
|
||||
|
||||
void switchVFO(Vfo_e new_vfo){
|
||||
ritDisable();//If we are in RIT mode, we need to disable it before setting the active VFO so that the correct VFO gets it's frequency restored
|
||||
|
||||
globalSettings.activeVfo = new_vfo;
|
||||
setFrequency(GetActiveVfoFreq());
|
||||
saveVFOs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the properly tx harmonic filters
|
||||
* The four harmonic filters use only three relays
|
||||
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
|
||||
* Briefly, it works like this,
|
||||
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
|
||||
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
|
||||
* the KT1 is on for the three other cases.
|
||||
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
|
||||
* to 18 MHz LPF (That also works for 14 Mhz)
|
||||
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
|
||||
* - KT3, when switched on selects the 7-10 Mhz filter
|
||||
* - KT3 when switched off selects the 3.5-5 Mhz filter
|
||||
* See the circuit to understand this
|
||||
*/
|
||||
|
||||
void setTXFilters(unsigned long freq){
|
||||
|
||||
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
|
||||
digitalWrite(PIN_TX_LPF_A, 0);
|
||||
digitalWrite(PIN_TX_LPF_B, 0);
|
||||
digitalWrite(PIN_TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
|
||||
digitalWrite(PIN_TX_LPF_A, 1);
|
||||
digitalWrite(PIN_TX_LPF_B, 0);
|
||||
digitalWrite(PIN_TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq > 7000000L){
|
||||
digitalWrite(PIN_TX_LPF_A, 0);
|
||||
digitalWrite(PIN_TX_LPF_B, 1);
|
||||
digitalWrite(PIN_TX_LPF_C, 0);
|
||||
}
|
||||
else {
|
||||
digitalWrite(PIN_TX_LPF_A, 0);
|
||||
digitalWrite(PIN_TX_LPF_B, 0);
|
||||
digitalWrite(PIN_TX_LPF_C, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the most frequently called function that configures the
|
||||
* radio to a particular frequeny, sideband and sets up the transmit filters
|
||||
*
|
||||
* The transmit filter relays are powered up only during the tx so they dont
|
||||
* draw any current during rx.
|
||||
*
|
||||
* The carrier oscillator of the detector/modulator is permanently fixed at
|
||||
* uppper sideband. The sideband selection is done by placing the second oscillator
|
||||
* either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands
|
||||
* through mixing of the second local oscillator.
|
||||
*/
|
||||
|
||||
void setFrequency(const unsigned long freq,
|
||||
const bool transmit){
|
||||
static const unsigned long FIRST_IF = 45005000UL;
|
||||
|
||||
setTXFilters(freq);
|
||||
|
||||
//Nominal values for the oscillators
|
||||
uint32_t local_osc_freq = FIRST_IF + freq;
|
||||
uint32_t ssb_osc_freq = FIRST_IF;//will be changed depending on sideband
|
||||
uint32_t bfo_osc_freq = globalSettings.usbCarrierFreq;
|
||||
|
||||
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
|
||||
if(transmit){
|
||||
//We don't do any mixing or converting when transmitting
|
||||
local_osc_freq = freq;
|
||||
ssb_osc_freq = 0;
|
||||
bfo_osc_freq = 0;
|
||||
}
|
||||
else{
|
||||
//We offset when receiving CW so that it's audible
|
||||
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
|
||||
local_osc_freq -= globalSettings.cwSideToneFreq;
|
||||
ssb_osc_freq += globalSettings.usbCarrierFreq;
|
||||
}
|
||||
else{
|
||||
local_osc_freq += globalSettings.cwSideToneFreq;
|
||||
ssb_osc_freq -= globalSettings.usbCarrierFreq;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{//SSB mode
|
||||
if(VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
|
||||
ssb_osc_freq += globalSettings.usbCarrierFreq;
|
||||
}
|
||||
else{
|
||||
ssb_osc_freq -= globalSettings.usbCarrierFreq;
|
||||
}
|
||||
}
|
||||
|
||||
si5351bx_setfreq(2, local_osc_freq);
|
||||
si5351bx_setfreq(1, ssb_osc_freq);
|
||||
si5351bx_setfreq(0, bfo_osc_freq);
|
||||
|
||||
SetActiveVfoFreq(freq);
|
||||
}
|
||||
|
||||
/**
|
||||
* startTx is called by the PTT, cw keyer and CAT protocol to
|
||||
* put the uBitx in tx mode. It takes care of rit settings, sideband settings
|
||||
* Note: In cw mode, doesnt key the radio, only puts it in tx mode
|
||||
* CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode
|
||||
*/
|
||||
|
||||
void startTx(TuningMode_e tx_mode){
|
||||
globalSettings.tuningMode = tx_mode;
|
||||
|
||||
if (globalSettings.ritOn){
|
||||
//save the current as the rx frequency
|
||||
uint32_t rit_tx_freq = globalSettings.ritFrequency;
|
||||
globalSettings.ritFrequency = GetActiveVfoFreq();
|
||||
setFrequency(rit_tx_freq,true);
|
||||
}
|
||||
else{
|
||||
if(globalSettings.splitOn){
|
||||
if(Vfo_e::VFO_B == globalSettings.activeVfo){
|
||||
globalSettings.activeVfo = Vfo_e::VFO_A;
|
||||
}
|
||||
else{
|
||||
globalSettings.activeVfo = Vfo_e::VFO_B;
|
||||
}
|
||||
}
|
||||
setFrequency(GetActiveVfoFreq(),true);
|
||||
}
|
||||
|
||||
digitalWrite(PIN_TX_RXn, 1);//turn on the tx
|
||||
globalSettings.txActive = true;
|
||||
drawTx();
|
||||
}
|
||||
|
||||
void stopTx(){
|
||||
digitalWrite(PIN_TX_RXn, 0);//turn off the tx
|
||||
globalSettings.txActive = false;
|
||||
|
||||
if(globalSettings.ritOn){
|
||||
uint32_t rit_rx_freq = globalSettings.ritFrequency;
|
||||
globalSettings.ritFrequency = GetActiveVfoFreq();
|
||||
setFrequency(rit_rx_freq);
|
||||
}
|
||||
else{
|
||||
if(globalSettings.splitOn){
|
||||
if(Vfo_e::VFO_B == globalSettings.activeVfo){
|
||||
globalSettings.activeVfo = Vfo_e::VFO_A;
|
||||
}
|
||||
else{
|
||||
globalSettings.activeVfo = Vfo_e::VFO_B;
|
||||
}
|
||||
}
|
||||
setFrequency(GetActiveVfoFreq());
|
||||
}
|
||||
drawTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* ritEnable is called with a frequency parameter that determines
|
||||
* what the tx frequency will be
|
||||
*/
|
||||
void ritEnable(unsigned long freq){
|
||||
globalSettings.ritOn = true;
|
||||
//save the non-rit frequency back into the VFO memory
|
||||
//as RIT is a temporary shift, this is not saved to EEPROM
|
||||
globalSettings.ritFrequency = freq;
|
||||
}
|
||||
|
||||
// this is called by the RIT menu routine
|
||||
void ritDisable(){
|
||||
if(globalSettings.ritOn){
|
||||
globalSettings.ritOn = false;
|
||||
setFrequency(globalSettings.ritFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
bool autoSelectSidebandChanged(const uint32_t old_frequency)
|
||||
{
|
||||
const uint32_t new_freq = GetActiveVfoFreq();
|
||||
//Transition from below to above the traditional threshold for USB
|
||||
if(old_frequency < THRESHOLD_USB_LSB && new_freq >= THRESHOLD_USB_LSB){
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
|
||||
setFrequency(new_freq);//Refresh tuning to activate the new sideband mode
|
||||
return true;
|
||||
}
|
||||
|
||||
//Transition from above to below the traditional threshold for USB
|
||||
if(old_frequency >= THRESHOLD_USB_LSB && new_freq < THRESHOLD_USB_LSB){
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
|
||||
setFrequency(new_freq);//Refresh tuning to activate the new sideband mode
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
15
tuner.h
Normal file
15
tuner.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
void saveVFOs();
|
||||
void setFrequency(const unsigned long freq, const bool transmit = false);
|
||||
void startTx(TuningMode_e tx_mode);
|
||||
void stopTx();
|
||||
void ritEnable(unsigned long f);
|
||||
void ritDisable();
|
||||
void checkCAT();
|
||||
void cwKeyer(void);
|
||||
void switchVFO(Vfo_e vfoSelect);
|
||||
|
||||
bool autoSelectSidebandChanged(const uint32_t old_frequency); //if the current frequency defaults to a different sideband mode, updates to that sideband mode and returns true. Else, returns false
|
212
ubitx.h
212
ubitx.h
@ -1,212 +0,0 @@
|
||||
|
||||
/* The ubitx is powered by an arduino nano. The pin assignment is as folows
|
||||
*
|
||||
*/
|
||||
|
||||
#define ENC_A (A0) // Tuning encoder interface
|
||||
#define ENC_B (A1) // Tuning encoder interface
|
||||
#define FBUTTON (A2) // Tuning encoder interface
|
||||
#define PTT (A3) // Sense it for ssb and as a straight key for cw operation
|
||||
#define ANALOG_KEYER (A6) // This is used as keyer. The analog port has 4.7K pull up resistor. Details are in the circuit description on www.hfsignals.com
|
||||
#define ANALOG_SPARE (A7) // Not used yet
|
||||
|
||||
#define TX_RX (7) // Pin from the Nano to the radio to switch to TX (HIGH) and RX(LOW)
|
||||
#define CW_TONE (6) // Generates a square wave sidetone while sending the CW.
|
||||
#define TX_LPF_A (5) // The 30 MHz LPF is permanently connected in the output of the PA...
|
||||
#define TX_LPF_B (4) // ...Alternatively, either 3.5 MHz, 7 MHz or 14 Mhz LPFs are...
|
||||
#define TX_LPF_C (3) // ...switched inline depending upon the TX frequency
|
||||
#define CW_KEY (2) // Pin goes high during CW keydown to transmit the carrier.
|
||||
// ... The CW_KEY is needed in addition to the TX/RX key as the...
|
||||
// ...key can be up within a tx period
|
||||
|
||||
|
||||
/** pin assignments
|
||||
14 T_IRQ 2 std changed
|
||||
13 T_DOUT (parallel to SOD/MOSI, pin 9 of display)
|
||||
12 T_DIN (parallel to SDI/MISO, pin 6 of display)
|
||||
11 T_CS 9 (we need to specify this)
|
||||
10 T_CLK (parallel to SCK, pin 7 of display)
|
||||
9 SDO(MSIO) 12 12 (spi)
|
||||
8 LED A0 8 (not needed, permanently on +3.3v) (resistor from 5v,
|
||||
7 SCK 13 13 (spi)
|
||||
6 SDI 11 11 (spi)
|
||||
5 D/C A3 7 (changable)
|
||||
4 RESET A4 9 (not needed, permanently +5v)
|
||||
3 CS A5 10 (changable)
|
||||
2 GND GND
|
||||
1 VCC VCC
|
||||
|
||||
The model is called tjctm24028-spi
|
||||
it uses an ILI9341 display controller and an XPT2046 touch controller.
|
||||
*/
|
||||
|
||||
#define TFT_DC 9
|
||||
#define TFT_CS 10
|
||||
#define CS_PIN 8 //this is the pin to select the touch controller on spi interface
|
||||
|
||||
/**
|
||||
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
|
||||
* We have to be very careful with variables that are declared inside the functions as they are
|
||||
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
|
||||
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
|
||||
* and mess up your programs.
|
||||
* We circumvent this by declaring a few global buffers as kitchen counters where we can
|
||||
* slice and dice our strings. These strings are mostly used to control the display or handle
|
||||
* the input and output from the USB port. We must keep a count of the bytes used while reading
|
||||
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
|
||||
*/
|
||||
extern char c[30], b[30];
|
||||
extern char printBuff[2][20]; //mirrors what is showing on the two lines of the display
|
||||
|
||||
/**
|
||||
* The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig.
|
||||
* This assignment is as follows :
|
||||
* Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
* GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7
|
||||
* These too are flexible with what you may do with them, for the Raduino, we use them to :
|
||||
* - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer
|
||||
* - CW_KEY line : turns on the carrier for CW
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* These are the indices where these user changable settinngs are stored in the EEPROM
|
||||
*/
|
||||
#define MASTER_CAL 0
|
||||
#define LSB_CAL 4
|
||||
#define USB_CAL 8
|
||||
#define SIDE_TONE 12
|
||||
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
|
||||
#define VFO_A 16
|
||||
#define VFO_B 20
|
||||
#define CW_SIDETONE 24
|
||||
#define CW_SPEED 28
|
||||
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
|
||||
#define SLOPE_X 32
|
||||
#define SLOPE_Y 36
|
||||
#define OFFSET_X 40
|
||||
#define OFFSET_Y 44
|
||||
#define CW_DELAYTIME 48
|
||||
|
||||
//These are defines for the new features back-ported from KD8CEC's software
|
||||
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
|
||||
#define VFO_A_MODE 256 // 2: LSB, 3: USB
|
||||
#define VFO_B_MODE 257
|
||||
|
||||
//values that are stroed for the VFO modes
|
||||
#define VFO_MODE_LSB 2
|
||||
#define VFO_MODE_USB 3
|
||||
|
||||
// handkey, iambic a, iambic b : 0,1,2f
|
||||
#define CW_KEY_TYPE 358
|
||||
|
||||
/**
|
||||
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
|
||||
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
|
||||
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
|
||||
* L-network used on it's either sides.
|
||||
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
|
||||
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
|
||||
* and USB becomes LSB.
|
||||
* The second IF of 11.059 Mhz has a ladder crystal filter. If a second oscillator is used at
|
||||
* 56 Mhz (appox), the signal is subtracted FROM the oscillator, inverting a second time, and arrives
|
||||
* at the 11.059 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
|
||||
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
|
||||
* thus keeping the signal's sidebands inverted. The USB will become LSB.
|
||||
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
|
||||
* 11 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
|
||||
*/
|
||||
|
||||
#define INIT_USB_FREQ (11059200l)
|
||||
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
|
||||
#define LOWEST_FREQ (100000l)
|
||||
#define HIGHEST_FREQ (30000000l)
|
||||
|
||||
//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes
|
||||
//these are the parameter passed to startTx
|
||||
#define TX_SSB 0
|
||||
#define TX_CW 1
|
||||
|
||||
extern char ritOn;
|
||||
extern char vfoActive;
|
||||
extern unsigned long vfoA, vfoB, sideTone, usbCarrier;
|
||||
extern char isUsbVfoA, isUsbVfoB;
|
||||
extern unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
|
||||
extern unsigned long firstIF;
|
||||
|
||||
// if cwMode is flipped on, the rx frequency is tuned down by sidetone hz instead of being zerobeat
|
||||
extern int cwMode;
|
||||
|
||||
|
||||
//these are variables that control the keyer behaviour
|
||||
extern int cwSpeed; //this is actuall the dot period in milliseconds
|
||||
extern int32_t calibration;
|
||||
extern int cwDelayTime;
|
||||
extern bool Iambic_Key;
|
||||
|
||||
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
|
||||
extern unsigned char keyerControl;
|
||||
//during CAT commands, we will freeeze the display until CAT is disengaged
|
||||
extern unsigned char doingCAT;
|
||||
|
||||
|
||||
/**
|
||||
* Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
|
||||
*/
|
||||
extern boolean txCAT; //turned on if the transmitting due to a CAT command
|
||||
extern char inTx; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
|
||||
extern int splitOn; //working split, uses VFO B as the transmit frequency
|
||||
extern char keyDown; //in cw mode, denotes the carrier is being transmitted
|
||||
extern char isUSB; //upper sideband was selected, this is reset to the default for the
|
||||
//frequency when it crosses the frequency border of 10 MHz
|
||||
extern byte menuOn; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
|
||||
extern unsigned long cwTimeout; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
|
||||
extern unsigned long dbgCount; //not used now
|
||||
extern unsigned char txFilter ; //which of the four transmit filters are in use
|
||||
extern boolean modeCalibrate;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
|
||||
//beat frequency
|
||||
|
||||
/* these are functions implemented in the main file named as ubitx_xxx.ino */
|
||||
void active_delay(int delay_by);
|
||||
void saveVFOs();
|
||||
void setFrequency(unsigned long f);
|
||||
void startTx(byte txMode);
|
||||
void stopTx();
|
||||
void ritEnable(unsigned long f);
|
||||
void ritDisable();
|
||||
void checkCAT();
|
||||
void cwKeyer(void);
|
||||
void switchVFO(int vfoSelect);
|
||||
|
||||
int enc_read(void); // returns the number of ticks in a short interval, +ve in clockwise, -ve in anti-clockwise
|
||||
int btnDown(); //returns true if the encoder button is pressed
|
||||
|
||||
/* these functions are called universally to update the display */
|
||||
void updateDisplay(); //updates just the VFO frequency to show what is in 'frequency' variable
|
||||
void redrawVFOs(); //redraws only the changed digits of the vfo
|
||||
void guiUpdate(); //repaints the entire screen. Slow!!
|
||||
void drawCommandbar(char *text);
|
||||
void drawTx();
|
||||
//getValueByKnob() provides a reusable dialog box to get a value from the encoder, the prefix and postfix
|
||||
//are useful to concatanate the values with text like "Set Freq to " x " KHz"
|
||||
int getValueByKnob(int minimum, int maximum, int step_size, int initial, char* prefix, char *postfix);
|
||||
|
||||
//functions of the setup menu. implemented in seteup.cpp
|
||||
void doSetup2(); //main setup function, displays the setup menu, calls various dialog boxes
|
||||
void setupBFO();
|
||||
void setupFreq();
|
||||
|
||||
//main functions to check if any button is pressed and other user interface events
|
||||
void doCommands(); //does the commands with encoder to jump from button to button
|
||||
void checkTouch(); //does the commands with a touch on the buttons
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* these are functiosn implemented in ubitx_si5351.cpp */
|
||||
void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
|
||||
void initOscillators();
|
||||
void si5351_set_calibration(int32_t cal); //calibration is a small value that is nudged to make up for the inaccuracies of the reference 25 MHz crystal frequency
|
671
ubitx_cat.cpp
671
ubitx_cat.cpp
@ -1,68 +1,157 @@
|
||||
#include <Arduino.h>
|
||||
#include "ubitx.h"
|
||||
#include "nano_gui.h"
|
||||
#include "scratch_space.h"
|
||||
#include "settings.h"
|
||||
#include "tuner.h"
|
||||
|
||||
/**
|
||||
* The CAT protocol is used by many radios to provide remote control to comptuers through
|
||||
* the serial port.
|
||||
*
|
||||
*
|
||||
* This is very much a work in progress. Parts of this code have been liberally
|
||||
* borrowed from other GPLicensed works like hamlib.
|
||||
*
|
||||
* WARNING : This is an unstable version and it has worked with fldigi,
|
||||
* it gives time out error with WSJTX 1.8.0
|
||||
*
|
||||
* WARNING : This is an unstable version and it has worked with fldigi,
|
||||
* it gives time out error with WSJTX 1.8.0
|
||||
*/
|
||||
|
||||
static unsigned long rxBufferArriveTime = 0;
|
||||
static byte rxBufferCheckCount = 0;
|
||||
#define CAT_RECEIVE_TIMEOUT 500
|
||||
static byte cat[5];
|
||||
static byte insideCat = 0;
|
||||
static byte useOpenRadioControl = 0;
|
||||
static const uint8_t FT817_MESSAGE_SIZE = 5;
|
||||
|
||||
static const uint8_t ACK = 0x00;
|
||||
static const uint8_t RACK = 0xF0;//Re-Acknowledge sent when the state requests is already active
|
||||
|
||||
//Data is ordered parameters 1-4, then command code last
|
||||
enum CatDataIndex_e : uint8_t {
|
||||
P1 = 0,
|
||||
P2 = 1,
|
||||
P3 = 2,
|
||||
P4 = 3,
|
||||
CMD = 4
|
||||
};
|
||||
|
||||
enum Ft817Command_e : uint8_t {
|
||||
//Listed in the order presented by FT-817ND_OM_ENG_E13771011.pdf
|
||||
OffBit = 0x80,
|
||||
LockOn = 0x00,
|
||||
LockOff = LockOn | OffBit,
|
||||
PttOn = 0x08,
|
||||
PttOff = PttOn | OffBit,
|
||||
SetFrequency = 0x01,//P1-P4 are BCD, 0x01 0x42 0x34 0x56 = 14.23456MHz
|
||||
OperatingMode = 0x07,//See OperatingMode_e for P1 decode
|
||||
ClarOn = 0x05,
|
||||
ClarOff = ClarOn | OffBit,
|
||||
ClarFrequency = 0xF5,//P1 is sign/direction (0x00 = +, - otherwise), P3-P4 are BCD, 0x12 0x34 = 12.34kHz
|
||||
VfoToggle = 0x81,
|
||||
SplitOn = 0x02,
|
||||
SplitOff = SplitOn | OffBit,
|
||||
RepeaterMode = 0x09,//See RepeaterMode_e for P1 decode
|
||||
RepeaterOffset = 0xF9,//P1-P4 are BCD
|
||||
CtcssDcsMode = 0x0A,//See CtcssDcsMode_e for P1 decode
|
||||
CtcssTone = 0x0B,//P1-P2 are BCD, 0x08 0x85 = 88.5MHz
|
||||
DcsTone = 0x0C,//P1-P2 are BCD, 0x00 0x23 = code 023
|
||||
ReadRxStatus = 0xE7,//Returns ReadRxStatus_t
|
||||
ReadTxStatus = 0xF7,//Returns ReadTxStatus_t
|
||||
ReadFreqAndMode = 0x03,//Returns current frequency (BCD, 4 bytes), then mode (OperatingMode_e)
|
||||
PowerOn = 0x0F,
|
||||
PowerOff = PowerOn | OffBit,
|
||||
//Unofficial commands
|
||||
ReadEeprom = 0xBB,
|
||||
};
|
||||
|
||||
enum OperatingMode_e : uint8_t {
|
||||
LSB = 0x00,
|
||||
USB = 0x01,
|
||||
CW = 0x02,
|
||||
CWR = 0x03,//CW-reverse aka LSB CW
|
||||
AM = 0x04,
|
||||
FM = 0x08,
|
||||
DIG = 0x0A,
|
||||
PKT = 0x0C,
|
||||
};
|
||||
|
||||
enum RepeaterMode_e : uint8_t {
|
||||
ShiftMinus = 0x09,
|
||||
ShiftPlus = 0x49,
|
||||
Simplex = 0x89,
|
||||
};
|
||||
|
||||
enum CtcssDcsMode_e : uint8_t {
|
||||
DcsOn = 0x0A,
|
||||
CtcssOn = 0x2A,
|
||||
EncoderOn = 0x4A,
|
||||
Off = 0x8A,
|
||||
};
|
||||
|
||||
struct ReadRxStatus_t {
|
||||
//Bitfields are not defined by the standard to be portable, which is unfortunate
|
||||
uint8_t Smeter : 4;//0x00 = S0, 0x09 = S9, etc.
|
||||
uint8_t Dummy : 1;
|
||||
uint8_t DiscriminatorCenteringOff : 1;
|
||||
uint8_t CodeUnmatched : 1;
|
||||
uint8_t SquelchSuppressionActive : 1;
|
||||
};
|
||||
|
||||
struct ReadTxStatus_t {
|
||||
//Bitfields are not defined by the standard to be portable, which is unfortunate
|
||||
uint8_t PowerOutputMeter : 4;
|
||||
uint8_t Dummy : 1;
|
||||
uint8_t SplitOff : 1;
|
||||
uint8_t HighSwrDetected : 1;
|
||||
uint8_t PttOff : 1;
|
||||
};
|
||||
|
||||
//Values based on http://www.ka7oei.com/ft817_memmap.html
|
||||
//hamlib likes to read addresses 0x0065 (read as 0x0064) and 0x007A, but including support for some others
|
||||
enum Ft817Eeprom_e : uint16_t {
|
||||
VfoAndBankSelect = 0x0055,
|
||||
TuningModes = 0x0057,
|
||||
KeyerStatus = 0x0058,
|
||||
BandSelect = 0x0059,
|
||||
BeepVolume = 0x005C,
|
||||
CwPitch = 0x005E,
|
||||
CwWeight = 0x005F,
|
||||
CwDelay = 0x0060,
|
||||
SidetoneVolume = 0x0061,
|
||||
CwSpeed = 0x0062,
|
||||
VoxGain = 0x0063,
|
||||
CatBaudRate = 0x0064,
|
||||
SsbMicVolume = 0x0067,
|
||||
AmMicVolume = 0x0068,
|
||||
FmMicVolume = 0x0069,
|
||||
TxPower = 0x0079,
|
||||
AntennaSelectAndSplit = 0x007A,
|
||||
VfoAPhantomMode = 0x01E9,
|
||||
};
|
||||
|
||||
//for broken protocol
|
||||
#define CAT_RECEIVE_TIMEOUT 500
|
||||
static const uint16_t CAT_RECEIVE_TIMEOUT_MS = 500;
|
||||
|
||||
#define CAT_MODE_LSB 0x00
|
||||
#define CAT_MODE_USB 0x01
|
||||
#define CAT_MODE_CW 0x02
|
||||
#define CAT_MODE_CWR 0x03
|
||||
#define CAT_MODE_AM 0x04
|
||||
#define CAT_MODE_FM 0x08
|
||||
#define CAT_MODE_DIG 0x0A
|
||||
#define CAT_MODE_PKT 0x0C
|
||||
#define CAT_MODE_FMN 0x88
|
||||
|
||||
#define ACK 0
|
||||
|
||||
unsigned int skipTimeCount = 0;
|
||||
|
||||
byte setHighNibble(byte b,byte v) {
|
||||
uint8_t setHighNibble(uint8_t b, uint8_t v) {
|
||||
// Clear the high nibble
|
||||
b &= 0x0f;
|
||||
// Set the high nibble
|
||||
return b | ((v & 0x0f) << 4);
|
||||
}
|
||||
|
||||
byte setLowNibble(byte b,byte v) {
|
||||
uint8_t setLowNibble(uint8_t b, uint8_t v) {
|
||||
// Clear the low nibble
|
||||
b &= 0xf0;
|
||||
// Set the low nibble
|
||||
return b | (v & 0x0f);
|
||||
}
|
||||
|
||||
byte getHighNibble(byte b) {
|
||||
uint8_t getHighNibble(uint8_t b) {
|
||||
return (b >> 4) & 0x0f;
|
||||
}
|
||||
|
||||
byte getLowNibble(byte b) {
|
||||
uint8_t getLowNibble(uint8_t b) {
|
||||
return b & 0x0f;
|
||||
}
|
||||
|
||||
// Takes a number and produces the requested number of decimal digits, staring
|
||||
// from the least significant digit.
|
||||
// from the least significant digit.
|
||||
//
|
||||
void getDecimalDigits(unsigned long number,byte* result,int digits) {
|
||||
void getDecimalDigits(unsigned long number, uint8_t* result,int digits) {
|
||||
for (int i = 0; i < digits; i++) {
|
||||
// "Mask off" (in a decimal sense) the LSD and return it
|
||||
result[i] = number % 10;
|
||||
@ -73,84 +162,46 @@ void getDecimalDigits(unsigned long number,byte* result,int digits) {
|
||||
|
||||
// Takes a frequency and writes it into the CAT command buffer in BCD form.
|
||||
//
|
||||
void writeFreq(unsigned long freq,byte* cmd) {
|
||||
void writeFreq(unsigned long freq, uint8_t* cmd) {
|
||||
// Convert the frequency to a set of decimal digits. We are taking 9 digits
|
||||
// so that we can get up to 999 MHz. But the protocol doesn't care about the
|
||||
// LSD (1's place), so we ignore that digit.
|
||||
byte digits[9];
|
||||
uint8_t digits[9];
|
||||
getDecimalDigits(freq,digits,9);
|
||||
// Start from the LSB and get each nibble
|
||||
cmd[3] = setLowNibble(cmd[3],digits[1]);
|
||||
cmd[3] = setHighNibble(cmd[3],digits[2]);
|
||||
cmd[2] = setLowNibble(cmd[2],digits[3]);
|
||||
cmd[2] = setHighNibble(cmd[2],digits[4]);
|
||||
cmd[1] = setLowNibble(cmd[1],digits[5]);
|
||||
cmd[1] = setHighNibble(cmd[1],digits[6]);
|
||||
cmd[0] = setLowNibble(cmd[0],digits[7]);
|
||||
cmd[0] = setHighNibble(cmd[0],digits[8]);
|
||||
// Start from the LSB and get each nibble
|
||||
cmd[P4] = setLowNibble(cmd[P4],digits[1]);
|
||||
cmd[P4] = setHighNibble(cmd[P4],digits[2]);
|
||||
cmd[P3] = setLowNibble(cmd[P3],digits[3]);
|
||||
cmd[P3] = setHighNibble(cmd[P3],digits[4]);
|
||||
cmd[P2] = setLowNibble(cmd[P2],digits[5]);
|
||||
cmd[P2] = setHighNibble(cmd[P2],digits[6]);
|
||||
cmd[P1] = setLowNibble(cmd[P1],digits[7]);
|
||||
cmd[P1] = setHighNibble(cmd[P1],digits[8]);
|
||||
}
|
||||
|
||||
// This function takes a frquency that is encoded using 4 bytes of BCD
|
||||
// This function takes a frquency that is encoded using 4 uint8_ts of BCD
|
||||
// representation and turns it into an long measured in Hz.
|
||||
//
|
||||
// [12][34][56][78] = 123.45678? Mhz
|
||||
//
|
||||
unsigned long readFreq(byte* cmd) {
|
||||
uint32_t readFreq(uint8_t* cmd) {
|
||||
// Pull off each of the digits
|
||||
byte d7 = getHighNibble(cmd[0]);
|
||||
byte d6 = getLowNibble(cmd[0]);
|
||||
byte d5 = getHighNibble(cmd[1]);
|
||||
byte d4 = getLowNibble(cmd[1]);
|
||||
byte d3 = getHighNibble(cmd[2]);
|
||||
byte d2 = getLowNibble(cmd[2]);
|
||||
byte d1 = getHighNibble(cmd[3]);
|
||||
byte d0 = getLowNibble(cmd[3]);
|
||||
return
|
||||
(unsigned long)d7 * 100000000L +
|
||||
(unsigned long)d6 * 10000000L +
|
||||
(unsigned long)d5 * 1000000L +
|
||||
(unsigned long)d4 * 100000L +
|
||||
(unsigned long)d3 * 10000L +
|
||||
(unsigned long)d2 * 1000L +
|
||||
(unsigned long)d1 * 100L +
|
||||
(unsigned long)d0 * 10L;
|
||||
unsigned long ret = 0;
|
||||
for(uint8_t i = 0; i < 4; ++i){
|
||||
const uint8_t d1 = getHighNibble(cmd[i]);
|
||||
const uint8_t d0 = getLowNibble(cmd[i]);
|
||||
ret *= 100;
|
||||
ret += 10*d1 + d0;
|
||||
}
|
||||
|
||||
return ret*10;
|
||||
}
|
||||
|
||||
//void ReadEEPRom_FT817(byte fromType)
|
||||
void catReadEEPRom(void)
|
||||
void catGetEeprom(const uint16_t read_address, uint8_t* response)
|
||||
{
|
||||
//for remove warnings
|
||||
byte temp0 = cat[0];
|
||||
byte temp1 = cat[1];
|
||||
/*
|
||||
itoa((int) cat[0], b, 16);
|
||||
strcat(b, ":");
|
||||
itoa((int) cat[1], c, 16);
|
||||
strcat(b, c);
|
||||
printLine2(b);
|
||||
*/
|
||||
|
||||
cat[0] = 0;
|
||||
cat[1] = 0;
|
||||
//for remove warnings[1] = 0;
|
||||
|
||||
switch (temp1)
|
||||
switch (read_address)
|
||||
{
|
||||
case 0x45 : //
|
||||
if (temp0 == 0x03)
|
||||
{
|
||||
cat[0] = 0x00;
|
||||
cat[1] = 0xD0;
|
||||
}
|
||||
break;
|
||||
case 0x47 : //
|
||||
if (temp0 == 0x03)
|
||||
{
|
||||
cat[0] = 0xDC;
|
||||
cat[1] = 0xE0;
|
||||
}
|
||||
break;
|
||||
case 0x55 :
|
||||
case Ft817Eeprom_e::VfoAndBankSelect:
|
||||
//0 : VFO A/B 0 = VFO-A, 1 = VFO-B
|
||||
//1 : MTQMB Select 0 = (Not MTQMB), 1 = MTQMB ("Memory Tune Quick Memory Bank")
|
||||
//2 : QMB Select 0 = (Not QMB), 1 = QMB ("Quick Memory Bank")
|
||||
@ -159,299 +210,211 @@ void catReadEEPRom(void)
|
||||
//5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE
|
||||
//6 :
|
||||
//7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0)
|
||||
cat[0] = 0x80 + (vfoActive == VFO_B ? 1 : 0);
|
||||
cat[1] = 0x00;
|
||||
*response = 0x80 //always report VFO mode
|
||||
| ((VFO_B == globalSettings.activeVfo) ? 0x01 : 0x00);
|
||||
break;
|
||||
case 0x57 : //
|
||||
//0 : 1-0 AGC Mode 00 = Auto, 01 = Fast, 10 = Slow, 11 = Off
|
||||
//2 DSP On/Off 0 = Off, 1 = On (Display format)
|
||||
//4 PBT On/Off 0 = Off, 1 = On (Passband Tuning)
|
||||
//5 NB On/Off 0 = Off, 1 = On (Noise Blanker)
|
||||
//6 Lock On/Off 0 = Off, 1 = On (Dial Lock)
|
||||
//7 FST (Fast Tuning) On/Off 0 = Off, 1 = On (Fast tuning)
|
||||
|
||||
cat[0] = 0xC0;
|
||||
cat[1] = 0x40;
|
||||
break;
|
||||
case 0x59 : // band select VFO A Band Select 0000 = 160 M, 0001 = 75 M, 0010 = 40 M, 0011 = 30 M, 0100 = 20 M, 0101 = 17 M, 0110 = 15 M, 0111 = 12 M, 1000 = 10 M, 1001 = 6 M, 1010 = FM BCB, 1011 = Air, 1100 = 2 M, 1101 = UHF, 1110 = (Phantom)
|
||||
//http://www.ka7oei.com/ft817_memmap.html
|
||||
//CAT_BUFF[0] = 0xC2;
|
||||
//CAT_BUFF[1] = 0x82;
|
||||
break;
|
||||
case 0x5C : //Beep Volume (0-100) (#13)
|
||||
cat[0] = 0xB2;
|
||||
cat[1] = 0x42;
|
||||
break;
|
||||
case 0x5E :
|
||||
case Ft817Eeprom_e::CwPitch:
|
||||
//3-0 : CW Pitch (300-1000 Hz) (#20) From 0 to E (HEX) with 0 = 300 Hz and each step representing 50 Hz
|
||||
//5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel
|
||||
//7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW
|
||||
//CAT_BUFF[0] = 0x08;
|
||||
cat[0] = (sideTone - 300)/50;
|
||||
cat[1] = 0x25;
|
||||
*response = (globalSettings.cwSideToneFreq - 300)/50;
|
||||
break;
|
||||
case 0x61 : //Sidetone (Volume) (#44)
|
||||
cat[0] = sideTone % 50;
|
||||
cat[1] = 0x08;
|
||||
case Ft817Eeprom_e::SidetoneVolume:
|
||||
//Sidetone (Volume) (#44) 0-100
|
||||
*response = globalSettings.cwSideToneFreq / 100;
|
||||
break;
|
||||
case 0x5F : //
|
||||
//4-0 CW Weight (1.:2.5-1:4.5) (#22) From 0 to 14 (HEX) with 0 = 1:2.5, incrementing in 0.1 weight steps
|
||||
//5 420 ARS (#2) 0 = Off, 1 = On
|
||||
//6 144 ARS (#1) 0 = Off, 1 = On
|
||||
//7 Sql/RF-G (#45) 0 = Off, 1 = On
|
||||
cat[0] = 0x32;
|
||||
cat[1] = 0x08;
|
||||
case Ft817Eeprom_e::CwDelay:
|
||||
//CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
|
||||
*response = globalSettings.cwActiveTimeoutMs / 10;
|
||||
break;
|
||||
case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms
|
||||
cat[0] = cwDelayTime;
|
||||
cat[1] = 0x32;
|
||||
break;
|
||||
case 0x62 : //
|
||||
case Ft817Eeprom_e::CwSpeed:
|
||||
//5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps)
|
||||
//7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours
|
||||
//CAT_BUFF[0] = 0x08;
|
||||
cat[0] = 1200 / cwSpeed - 4;
|
||||
cat[1] = 0xB2;
|
||||
*response = (1200 / globalSettings.cwDitDurationMs) - 4;
|
||||
break;
|
||||
case 0x63 : //
|
||||
//6-0 VOX Gain (#51) Contains 1-100 (decimal) as displayed
|
||||
//7 Disable AM/FM Dial (#4) 0 = Enable, 1 = Disable
|
||||
cat[0] = 0xB2;
|
||||
cat[1] = 0xA5;
|
||||
case Ft817Eeprom_e::CatBaudRate:
|
||||
//4-0 : VOX Delay (#50) 0 = 100 Ms with each step representing 100 Ms. 24 = 2500 Ms
|
||||
//5 : Emergency (#28) 0 = Off, 1 = On
|
||||
//7-6 : CAT Rate (4800, 9600, 38400) (#14) 00 = 4800, 01 = 9600, 10 = 38400 Baud
|
||||
*response = 0xA5;
|
||||
break;
|
||||
case 0x64 : //
|
||||
case Ft817Eeprom_e::VfoAPhantomMode:
|
||||
//2-0 : 000 = LSB, 001 = USB, 010 = CW, 011 = CWR, 100 = AM, 101 = FM, 110 = DIG, 111 = PKT
|
||||
//7-3 : ?
|
||||
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
|
||||
*response = OperatingMode_e::USB;
|
||||
}
|
||||
else{
|
||||
*response = OperatingMode_e::LSB;
|
||||
}
|
||||
break;
|
||||
case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed
|
||||
cat[0] = 0xB2;
|
||||
cat[1] = 0xB2;
|
||||
break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed
|
||||
case 0x78 :
|
||||
if (isUSB)
|
||||
cat[0] = CAT_MODE_USB;
|
||||
else
|
||||
cat[0] = CAT_MODE_LSB;
|
||||
|
||||
if (cat[0] != 0) cat[0] = 1 << 5;
|
||||
case Ft817Eeprom_e::AntennaSelectAndSplit:
|
||||
//0 : HF Antenna Select 0 = Front, 1 = Rear
|
||||
//1 : 6 M Antenna Select 0 = Front, 1 = Rear
|
||||
//2 : FM BCB Antenna Select 0 = Front, 1 = Rear
|
||||
//3 : Air Antenna Select 0 = Front, 1 = Rear
|
||||
//4 : 2 M Antenna Select 0 = Front, 1 = Rear
|
||||
//5 : UHF Antenna Select 0 = Front, 1 = Rear
|
||||
//6 : ? ?
|
||||
//7 : SPL On/Off 0 = Off, 1 = On
|
||||
*response = (globalSettings.splitOn ? 0xFF : 0x7F);
|
||||
break;
|
||||
case 0x79 : //
|
||||
//1-0 TX Power (All bands) 00 = High, 01 = L3, 10 = L2, 11 = L1
|
||||
//3 PRI On/Off 0 = Off, 1 = On
|
||||
//DW On/Off 0 = Off, 1 = On
|
||||
//SCN (Scan) Mode 00 = No scan, 10 = Scan up, 11 = Scan down
|
||||
//ART On/Off 0 = Off, 1 = On
|
||||
cat[0] = 0x00;
|
||||
cat[1] = 0x00;
|
||||
break;
|
||||
case 0x7A : //SPLIT
|
||||
//7A 0 HF Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 1 6 M Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 2 FM BCB Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 3 Air Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 4 2 M Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 5 UHF Antenna Select 0 = Front, 1 = Rear
|
||||
//7A 6 ? ?
|
||||
//7A 7 SPL On/Off 0 = Off, 1 = On
|
||||
|
||||
cat[0] = (splitOn ? 0xFF : 0x7F);
|
||||
break;
|
||||
case 0xB3 : //
|
||||
cat[0] = 0x00;
|
||||
cat[1] = 0x4D;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// sent the data
|
||||
Serial.write(cat, 2);
|
||||
}
|
||||
|
||||
void processCATCommand2(byte* cmd) {
|
||||
byte response[5];
|
||||
unsigned long f;
|
||||
|
||||
switch(cmd[4]){
|
||||
/* case 0x00:
|
||||
response[0]=0;
|
||||
Serial.write(response, 1);
|
||||
break;
|
||||
*/
|
||||
case 0x01:
|
||||
//set frequency
|
||||
f = readFreq(cmd);
|
||||
setFrequency(f);
|
||||
updateDisplay();
|
||||
response[0]=0;
|
||||
Serial.write(response, 1);
|
||||
//sprintf(b, "set:%ld", f);
|
||||
//printLine2(b);
|
||||
break;
|
||||
//Maps some of the fixed memory layout of the FT817's EEPROM
|
||||
void catReadEEPRom(uint8_t* cmd, uint8_t* response)
|
||||
{
|
||||
const uint16_t read_address = cmd[P1] << 8 | cmd[P2];
|
||||
|
||||
case 0x02:
|
||||
//split on
|
||||
splitOn = 1;
|
||||
break;
|
||||
case 0x82:
|
||||
//split off
|
||||
splitOn = 0;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
writeFreq(frequency,response); // Put the frequency into the buffer
|
||||
if (isUSB)
|
||||
response[4] = 0x01; //USB
|
||||
else
|
||||
response[4] = 0x00; //LSB
|
||||
Serial.write(response,5);
|
||||
//printLine2("cat:getfreq");
|
||||
break;
|
||||
|
||||
case 0x07: // set mode
|
||||
if (cmd[0] == 0x00 || cmd[0] == 0x03)
|
||||
isUSB = 0;
|
||||
else
|
||||
isUSB = 1;
|
||||
response[0] = 0x00;
|
||||
Serial.write(response, 1);
|
||||
setFrequency(frequency);
|
||||
//printLine2("cat: mode changed");
|
||||
//updateDisplay();
|
||||
break;
|
||||
|
||||
case 0x08: // PTT On
|
||||
if (!inTx) {
|
||||
response[0] = 0;
|
||||
txCAT = true;
|
||||
startTx(TX_SSB);
|
||||
updateDisplay();
|
||||
} else {
|
||||
response[0] = 0xf0;
|
||||
}
|
||||
Serial.write(response,1);
|
||||
updateDisplay();
|
||||
break;
|
||||
catGetEeprom(read_address,response);
|
||||
catGetEeprom(read_address+1,response+1);
|
||||
}
|
||||
|
||||
case 0x88 : //PTT OFF
|
||||
if (inTx) {
|
||||
stopTx();
|
||||
txCAT = false;
|
||||
}
|
||||
response[0] = 0;
|
||||
Serial.write(response,1);
|
||||
updateDisplay();
|
||||
break;
|
||||
void processCatCommand(uint8_t* cmd) {
|
||||
//A response of a single byte, 0x00, is an ACK, so default to that
|
||||
uint8_t response[FT817_MESSAGE_SIZE] = {ACK};
|
||||
uint8_t response_length = 1;
|
||||
|
||||
case 0x81:
|
||||
//toggle the VFOs
|
||||
response[0] = 0;
|
||||
if (vfoActive == VFO_A)
|
||||
switchVFO(VFO_B);
|
||||
else
|
||||
switchVFO(VFO_A);
|
||||
//menuVfoToggle(1); // '1' forces it to change the VFO
|
||||
Serial.write(response,1);
|
||||
updateDisplay();
|
||||
break;
|
||||
|
||||
case 0xBB: //Read FT-817 EEPROM Data (for comfirtable)
|
||||
catReadEEPRom();
|
||||
break;
|
||||
|
||||
case 0xe7 :
|
||||
// get receiver status, we have hardcoded this as
|
||||
//as we dont' support ctcss, etc.
|
||||
response[0] = 0x09;
|
||||
Serial.write(response,1);
|
||||
break;
|
||||
|
||||
case 0xf7:
|
||||
switch(cmd[CMD]){
|
||||
case Ft817Command_e::SetFrequency:
|
||||
{
|
||||
boolean isHighSWR = false;
|
||||
boolean isSplitOn = false;
|
||||
|
||||
/*
|
||||
Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib)
|
||||
*/
|
||||
response[0] = ((inTx ? 0 : 1) << 7) +
|
||||
((isHighSWR ? 1 : 0) << 6) + //hi swr off / on
|
||||
((isSplitOn ? 1 : 0) << 5) + //Split on / off
|
||||
(0 << 4) + //dummy data
|
||||
0x08; //P0 meter data
|
||||
|
||||
Serial.write(response, 1);
|
||||
uint32_t f = readFreq(cmd);
|
||||
setFrequency(f);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//somehow, get this to print the four bytes
|
||||
ultoa(*((unsigned long *)cmd), c, 16);
|
||||
/*itoa(cmd[4], b, 16);
|
||||
strcat(b, ">");
|
||||
strcat(b, c);
|
||||
printLine2(b);*/
|
||||
response[0] = 0x00;
|
||||
Serial.write(response[0]);
|
||||
|
||||
case Ft817Command_e::SplitOn:
|
||||
if(globalSettings.splitOn){
|
||||
response[0] = RACK;
|
||||
}
|
||||
globalSettings.splitOn = true;
|
||||
break;
|
||||
case Ft817Command_e::SplitOff:
|
||||
if(!globalSettings.splitOn){
|
||||
response[0] = RACK;
|
||||
}
|
||||
globalSettings.splitOn = false;
|
||||
break;
|
||||
|
||||
case Ft817Command_e::ReadFreqAndMode:
|
||||
//First 4 bytes are the frequency
|
||||
writeFreq(GetActiveVfoFreq(),response);//bytes 0-3
|
||||
//Last byte is the mode
|
||||
if (VfoMode_e::VFO_MODE_USB == GetActiveVfoMode()){
|
||||
response[4] = OperatingMode_e::USB;
|
||||
}
|
||||
else{
|
||||
response[4] = OperatingMode_e::LSB;
|
||||
}
|
||||
response_length = 5;
|
||||
break;
|
||||
|
||||
case Ft817Command_e::OperatingMode:
|
||||
if(OperatingMode_e::LSB == cmd[P1] || OperatingMode_e::CWR == cmd[P1]){
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
|
||||
}
|
||||
else{
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
|
||||
}
|
||||
|
||||
setFrequency(GetActiveVfoFreq());//Refresh frequency to get new mode to take effect
|
||||
break;
|
||||
|
||||
case Ft817Command_e::PttOn:
|
||||
if (!globalSettings.txActive) {
|
||||
globalSettings.txCatActive = true;
|
||||
startTx(globalSettings.tuningMode);
|
||||
}
|
||||
else {
|
||||
response[0] = RACK;
|
||||
}
|
||||
break;
|
||||
|
||||
case Ft817Command_e::PttOff:
|
||||
if (globalSettings.txActive) {
|
||||
stopTx();
|
||||
}
|
||||
else{
|
||||
response[0] = RACK;
|
||||
}
|
||||
globalSettings.txCatActive = false;
|
||||
break;
|
||||
|
||||
case Ft817Command_e::VfoToggle:
|
||||
if (Vfo_e::VFO_A == globalSettings.activeVfo){
|
||||
globalSettings.activeVfo = Vfo_e::VFO_B;
|
||||
}
|
||||
else{
|
||||
globalSettings.activeVfo = Vfo_e::VFO_A;
|
||||
}
|
||||
break;
|
||||
|
||||
case Ft817Command_e::ReadEeprom:
|
||||
catReadEEPRom(cmd,response);
|
||||
response_length = 2;
|
||||
break;
|
||||
|
||||
case Ft817Command_e::ReadRxStatus:
|
||||
//We don't have visibility into these values, so just hard code stuff
|
||||
ReadRxStatus_t reply_status;
|
||||
reply_status.Dummy = 0;
|
||||
reply_status.Smeter = 9;//S9
|
||||
reply_status.SquelchSuppressionActive = 0;
|
||||
reply_status.DiscriminatorCenteringOff = 1;
|
||||
reply_status.CodeUnmatched = 0;
|
||||
response[0] = *(uint8_t*)&reply_status;
|
||||
break;
|
||||
|
||||
case Ft817Command_e::ReadTxStatus:
|
||||
{
|
||||
//We don't have visibility into some of these values, so just hard code stuff
|
||||
ReadTxStatus_t reply_status;
|
||||
reply_status.Dummy = 0;
|
||||
reply_status.HighSwrDetected = 0;
|
||||
reply_status.PowerOutputMeter = 0xF;
|
||||
reply_status.PttOff = !globalSettings.txActive;
|
||||
reply_status.SplitOff = globalSettings.splitOn;//Yaesu's documentation says that 1 = split off, but as of 2020-05-04 hamlib reads (*split = (p->tx_status & 0x20) ? RIG_SPLIT_ON : RIG_SPLIT_OFF), so do what hamlib wants
|
||||
|
||||
response[0] = *(uint8_t*)&reply_status;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
//Do something?
|
||||
break;
|
||||
}
|
||||
|
||||
insideCat = false;
|
||||
Serial.write(response, response_length);
|
||||
}
|
||||
|
||||
int catCount = 0;
|
||||
void checkCAT(){
|
||||
byte i;
|
||||
static uint8_t rx_buffer[FT817_MESSAGE_SIZE];
|
||||
static uint8_t current_index = 0;
|
||||
static uint32_t timeout = 0;
|
||||
|
||||
//Check Serial Port Buffer
|
||||
if (Serial.available() == 0) { //Set Buffer Clear status
|
||||
rxBufferCheckCount = 0;
|
||||
return;
|
||||
}
|
||||
else if (Serial.available() < 5) { //First Arrived
|
||||
if (rxBufferCheckCount == 0){
|
||||
rxBufferCheckCount = Serial.available();
|
||||
rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout
|
||||
}
|
||||
else if (rxBufferArriveTime < millis()){ //Clear Buffer
|
||||
for (i = 0; i < Serial.available(); i++)
|
||||
rxBufferCheckCount = Serial.read();
|
||||
rxBufferCheckCount = 0;
|
||||
}
|
||||
else if (rxBufferCheckCount < Serial.available()){ // Increase buffer count, slow arrive
|
||||
rxBufferCheckCount = Serial.available();
|
||||
rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout
|
||||
if(timeout < millis()){
|
||||
current_index = 0;
|
||||
timeout = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Arived CAT DATA
|
||||
for (i = 0; i < 5; i++)
|
||||
cat[i] = Serial.read();
|
||||
|
||||
|
||||
//this code is not re-entrant.
|
||||
if (insideCat == 1)
|
||||
return;
|
||||
insideCat = 1;
|
||||
|
||||
/**
|
||||
* This routine is enabled to debug the cat protocol
|
||||
**/
|
||||
catCount++;
|
||||
|
||||
/*
|
||||
if (cat[4] != 0xf7 && cat[4] != 0xbb && cat[4] != 0x03){
|
||||
sprintf(b, "%d %02x %02x%02x%02x%02x", catCount, cat[4],cat[0], cat[1], cat[2], cat[3]);
|
||||
printLine2(b);
|
||||
else{
|
||||
if(0 == current_index){
|
||||
timeout = millis() + CAT_RECEIVE_TIMEOUT_MS;
|
||||
}
|
||||
rx_buffer[current_index] = Serial.read();
|
||||
++current_index;
|
||||
if(current_index < FT817_MESSAGE_SIZE){
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if (!doingCAT){
|
||||
doingCAT = 1;
|
||||
displayText("CAT on", 100,120,100,40, ILI9341_ORANGE, ILI9341_BLACK, ILI9341_WHITE);
|
||||
}
|
||||
*/
|
||||
processCATCommand2(cat);
|
||||
insideCat = 0;
|
||||
processCatCommand(rx_buffer);
|
||||
current_index = 0;
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
|
||||
|
1024
ubitx_ui.cpp
1024
ubitx_ui.cpp
File diff suppressed because it is too large
Load Diff
@ -1,755 +0,0 @@
|
||||
/**
|
||||
* This source file is under General Public License version 3.
|
||||
*
|
||||
* This verision uses a built-in Si5351 library
|
||||
* Most source code are meant to be understood by the compilers and the computers.
|
||||
* Code that has to be hackable needs to be well understood and properly documented.
|
||||
* Donald Knuth coined the term Literate Programming to indicate code that is written be
|
||||
* easily read and understood.
|
||||
*
|
||||
* The Raduino is a small board that includes the Arduin Nano, a TFT display and
|
||||
* an Si5351a frequency synthesizer. This board is manufactured by HF Signals Electronics Pvt Ltd
|
||||
*
|
||||
* To learn more about Arduino you may visit www.arduino.cc.
|
||||
*
|
||||
* The Arduino works by starts executing the code in a function called setup() and then it
|
||||
* repeatedly keeps calling loop() forever. All the initialization code is kept in setup()
|
||||
* and code to continuously sense the tuning knob, the function button, transmit/receive,
|
||||
* etc is all in the loop() function. If you wish to study the code top down, then scroll
|
||||
* to the bottom of this file and read your way up.
|
||||
*
|
||||
* Below are the libraries to be included for building the Raduino
|
||||
* The EEPROM library is used to store settings like the frequency memory, caliberation data, etc.
|
||||
*
|
||||
* The main chip which generates upto three oscillators of various frequencies in the
|
||||
* Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
|
||||
* from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
|
||||
* Instead, you can look up the Si5351 library written by xxx, yyy. You can download and
|
||||
* install it from www.url.com to complile this file.
|
||||
* The Wire.h library is used to talk to the Si5351 and we also declare an instance of
|
||||
* Si5351 object to control the clocks.
|
||||
*/
|
||||
#include <Wire.h>
|
||||
#include <EEPROM.h>
|
||||
#include "ubitx.h"
|
||||
#include "nano_gui.h"
|
||||
|
||||
/**
|
||||
The main chip which generates upto three oscillators of various frequencies in the
|
||||
Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
|
||||
from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
|
||||
|
||||
We no longer use the standard SI5351 library because of its huge overhead due to many unused
|
||||
features consuming a lot of program space. Instead of depending on an external library we now use
|
||||
Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the
|
||||
code). Here are some defines and declarations used by Jerry's routines:
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
|
||||
* We have to be very careful with variables that are declared inside the functions as they are
|
||||
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
|
||||
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
|
||||
* and mess up your programs.
|
||||
* We circumvent this by declaring a few global buffers as kitchen counters where we can
|
||||
* slice and dice our strings. These strings are mostly used to control the display or handle
|
||||
* the input and output from the USB port. We must keep a count of the bytes used while reading
|
||||
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
|
||||
*/
|
||||
char c[30], b[30];
|
||||
|
||||
/**
|
||||
* These are the indices where these user changable settinngs are stored in the EEPROM
|
||||
*/
|
||||
#define MASTER_CAL 0
|
||||
#define LSB_CAL 4
|
||||
#define USB_CAL 8
|
||||
#define SIDE_TONE 12
|
||||
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
|
||||
#define VFO_A 16
|
||||
#define VFO_B 20
|
||||
#define CW_SIDETONE 24
|
||||
#define CW_SPEED 28
|
||||
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
|
||||
#define SLOPE_X 32
|
||||
#define SLOPE_Y 36
|
||||
#define OFFSET_X 40
|
||||
#define OFFSET_Y 44
|
||||
#define CW_DELAYTIME 48
|
||||
|
||||
//These are defines for the new features back-ported from KD8CEC's software
|
||||
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
|
||||
#define VFO_A_MODE 256 // 2: LSB, 3: USB
|
||||
#define VFO_B_MODE 257
|
||||
|
||||
//values that are stroed for the VFO modes
|
||||
#define VFO_MODE_LSB 2
|
||||
#define VFO_MODE_USB 3
|
||||
|
||||
// handkey, iambic a, iambic b : 0,1,2f
|
||||
#define CW_KEY_TYPE 358
|
||||
|
||||
/**
|
||||
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
|
||||
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
|
||||
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
|
||||
* L-network used on it's either sides.
|
||||
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
|
||||
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
|
||||
* and USB becomes LSB.
|
||||
* The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at
|
||||
* 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives
|
||||
* at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
|
||||
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
|
||||
* thus keeping the signal's sidebands inverted. The USB will become LSB.
|
||||
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
|
||||
* 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
|
||||
*/
|
||||
|
||||
#define INIT_USB_FREQ (11059200l)
|
||||
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
|
||||
#define LOWEST_FREQ (100000l)
|
||||
#define HIGHEST_FREQ (30000000l)
|
||||
|
||||
//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes
|
||||
//these are the parameter passed to startTx
|
||||
#define TX_SSB 0
|
||||
#define TX_CW 1
|
||||
|
||||
char ritOn = 0;
|
||||
char vfoActive = VFO_A;
|
||||
int8_t meter_reading = 0; // a -1 on meter makes it invisible
|
||||
unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier;
|
||||
char isUsbVfoA=0, isUsbVfoB=1;
|
||||
unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
|
||||
unsigned long firstIF = 45005000L;
|
||||
|
||||
// if cwMode is flipped on, the rx frequency is tuned down by sidetone hz instead of being zerobeat
|
||||
int cwMode = 0;
|
||||
|
||||
|
||||
//these are variables that control the keyer behaviour
|
||||
int cwSpeed = 100; //this is actuall the dot period in milliseconds
|
||||
extern int32_t calibration;
|
||||
int cwDelayTime = 60;
|
||||
bool Iambic_Key = true;
|
||||
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
|
||||
unsigned char keyerControl = IAMBICB;
|
||||
//during CAT commands, we will freeeze the display until CAT is disengaged
|
||||
unsigned char doingCAT = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
|
||||
*/
|
||||
boolean txCAT = false; //turned on if the transmitting due to a CAT command
|
||||
char inTx = 0; //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
|
||||
int splitOn = 0; //working split, uses VFO B as the transmit frequency
|
||||
char keyDown = 0; //in cw mode, denotes the carrier is being transmitted
|
||||
char isUSB = 0; //upper sideband was selected, this is reset to the default for the
|
||||
//frequency when it crosses the frequency border of 10 MHz
|
||||
byte menuOn = 0; //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
|
||||
unsigned long cwTimeout = 0; //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
|
||||
unsigned long dbgCount = 0; //not used now
|
||||
unsigned char txFilter = 0; //which of the four transmit filters are in use
|
||||
boolean modeCalibrate = false;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
|
||||
//beat frequency
|
||||
|
||||
/**
|
||||
* Below are the basic functions that control the uBitx. Understanding the functions before
|
||||
* you start hacking around
|
||||
*/
|
||||
|
||||
/**
|
||||
* Our own delay. During any delay, the raduino should still be processing a few times.
|
||||
*/
|
||||
|
||||
void active_delay(int delay_by){
|
||||
unsigned long timeStart = millis();
|
||||
while (millis() - timeStart <= (unsigned long)delay_by) {
|
||||
delay(10);
|
||||
//Background Work
|
||||
checkCAT();
|
||||
}
|
||||
}
|
||||
|
||||
void saveVFOs(){
|
||||
|
||||
if (vfoActive == VFO_A)
|
||||
EEPROM.put(VFO_A, frequency);
|
||||
else
|
||||
EEPROM.put(VFO_A, vfoA);
|
||||
|
||||
if (isUsbVfoA)
|
||||
EEPROM.put(VFO_A_MODE, VFO_MODE_USB);
|
||||
else
|
||||
EEPROM.put(VFO_A_MODE, VFO_MODE_LSB);
|
||||
|
||||
if (vfoActive == VFO_B)
|
||||
EEPROM.put(VFO_B, frequency);
|
||||
else
|
||||
EEPROM.put(VFO_B, vfoB);
|
||||
|
||||
if (isUsbVfoB)
|
||||
EEPROM.put(VFO_B_MODE, VFO_MODE_USB);
|
||||
else
|
||||
EEPROM.put(VFO_B_MODE, VFO_MODE_LSB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the properly tx harmonic filters
|
||||
* The four harmonic filters use only three relays
|
||||
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
|
||||
* Briefly, it works like this,
|
||||
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
|
||||
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
|
||||
* the KT1 is on for the three other cases.
|
||||
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
|
||||
* to 18 MHz LPF (That also works for 14 Mhz)
|
||||
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
|
||||
* - KT3, when switched on selects the 7-10 Mhz filter
|
||||
* - KT3 when switched off selects the 3.5-5 Mhz filter
|
||||
* See the circuit to understand this
|
||||
*/
|
||||
|
||||
void setTXFilters(unsigned long freq){
|
||||
|
||||
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
|
||||
digitalWrite(TX_LPF_A, 1);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq > 7000000L){
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 1);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else {
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setTXFilters_v5(unsigned long freq){
|
||||
|
||||
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
|
||||
digitalWrite(TX_LPF_A, 1);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else if (freq > 7000000L){
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 1);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
}
|
||||
else {
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the most frequently called function that configures the
|
||||
* radio to a particular frequeny, sideband and sets up the transmit filters
|
||||
*
|
||||
* The transmit filter relays are powered up only during the tx so they dont
|
||||
* draw any current during rx.
|
||||
*
|
||||
* The carrier oscillator of the detector/modulator is permanently fixed at
|
||||
* uppper sideband. The sideband selection is done by placing the second oscillator
|
||||
* either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands
|
||||
* through mixing of the second local oscillator.
|
||||
*/
|
||||
|
||||
void setFrequency(unsigned long f){
|
||||
uint64_t osc_f, firstOscillator, secondOscillator;
|
||||
|
||||
setTXFilters(f);
|
||||
|
||||
/*
|
||||
if (isUSB){
|
||||
si5351bx_setfreq(2, firstIF + f);
|
||||
si5351bx_setfreq(1, firstIF + usbCarrier);
|
||||
}
|
||||
else{
|
||||
si5351bx_setfreq(2, firstIF + f);
|
||||
si5351bx_setfreq(1, firstIF - usbCarrier);
|
||||
}
|
||||
*/
|
||||
//alternative to reduce the intermod spur
|
||||
if (isUSB){
|
||||
if (cwMode)
|
||||
si5351bx_setfreq(2, firstIF + f + sideTone);
|
||||
else
|
||||
si5351bx_setfreq(2, firstIF + f);
|
||||
si5351bx_setfreq(1, firstIF + usbCarrier);
|
||||
}
|
||||
else{
|
||||
if (cwMode)
|
||||
si5351bx_setfreq(2, firstIF + f + sideTone);
|
||||
else
|
||||
si5351bx_setfreq(2, firstIF + f);
|
||||
si5351bx_setfreq(1, firstIF - usbCarrier);
|
||||
}
|
||||
|
||||
frequency = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* startTx is called by the PTT, cw keyer and CAT protocol to
|
||||
* put the uBitx in tx mode. It takes care of rit settings, sideband settings
|
||||
* Note: In cw mode, doesnt key the radio, only puts it in tx mode
|
||||
* CW offest is calculated as lower than the operating frequency when in LSB mode, and vice versa in USB mode
|
||||
*/
|
||||
|
||||
void startTx(byte txMode){
|
||||
unsigned long tx_freq = 0;
|
||||
|
||||
digitalWrite(TX_RX, 1);
|
||||
inTx = 1;
|
||||
|
||||
if (ritOn){
|
||||
//save the current as the rx frequency
|
||||
ritRxFrequency = frequency;
|
||||
setFrequency(ritTxFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (splitOn == 1) {
|
||||
if (vfoActive == VFO_B) {
|
||||
vfoActive = VFO_A;
|
||||
isUSB = isUsbVfoA;
|
||||
frequency = vfoA;
|
||||
}
|
||||
else if (vfoActive == VFO_A){
|
||||
vfoActive = VFO_B;
|
||||
frequency = vfoB;
|
||||
isUSB = isUsbVfoB;
|
||||
}
|
||||
}
|
||||
setFrequency(frequency);
|
||||
}
|
||||
|
||||
if (txMode == TX_CW){
|
||||
digitalWrite(TX_RX, 0);
|
||||
|
||||
//turn off the second local oscillator and the bfo
|
||||
si5351bx_setfreq(0, 0);
|
||||
si5351bx_setfreq(1, 0);
|
||||
|
||||
//shif the first oscillator to the tx frequency directly
|
||||
//the key up and key down will toggle the carrier unbalancing
|
||||
//the exact cw frequency is the tuned frequency + sidetone
|
||||
if (isUSB)
|
||||
si5351bx_setfreq(2, frequency + sideTone);
|
||||
else
|
||||
si5351bx_setfreq(2, frequency - sideTone);
|
||||
|
||||
delay(20);
|
||||
digitalWrite(TX_RX, 1);
|
||||
}
|
||||
drawTx();
|
||||
//updateDisplay();
|
||||
}
|
||||
|
||||
void stopTx(){
|
||||
inTx = 0;
|
||||
|
||||
digitalWrite(TX_RX, 0); //turn off the tx
|
||||
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off
|
||||
|
||||
if (ritOn)
|
||||
setFrequency(ritRxFrequency);
|
||||
else{
|
||||
if (splitOn == 1) {
|
||||
//vfo Change
|
||||
if (vfoActive == VFO_B){
|
||||
vfoActive = VFO_A;
|
||||
frequency = vfoA;
|
||||
isUSB = isUsbVfoA;
|
||||
}
|
||||
else if (vfoActive == VFO_A){
|
||||
vfoActive = VFO_B;
|
||||
frequency = vfoB;
|
||||
isUSB = isUsbVfoB;
|
||||
}
|
||||
}
|
||||
setFrequency(frequency);
|
||||
}
|
||||
//updateDisplay();
|
||||
drawTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* ritEnable is called with a frequency parameter that determines
|
||||
* what the tx frequency will be
|
||||
*/
|
||||
void ritEnable(unsigned long f){
|
||||
ritOn = 1;
|
||||
//save the non-rit frequency back into the VFO memory
|
||||
//as RIT is a temporary shift, this is not saved to EEPROM
|
||||
ritTxFrequency = f;
|
||||
}
|
||||
|
||||
// this is called by the RIT menu routine
|
||||
void ritDisable(){
|
||||
if (ritOn){
|
||||
ritOn = 0;
|
||||
setFrequency(ritTxFrequency);
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic User Interface Routines. These check the front panel for any activity
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PTT is checked only if we are not already in a cw transmit session
|
||||
* If the PTT is pressed, we shift to the ritbase if the rit was on
|
||||
* flip the T/R line to T and update the display to denote transmission
|
||||
*/
|
||||
|
||||
void checkPTT(){
|
||||
//we don't check for ptt when transmitting cw
|
||||
if (cwTimeout > 0)
|
||||
return;
|
||||
|
||||
if (digitalRead(PTT) == 0 && inTx == 0){
|
||||
startTx(TX_SSB);
|
||||
active_delay(50); //debounce the PTT
|
||||
}
|
||||
|
||||
if (digitalRead(PTT) == 1 && inTx == 1)
|
||||
stopTx();
|
||||
}
|
||||
|
||||
//check if the encoder button was pressed
|
||||
void checkButton(){
|
||||
int i, t1, t2, knob, new_knob;
|
||||
|
||||
//only if the button is pressed
|
||||
if (!btnDown())
|
||||
return;
|
||||
active_delay(50);
|
||||
if (!btnDown()) //debounce
|
||||
return;
|
||||
|
||||
//disengage any CAT work
|
||||
doingCAT = 0;
|
||||
|
||||
int downTime = 0;
|
||||
while(btnDown()){
|
||||
active_delay(10);
|
||||
downTime++;
|
||||
if (downTime > 300){
|
||||
doSetup2();
|
||||
return;
|
||||
}
|
||||
}
|
||||
active_delay(100);
|
||||
|
||||
|
||||
doCommands();
|
||||
//wait for the button to go up again
|
||||
while(btnDown())
|
||||
active_delay(10);
|
||||
active_delay(50);//debounce
|
||||
}
|
||||
|
||||
void switchVFO(int vfoSelect){
|
||||
if (vfoSelect == VFO_A){
|
||||
if (vfoActive == VFO_B){
|
||||
vfoB = frequency;
|
||||
isUsbVfoB = isUSB;
|
||||
EEPROM.put(VFO_B, frequency);
|
||||
if (isUsbVfoB)
|
||||
EEPROM.put(VFO_B_MODE, VFO_MODE_USB);
|
||||
else
|
||||
EEPROM.put(VFO_B_MODE, VFO_MODE_LSB);
|
||||
}
|
||||
vfoActive = VFO_A;
|
||||
// printLine2("Selected VFO A ");
|
||||
frequency = vfoA;
|
||||
isUSB = isUsbVfoA;
|
||||
}
|
||||
else {
|
||||
if (vfoActive == VFO_A){
|
||||
vfoA = frequency;
|
||||
isUsbVfoA = isUSB;
|
||||
EEPROM.put(VFO_A, frequency);
|
||||
if (isUsbVfoA)
|
||||
EEPROM.put(VFO_A_MODE, VFO_MODE_USB);
|
||||
else
|
||||
EEPROM.put(VFO_A_MODE, VFO_MODE_LSB);
|
||||
}
|
||||
vfoActive = VFO_B;
|
||||
// printLine2("Selected VFO B ");
|
||||
frequency = vfoB;
|
||||
isUSB = isUsbVfoB;
|
||||
}
|
||||
|
||||
setFrequency(frequency);
|
||||
redrawVFOs();
|
||||
saveVFOs();
|
||||
}
|
||||
|
||||
/**
|
||||
* The tuning jumps by 50 Hz on each step when you tune slowly
|
||||
* As you spin the encoder faster, the jump size also increases
|
||||
* This way, you can quickly move to another band by just spinning the
|
||||
* tuning knob
|
||||
*/
|
||||
|
||||
void doTuning(){
|
||||
int s;
|
||||
static unsigned long prev_freq;
|
||||
static unsigned long nextFrequencyUpdate = 0;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
if (now >= nextFrequencyUpdate && prev_freq != frequency){
|
||||
updateDisplay();
|
||||
nextFrequencyUpdate = now + 500;
|
||||
prev_freq = frequency;
|
||||
}
|
||||
|
||||
s = enc_read();
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
doingCAT = 0; // go back to manual mode if you were doing CAT
|
||||
prev_freq = frequency;
|
||||
|
||||
|
||||
if (s > 10)
|
||||
frequency += 200l * s;
|
||||
else if (s > 5)
|
||||
frequency += 100l * s;
|
||||
else if (s > 0)
|
||||
frequency += 50l * s;
|
||||
else if (s < -10)
|
||||
frequency += 200l * s;
|
||||
else if (s < -5)
|
||||
frequency += 100l * s;
|
||||
else if (s < 0)
|
||||
frequency += 50l * s;
|
||||
|
||||
if (prev_freq < 10000000l && frequency > 10000000l)
|
||||
isUSB = true;
|
||||
|
||||
if (prev_freq > 10000000l && frequency < 10000000l)
|
||||
isUSB = false;
|
||||
|
||||
setFrequency(frequency);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* RIT only steps back and forth by 100 hz at a time
|
||||
*/
|
||||
void doRIT(){
|
||||
unsigned long newFreq;
|
||||
|
||||
int knob = enc_read();
|
||||
unsigned long old_freq = frequency;
|
||||
|
||||
if (knob < 0)
|
||||
frequency -= 100l;
|
||||
else if (knob > 0)
|
||||
frequency += 100;
|
||||
|
||||
if (old_freq != frequency){
|
||||
setFrequency(frequency);
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings are read from EEPROM. The first time around, the values may not be
|
||||
* present or out of range, in this case, some intelligent defaults are copied into the
|
||||
* variables.
|
||||
*/
|
||||
void initSettings(){
|
||||
byte x;
|
||||
//read the settings from the eeprom and restore them
|
||||
//if the readings are off, then set defaults
|
||||
EEPROM.get(MASTER_CAL, calibration);
|
||||
EEPROM.get(USB_CAL, usbCarrier);
|
||||
EEPROM.get(VFO_A, vfoA);
|
||||
EEPROM.get(VFO_B, vfoB);
|
||||
EEPROM.get(CW_SIDETONE, sideTone);
|
||||
EEPROM.get(CW_SPEED, cwSpeed);
|
||||
EEPROM.get(CW_DELAYTIME, cwDelayTime);
|
||||
|
||||
// the screen calibration parameters : int slope_x=104, slope_y=137, offset_x=28, offset_y=29;
|
||||
|
||||
if (usbCarrier > 11060000l || usbCarrier < 11048000l)
|
||||
usbCarrier = 11052000l;
|
||||
if (vfoA > 35000000l || 3500000l > vfoA)
|
||||
vfoA = 7150000l;
|
||||
if (vfoB > 35000000l || 3500000l > vfoB)
|
||||
vfoB = 14150000l;
|
||||
if (sideTone < 100 || 2000 < sideTone)
|
||||
sideTone = 800;
|
||||
if (cwSpeed < 10 || 1000 < cwSpeed)
|
||||
cwSpeed = 100;
|
||||
if (cwDelayTime < 10 || cwDelayTime > 100)
|
||||
cwDelayTime = 50;
|
||||
|
||||
/*
|
||||
* The VFO modes are read in as either 2 (USB) or 3(LSB), 0, the default
|
||||
* is taken as 'uninitialized
|
||||
*/
|
||||
|
||||
EEPROM.get(VFO_A_MODE, x);
|
||||
|
||||
switch(x){
|
||||
case VFO_MODE_USB:
|
||||
isUsbVfoA = 1;
|
||||
break;
|
||||
case VFO_MODE_LSB:
|
||||
isUsbVfoA = 0;
|
||||
break;
|
||||
default:
|
||||
if (vfoA > 10000000l)
|
||||
isUsbVfoA = 1;
|
||||
else
|
||||
isUsbVfoA = 0;
|
||||
}
|
||||
|
||||
EEPROM.get(VFO_B_MODE, x);
|
||||
switch(x){
|
||||
case VFO_MODE_USB:
|
||||
isUsbVfoB = 1;
|
||||
break;
|
||||
case VFO_MODE_LSB:
|
||||
isUsbVfoB = 0;
|
||||
break;
|
||||
default:
|
||||
if (vfoA > 10000000l)
|
||||
isUsbVfoB = 1;
|
||||
else
|
||||
isUsbVfoB = 0;
|
||||
}
|
||||
|
||||
//set the current mode
|
||||
isUSB = isUsbVfoA;
|
||||
|
||||
/*
|
||||
* The keyer type splits into two variables
|
||||
*/
|
||||
EEPROM.get(CW_KEY_TYPE, x);
|
||||
|
||||
if (x == 0)
|
||||
Iambic_Key = false;
|
||||
else if (x == 1){
|
||||
Iambic_Key = true;
|
||||
keyerControl &= ~IAMBICB;
|
||||
}
|
||||
else if (x == 2){
|
||||
Iambic_Key = true;
|
||||
keyerControl |= IAMBICB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void initPorts(){
|
||||
|
||||
analogReference(DEFAULT);
|
||||
|
||||
//??
|
||||
pinMode(ENC_A, INPUT_PULLUP);
|
||||
pinMode(ENC_B, INPUT_PULLUP);
|
||||
pinMode(FBUTTON, INPUT_PULLUP);
|
||||
|
||||
//configure the function button to use the external pull-up
|
||||
// pinMode(FBUTTON, INPUT);
|
||||
// digitalWrite(FBUTTON, HIGH);
|
||||
|
||||
pinMode(PTT, INPUT_PULLUP);
|
||||
// pinMode(ANALOG_KEYER, INPUT_PULLUP);
|
||||
|
||||
pinMode(CW_TONE, OUTPUT);
|
||||
digitalWrite(CW_TONE, 0);
|
||||
|
||||
pinMode(TX_RX,OUTPUT);
|
||||
digitalWrite(TX_RX, 0);
|
||||
|
||||
pinMode(TX_LPF_A, OUTPUT);
|
||||
pinMode(TX_LPF_B, OUTPUT);
|
||||
pinMode(TX_LPF_C, OUTPUT);
|
||||
digitalWrite(TX_LPF_A, 0);
|
||||
digitalWrite(TX_LPF_B, 0);
|
||||
digitalWrite(TX_LPF_C, 0);
|
||||
|
||||
pinMode(CW_KEY, OUTPUT);
|
||||
digitalWrite(CW_KEY, 0);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(38400);
|
||||
Serial.flush();
|
||||
|
||||
displayInit();
|
||||
initSettings();
|
||||
initPorts();
|
||||
initOscillators();
|
||||
frequency = vfoA;
|
||||
setFrequency(vfoA);
|
||||
|
||||
if (btnDown()){
|
||||
setupTouch();
|
||||
isUSB = 1;
|
||||
setFrequency(10000000l);
|
||||
setupFreq();
|
||||
isUSB = 0;
|
||||
setFrequency(7100000l);
|
||||
setupBFO();
|
||||
}
|
||||
guiUpdate();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The loop checks for keydown, ptt, function button and tuning.
|
||||
*/
|
||||
|
||||
byte flasher = 0;
|
||||
boolean wastouched = false;
|
||||
|
||||
void loop(){
|
||||
|
||||
if (cwMode)
|
||||
cwKeyer();
|
||||
else if (!txCAT)
|
||||
checkPTT();
|
||||
|
||||
checkButton();
|
||||
//tune only when not tranmsitting
|
||||
if (!inTx){
|
||||
if (ritOn)
|
||||
doRIT();
|
||||
else
|
||||
doTuning();
|
||||
checkTouch();
|
||||
}
|
||||
|
||||
checkCAT();
|
||||
}
|
185
ubitxv6.ino
Normal file
185
ubitxv6.ino
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* This source file is under General Public License version 3.
|
||||
*
|
||||
* This verision uses a built-in Si5351 library
|
||||
* Most source code are meant to be understood by the compilers and the computers.
|
||||
* Code that has to be hackable needs to be well understood and properly documented.
|
||||
* Donald Knuth coined the term Literate Programming to indicate code that is written be
|
||||
* easily read and understood.
|
||||
*
|
||||
* The Raduino is a small board that includes the Arduin Nano, a TFT display and
|
||||
* an Si5351a frequency synthesizer. This board is manufactured by HF Signals Electronics Pvt Ltd
|
||||
*
|
||||
* To learn more about Arduino you may visit www.arduino.cc.
|
||||
*
|
||||
* The Arduino works by starts executing the code in a function called setup() and then it
|
||||
* repeatedly keeps calling loop() forever. All the initialization code is kept in setup()
|
||||
* and code to continuously sense the tuning knob, the function button, transmit/receive,
|
||||
* etc is all in the loop() function. If you wish to study the code top down, then scroll
|
||||
* to the bottom of this file and read your way up.
|
||||
*
|
||||
* Below are the libraries to be included for building the Raduino
|
||||
* The EEPROM library is used to store settings like the frequency memory, caliberation data, etc.
|
||||
*
|
||||
* The main chip which generates upto three oscillators of various frequencies in the
|
||||
* Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
|
||||
* from www.silabs.com although, strictly speaking it is not a requirment to understand this code.
|
||||
* Instead, you can look up the Si5351 library written by xxx, yyy. You can download and
|
||||
* install it from www.url.com to complile this file.
|
||||
* The Wire.h library is used to talk to the Si5351 and we also declare an instance of
|
||||
* Si5351 object to control the clocks.
|
||||
*/
|
||||
#include <Wire.h>
|
||||
#include "encoder.h"
|
||||
#include "menu.h"
|
||||
#include "menu_main.h"
|
||||
#include "morse.h"
|
||||
#include "pin_definitions.h"
|
||||
#include "push_button.h"
|
||||
#include "nano_gui.h"
|
||||
#include "settings.h"
|
||||
#include "setup.h"
|
||||
#include "si5351.h"
|
||||
#include "touch.h"
|
||||
#include "tuner.h"
|
||||
#include "ui_touch.h"
|
||||
|
||||
/**
|
||||
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
|
||||
* We have to be very careful with variables that are declared inside the functions as they are
|
||||
* created in a memory region called the stack. The stack has just a few bytes of space on the Arduino
|
||||
* if you declare large strings inside functions, they can easily exceed the capacity of the stack
|
||||
* and mess up your programs.
|
||||
* We circumvent this by declaring a few global buffers as kitchen counters where we can
|
||||
* slice and dice our strings. These strings are mostly used to control the display or handle
|
||||
* the input and output from the USB port. We must keep a count of the bytes used while reading
|
||||
* the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable.
|
||||
*/
|
||||
char b[128];
|
||||
char c[30];
|
||||
|
||||
//during CAT commands, we will freeeze the display until CAT is disengaged
|
||||
unsigned char doingCAT = 0;
|
||||
|
||||
/**
|
||||
* Basic User Interface Routines. These check the front panel for any activity
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PTT is checked only if we are not already in a cw transmit session
|
||||
* If the PTT is pressed, we shift to the ritbase if the rit was on
|
||||
* flip the T/R line to T and update the display to denote transmission
|
||||
*/
|
||||
|
||||
void checkPTT(){
|
||||
//we don't check for ptt when transmitting cw
|
||||
if (globalSettings.cwExpirationTimeMs > 0){
|
||||
return;
|
||||
}
|
||||
|
||||
if(digitalRead(PIN_PTT) == 0 && !globalSettings.txActive){
|
||||
startTx(TuningMode_e::TUNE_SSB);
|
||||
delay(50); //debounce the PTT
|
||||
}
|
||||
|
||||
if (digitalRead(PIN_PTT) == 1 && globalSettings.txActive)
|
||||
stopTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings are read from EEPROM. The first time around, the values may not be
|
||||
* present or out of range, in this case, some intelligent defaults are copied into the
|
||||
* variables.
|
||||
*/
|
||||
void initSettings(){
|
||||
LoadDefaultSettings();
|
||||
LoadSettingsFromEeprom();
|
||||
}
|
||||
|
||||
void initPorts(){
|
||||
|
||||
analogReference(DEFAULT);
|
||||
|
||||
//??
|
||||
pinMode(PIN_ENC_A, INPUT_PULLUP);
|
||||
pinMode(PIN_ENC_B, INPUT_PULLUP);
|
||||
pinMode(PIN_ENC_PUSH_BUTTON, INPUT_PULLUP);
|
||||
enc_setup();
|
||||
|
||||
//configure the function button to use the external pull-up
|
||||
// pinMode(PIN_ENC_PUSH_BUTTON, INPUT);
|
||||
// digitalWrite(PIN_ENC_PUSH_BUTTON, HIGH);
|
||||
|
||||
pinMode(PIN_PTT, INPUT_PULLUP);
|
||||
// pinMode(PIN_ANALOG_KEYER, INPUT_PULLUP);
|
||||
|
||||
pinMode(PIN_CW_TONE, OUTPUT);
|
||||
digitalWrite(PIN_CW_TONE, 0);
|
||||
|
||||
pinMode(PIN_TX_RXn,OUTPUT);
|
||||
digitalWrite(PIN_TX_RXn, 0);
|
||||
|
||||
pinMode(PIN_TX_LPF_A, OUTPUT);
|
||||
pinMode(PIN_TX_LPF_B, OUTPUT);
|
||||
pinMode(PIN_TX_LPF_C, OUTPUT);
|
||||
digitalWrite(PIN_TX_LPF_A, 0);
|
||||
digitalWrite(PIN_TX_LPF_B, 0);
|
||||
digitalWrite(PIN_TX_LPF_C, 0);
|
||||
|
||||
pinMode(PIN_CW_KEY, OUTPUT);
|
||||
digitalWrite(PIN_CW_KEY, 0);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(38400);
|
||||
Serial.flush();
|
||||
|
||||
initSettings();
|
||||
displayInit();
|
||||
initTouch();
|
||||
initPorts();
|
||||
initOscillators();
|
||||
setFrequency(globalSettings.vfoA.frequency);
|
||||
|
||||
//Run initial calibration routine if button is pressed during power up
|
||||
if(ButtonPress_e::NotPressed != CheckTunerButton()){
|
||||
LoadDefaultSettings();
|
||||
setupTouch();
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_USB);
|
||||
setFrequency(10000000L);
|
||||
runLocalOscSetting();
|
||||
SetActiveVfoMode(VfoMode_e::VFO_MODE_LSB);
|
||||
setFrequency(7100000L);
|
||||
runBfoSetting();
|
||||
}
|
||||
|
||||
rootMenu->initMenu();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The loop checks for keydown, ptt, function button and tuning.
|
||||
*/
|
||||
|
||||
void loop(){
|
||||
if(TuningMode_e::TUNE_CW == globalSettings.tuningMode){
|
||||
cwKeyer();
|
||||
}
|
||||
else if(!globalSettings.txCatActive){
|
||||
checkPTT();
|
||||
}
|
||||
|
||||
checkCAT();
|
||||
|
||||
if(globalSettings.txActive){
|
||||
//Don't run menus when transmitting
|
||||
return;
|
||||
}
|
||||
|
||||
ButtonPress_e tuner_button = CheckTunerButton();
|
||||
Point touch_point;
|
||||
ButtonPress_e touch_button = checkTouch(&touch_point);
|
||||
int16_t knob = enc_read();
|
||||
runActiveMenu(tuner_button,touch_button,touch_point,knob);
|
||||
}
|
31
ui_touch.cpp
Normal file
31
ui_touch.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "ui_touch.h"
|
||||
|
||||
#include <Arduino.h>//delay
|
||||
|
||||
#include "button_timing.h"
|
||||
#include "touch.h"
|
||||
|
||||
ButtonPress_e checkTouch(Point *const touch_point_out){
|
||||
if (!readTouch(touch_point_out)){
|
||||
return ButtonPress_e::NotPressed;
|
||||
}
|
||||
delay(DEBOUNCE_DELAY_MS);
|
||||
if (!readTouch(touch_point_out)){//debounce
|
||||
return ButtonPress_e::NotPressed;
|
||||
}
|
||||
|
||||
uint16_t down_time = 0;
|
||||
while(readTouch(touch_point_out) && (down_time < LONG_PRESS_TIME_MS)){
|
||||
delay(LONG_PRESS_POLL_TIME_MS);
|
||||
down_time += LONG_PRESS_POLL_TIME_MS;
|
||||
}
|
||||
|
||||
scaleTouch(touch_point_out);
|
||||
|
||||
if(down_time < LONG_PRESS_TIME_MS){
|
||||
return ButtonPress_e::ShortPress;
|
||||
}
|
||||
else{
|
||||
return ButtonPress_e::LongPress;
|
||||
}
|
||||
}
|
6
ui_touch.h
Normal file
6
ui_touch.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "menu.h"
|
||||
#include "point.h"
|
||||
|
||||
ButtonPress_e checkTouch(Point *const touch_point_out);
|
44
utils.cpp
Normal file
44
utils.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <stdlib.h>//ultoa
|
||||
#include <string.h>//memset, strlen
|
||||
#include <WString.h>//F()
|
||||
|
||||
/*
|
||||
* Formats the frequency given
|
||||
*/
|
||||
void formatFreq(uint32_t freq, char* buff, uint16_t buff_size, uint8_t fixed_width)
|
||||
{
|
||||
memset(buff, 0, buff_size);
|
||||
|
||||
ultoa(freq, buff, 10);
|
||||
uint8_t num_digits = strlen(buff);
|
||||
const uint8_t num_spacers = (num_digits-1) / 3;
|
||||
const uint8_t num_leading_digits_raw = num_digits % 3;
|
||||
const uint8_t num_leading_digits = (0 == num_leading_digits_raw) ? 3 : num_leading_digits_raw;
|
||||
|
||||
if(0 < fixed_width){
|
||||
while(0 < fixed_width - num_digits - num_spacers){
|
||||
if(0 == fixed_width % 4){
|
||||
buff[0] = '\x81';//separator size
|
||||
}
|
||||
else{
|
||||
buff[0] = '\x80';//digit size
|
||||
}
|
||||
--fixed_width;
|
||||
++buff;
|
||||
}
|
||||
}
|
||||
|
||||
ultoa(freq, buff, 10);
|
||||
buff += num_leading_digits;
|
||||
num_digits -= num_leading_digits;
|
||||
for(int i = num_digits-1; i >= 0; --i){
|
||||
buff[i + (i/3 + 1)] = buff[i];
|
||||
}
|
||||
for(unsigned int i = 0; i < num_spacers; ++i){
|
||||
memcpy_P(buff,F("."),1);
|
||||
buff += 4;
|
||||
}
|
||||
}
|
7
utils.h
Normal file
7
utils.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LIMIT(val,min,max) ((val) < (min)) ? (min) : (((max) < (val)) ? (max) : (val))
|
||||
|
||||
void formatFreq(uint32_t freq, char* buff_out, uint16_t buff_size, uint8_t fixed_width = 0);
|
4
version.cpp
Normal file
4
version.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
#include "version.h"
|
||||
|
||||
const char VERSION_STRING_PRIVATE [] PROGMEM = "R1.5.1";
|
||||
const char* const VERSION_STRING = VERSION_STRING_PRIVATE;
|
Loading…
Reference in New Issue
Block a user