Compare commits

...

332 Commits

Author SHA1 Message Date
Michael Clemens
9d96fa649e callsign.cpp aktualisiert 2024-03-21 10:28:21 +01:00
Michael Clemens
6dceffd993 reactivated 80m, changed to color scheme to green on black 2021-05-17 17:29:57 +02:00
Michael Clemens
36cd505e8e
changed band limits, enabled 12m, disabled 80m 2021-05-13 15:20:01 +02:00
Michael Clemens
6d4fc996d9
Update callsign.cpp 2021-05-13 15:15:19 +02:00
reedbn
6c9e759901
Add user manual link to readme 2021-04-11 18:19:42 -07:00
reedbn
4918670eaa Save 12 bytes by adjusting math, inspried by Jack Purdum 2020-12-13 21:51:46 -08:00
reedbn
4eecda170d Add size to comment 2020-12-13 21:34:55 -08:00
reedbn
50381da6de Adjust keyer behavior to always run PTT button as if in KEYER_STRAIGHT mode 2020-12-13 18:19:13 -08:00
reedbn
b136f5e43c Add parentheses to force correct addition operation. Thanks to Barry Halterman for catching this. 2020-12-13 11:44:40 -08:00
Reed Nightingale
a4f5f4b3cb Update displayed version 2020-07-18 20:43:36 -07:00
reedbn
ab678f3e8b
Merge pull request #13 from reedbn/ide_1_8_13-size-fixes
Arduino IDE 1.8.13 size fixes
2020-07-18 20:30:40 -07:00
Reed Nightingale
c62eaaba2e Update readme with compiler info 2020-07-18 20:28:43 -07:00
Reed Nightingale
c11c8bfbe3 Remove bands that aren't on dial from band lookup struct 2020-07-18 18:58:43 -07:00
Reed Nightingale
84f47b0a23 Bump version 2020-05-05 21:41:45 -07:00
Reed Nightingale
740e881d28 Update readme due to new feature 2020-05-05 21:41:45 -07:00
Reed Nightingale
71e5f877da Add touch cancellation option 2020-05-05 21:32:30 -07:00
Reed Nightingale
e6fa6f935b Fix zraw assignment issue that was preventing touch press-and-hold from working 2020-05-05 21:25:52 -07:00
Reed Nightingale
b3a7e34d1b Replace 16-bit read style with slower, but apparently more reliable 8-bit read style 2020-05-05 20:36:03 -07:00
Reed Nightingale
d282062eef Force redraw after setupTouch is called so that menu is on screen 2020-05-05 20:35:17 -07:00
Reed Nightingale
f76b50fcc3 Update commented out debug print, and remove black screen draw at the end 2020-05-05 20:34:28 -07:00
Reed Nightingale
488e462c6c Debugging checkpoint 2020-05-05 19:54:07 -07:00
Reed Nightingale
c323cec0cb Update readme with lots of information for other prospective developers 2020-05-04 23:17:05 -07:00
Reed Nightingale
f541ff8928 Get rid of do-nothing functions 2020-05-04 23:00:31 -07:00
Reed Nightingale
e7748d2878 Switch colors from defines to consts 2020-05-04 22:53:41 -07:00
Reed Nightingale
e168a4d5b0 Bump version 2020-05-04 22:28:43 -07:00
Reed Nightingale
e3f23ae755 Slight tweak to how frequencies are changed so that quicklist changes will update the actual oscillators 2020-05-04 22:27:37 -07:00
Reed Nightingale
0b65559e97 Revert the previous commit 2020-05-04 22:26:09 -07:00
Reed Nightingale
2a412fd34a Force frequency change after recall. Unfortunately, this puts us just 4 bytes over the limit 2020-05-04 22:06:47 -07:00
Reed Nightingale
a138490dcc Change decision making in main menu loop to ensure display consistency when CAT changes 2020-05-04 21:57:24 -07:00
Reed Nightingale
4cf8f7a840 Use ACK/RACK, and set default response length to 1 so that it usually doesn't have to be re-set. 2020-05-04 01:01:05 -07:00
Reed Nightingale
6a6dc2a29a Remove debug drawing 2020-05-04 00:56:25 -07:00
Reed Nightingale
a7cbf50384 Fix comment based on reviewing hamlib code 2020-05-04 00:43:38 -07:00
Reed Nightingale
e809b37e29 Invert split value from what Yaesu's documentation says, because hamlib reads it inverted 2020-05-04 00:34:15 -07:00
Reed Nightingale
e770230d14 CAT debug stuff added 2020-05-04 00:03:43 -07:00
Reed Nightingale
7a32c8e205 Bump version number 2020-05-03 23:58:58 -07:00
Reed Nightingale
97d825351c Re-enable numpad, now that we have some more space 2020-05-03 23:56:08 -07:00
Reed Nightingale
2e74193d93 Give 0xBB a name 2020-05-03 23:54:38 -07:00
Reed Nightingale
ce0fac95fe Remove unused and fixed-output cases 2020-05-03 23:02:10 -07:00
Reed Nightingale
2505e54235 Remove unused cases 2020-05-03 22:37:38 -07:00
Reed Nightingale
8045b9b02b Remove explicit 0 sets 2020-05-03 22:35:35 -07:00
Reed Nightingale
eba70c64ef Swap EEPROM virtualization hardcoding for multi-byte reading using enums 2020-05-03 21:35:44 -07:00
Reed Nightingale
53a39b0699 Revise switch to use a single Serial.write, based on switched changes 2020-05-03 17:55:23 -07:00
Reed Nightingale
c6d83beff0 Use enums instead of hardcoded numbers 2020-05-03 17:45:43 -07:00
Reed Nightingale
8e4adbff72 Add enums for a bunch of commands 2020-05-03 16:36:27 -07:00
Reed Nightingale
a3b9f703ba Replace more indicies with enums 2020-05-03 15:36:37 -07:00
Reed Nightingale
8aa80bdbc7 Rename processing function, remove trailing spaces 2020-05-03 15:34:13 -07:00
Reed Nightingale
18a05460aa Simplify timeout logic, saving nearly 100 bytes 2020-05-03 15:32:06 -07:00
Reed Nightingale
e8c80a8304 Replace 'byte' with 'uint8_t' 2020-05-03 14:48:11 -07:00
Reed Nightingale
4e961bdea7 Give indicies names, so that it's easier to tell what's being extracted/set 2020-05-03 14:46:42 -07:00
Reed Nightingale
501205ebd5 Save ~100 bytes by rolling up the frequency parser 2020-04-27 00:17:41 -07:00
Reed Nightingale
a9b9b62dd1 Use driver-specified min/max frequencies 2020-04-26 23:49:26 -07:00
Reed Nightingale
55822dadf7 Give min/max frequency variable names 2020-04-26 23:46:32 -07:00
Reed Nightingale
ab8ba66067 Reuse menu running code between quicklist and numpad 2020-04-26 23:36:16 -07:00
Reed Nightingale
61c22474e9 Slim down some of the setup text 2020-04-26 23:36:16 -07:00
Reed Nightingale
77b1bd291e Save ~100 bytes by combining these two functions 2020-04-26 23:36:16 -07:00
Reed Nightingale
aa7e6825b4 Add quicklist to main menu, but disable numpad temporarily for size reasons 2020-04-26 23:36:16 -07:00
Reed Nightingale
fafc9acd0e Create quicklist menu 2020-04-26 23:36:16 -07:00
Reed Nightingale
b49b61615b Add quick list to EEPROM settings 2020-04-26 23:36:16 -07:00
Reed Nightingale
cd3364c835 Update version 2020-04-26 23:36:06 -07:00
Reed Nightingale
e980cff9ff Remove brackets from callsign 2020-04-26 23:35:54 -07:00
Reed Nightingale
0467223fe6 If RIT is on, turn it off before switching VFO 2020-04-26 15:17:18 -07:00
Reed Nightingale
94be10fd87 Justify RIT text left so that it doesn't interfere with the TX text 2020-04-26 15:11:44 -07:00
Reed Nightingale
8cc272b065 Use numpad icon 2020-04-26 15:03:32 -07:00
Reed Nightingale
f9907cebde Add icons for numpad and star 2020-04-26 15:03:18 -07:00
Reed Nightingale
b1e4466b0e Remove ornamental borders on setup page 2020-04-26 14:31:09 -07:00
Reed Nightingale
6cd72a5b8a Update sideband selection graphic on band switch 2020-04-25 23:06:16 -07:00
Reed Nightingale
e85db6781a Use same color for version and callsign for better callsign visibility 2020-04-25 22:56:35 -07:00
Reed Nightingale
bd1ff23d8f Add comment about toneAC2 library license 2020-04-25 22:55:10 -07:00
Reed Nightingale
c45a824276 Switch back to nicer font 2020-04-25 22:52:39 -07:00
Reed Nightingale
2c4448f6a0 Replace calls to the default Arduino Tone library to toneAC2, saving over 1024 bytes 2020-04-25 22:44:26 -07:00
Reed Nightingale
7a788b6297 Proxy the tone library so that it builds 2020-04-25 22:36:40 -07:00
Reed Nightingale
da45643035 Remove secondary pin from library 2020-04-25 22:36:17 -07:00
Reed Nightingale
0fe54d054f Add toneAC2 library 2020-04-25 22:32:17 -07:00
Reed Nightingale
2a8ff5e758 Replace fastest line routines with fillRect to save ~300 bytes 2020-04-25 21:09:54 -07:00
Reed Nightingale
06fa0e9ad7 Update README 2020-04-25 21:08:43 -07:00
Reed Nightingale
1f5f3a1f9f Use safer copy 2020-04-25 20:34:47 -07:00
Reed Nightingale
85ea714955 Show current setting on setting screen 2020-04-25 20:31:35 -07:00
Reed Nightingale
4e7d1717e0 Re-add version to main display, add callsign display, and re-enable TX indicator 2020-04-25 20:31:10 -07:00
Reed Nightingale
9b9a1610c2 Revert "Rough pass at button grids"
This reverts commit 1f97be3263.
2020-04-25 19:47:02 -07:00
Reed Nightingale
1f97be3263 Rough pass at button grids 2020-04-25 19:46:36 -07:00
Reed Nightingale
2db9899d4d Move SI5351 driver stuff into it's own header 2020-04-25 14:31:59 -07:00
Reed Nightingale
fdd2afce98 Fix auto-selection of sideband mode when tuning 2020-04-25 12:35:59 -07:00
Reed Nightingale
0f45a2fc43 Fix out-of-band midpoint selection 2020-04-25 12:17:15 -07:00
Reed Nightingale
f66a173dbe Get LSB and USB buttons to update properly 2020-04-25 12:10:25 -07:00
Reed Nightingale
8af59b0a85 Move button pressing logic to it's own file 2020-04-25 12:00:19 -07:00
Reed Nightingale
5596c0c8ab Remove function declarations from pin definition file 2020-04-25 11:47:11 -07:00
Reed Nightingale
91776fdcc7 Clean up pin descriptions a bit 2020-04-25 11:33:17 -07:00
Reed Nightingale
80494b62b9 Try a couple other fonts. None are great. 2020-04-21 21:56:51 -07:00
Reed Nightingale
98e62a6dca Switch to using a tiny but terrible looking font for size reasons 2020-04-21 21:40:02 -07:00
Reed Nightingale
40a13f1b5f Re-enable frequency entering 2020-04-21 21:38:34 -07:00
Reed Nightingale
1ee39ec535 Update includes, and disable numpad for space reasons for now 2020-04-21 21:19:47 -07:00
Reed Nightingale
cc013e5be4 stub out some functions that still need to be removed or refactored 2020-04-21 21:19:17 -07:00
Reed Nightingale
1766386411 Update pin names 2020-04-21 21:16:30 -07:00
Reed Nightingale
a2eae89733 Remove code that was moved to tuner, update includes and pin names 2020-04-21 21:16:10 -07:00
Reed Nightingale
1622d0ebe7 Update includes 2020-04-21 21:15:14 -07:00
Reed Nightingale
4a96027a07 Update includes 2020-04-21 21:14:42 -07:00
Reed Nightingale
67d34a3461 Update includes and pin names 2020-04-21 21:14:24 -07:00
Reed Nightingale
6face82c36 Update includes and pin names 2020-04-21 21:13:55 -07:00
Reed Nightingale
e593e0a060 Update includes 2020-04-21 21:13:33 -07:00
Reed Nightingale
5217858e23 Update includes and pin names 2020-04-21 21:13:14 -07:00
Reed Nightingale
363ea027f9 Update includes and pin names 2020-04-21 21:12:51 -07:00
Reed Nightingale
f5f914a2a9 Update includes 2020-04-21 21:12:02 -07:00
Reed Nightingale
ff4cc62e70 Update includes and pin names 2020-04-21 21:10:48 -07:00
Reed Nightingale
0091a1e17a Update includes, and make VFO switch explicit 2020-04-21 21:10:24 -07:00
Reed Nightingale
7d84ab744b Remove include 2020-04-21 21:09:55 -07:00
Reed Nightingale
b87ef7575a Move tuning functions to a single file 2020-04-21 21:09:21 -07:00
Reed Nightingale
f090a75221 Add dedicated scratch-space header so that we can track where these are still used 2020-04-21 21:08:51 -07:00
Reed Nightingale
f113551aa6 Use beginTransaction for spi, since it's surprisingly less additional memory than the setClockDivider call. Also, add SPI cleanup to early return case 2020-04-21 21:06:19 -07:00
Reed Nightingale
1e66731649 Remove unnecessary file 2020-04-12 17:45:06 -07:00
Reed Nightingale
b2e17548c2 Remove more redundant checks 2020-02-15 18:44:29 -08:00
Reed Nightingale
b116be8644 Remove redundant check 2020-02-15 18:43:10 -08:00
Reed Nightingale
3438bef4fb Clean up types a bit 2020-02-15 18:42:06 -08:00
Reed Nightingale
b909f4d652 Remove active delay 2020-02-15 17:10:27 -08:00
Reed Nightingale
f7229ebaf2 Save 406 bytes by refactoring the touch calibration routine 2020-02-15 17:07:18 -08:00
Reed Nightingale
6481812b4f Link in the setup menu. Exceeds available space :( 2020-02-15 15:31:14 -08:00
Reed Nightingale
cad3c3c57e Add an enterSubmenu routine to centralize that behavior 2020-02-15 15:27:48 -08:00
Reed Nightingale
98adba28ba Have a high level function in charge of finding the active submenu 2020-02-15 14:43:05 -08:00
Reed Nightingale
2bfa97687d Fix compiler errors 2020-02-15 14:29:23 -08:00
Reed Nightingale
0b96cfb1f7 Simplification of setting extraction, and setting menu running 2020-02-15 14:17:55 -08:00
Reed Nightingale
3dcbb8a0fe Small whitespace change 2020-02-15 14:14:44 -08:00
Reed Nightingale
e9e134079a Create a root menu for setup 2020-02-15 13:49:30 -08:00
Reed Nightingale
c8a90efee8 First pass at making menu selector run like a Menu_t 2020-02-15 13:49:09 -08:00
Reed Nightingale
72e4a71a4d First pass at splitting runSetting into Menu_t-compatible functions 2020-02-15 12:36:01 -08:00
Reed Nightingale
4582521f1b Play current frequency on entering 2020-02-14 19:28:58 -08:00
Reed Nightingale
bba125590b Change rendering of the input frequency 2020-02-14 19:16:11 -08:00
Reed Nightingale
f47c45194e Add actual menu running functions 2020-02-14 19:05:12 -08:00
Reed Nightingale
2f964b3ffb Remove unused function 2020-02-14 18:53:04 -08:00
Reed Nightingale
04560ef8b6 Add numpad functionality to main menu 2020-02-14 18:51:28 -08:00
Reed Nightingale
d42571f7a5 Start building out numpad menu 2020-02-14 18:50:57 -08:00
Reed Nightingale
4a0b0ec653 First pass at making numpad buttons 2020-02-14 18:50:26 -08:00
Reed Nightingale
2d3cc48b19 Play current frequency when VFO is selected 2020-02-12 22:18:02 -08:00
Reed Nightingale
423246f2b8 Add audio feedback for when entering/exiting bands 2020-02-11 23:36:43 -08:00
Reed Nightingale
5721e06a51 Instantly update tuning when changing modes 2020-02-11 23:31:14 -08:00
Reed Nightingale
23ecb4e4b2 Upcast to uint64 to avoid overflow 2020-02-11 23:24:37 -08:00
Reed Nightingale
77fffccb46 update band buttons when tuning 2020-02-11 23:10:06 -08:00
Reed Nightingale
405df5a757 Add method to update band buttons 2020-02-11 23:09:18 -08:00
Reed Nightingale
70a1025603 Fix progmem extraction 2020-02-11 22:59:44 -08:00
Reed Nightingale
4a27962a5b Save settings on VFO change or sideband change 2020-02-11 22:18:52 -08:00
Reed Nightingale
3b932d54b4 Actually activate buttons! 2020-02-11 22:17:30 -08:00
Reed Nightingale
c176604320 Use pointer passed in for work space 2020-02-11 22:15:46 -08:00
Reed Nightingale
2c20483931 Fix extraction in menu_utils 2020-02-11 22:01:07 -08:00
Reed Nightingale
6686f199b0 Unpack progmem fully 2020-02-11 21:45:26 -08:00
Reed Nightingale
2ff1d0d7e8 Fix types and expose some buttons 2020-02-11 21:44:40 -08:00
Reed Nightingale
c727d876eb Fix button drawing routine 2020-02-11 21:42:50 -08:00
Reed Nightingale
efd3bfc5ad Add color distinction to button drawing 2020-02-10 01:34:02 -08:00
Reed Nightingale
2722b4367a Update include annotation 2020-02-10 01:30:21 -08:00
Reed Nightingale
2826dfde56 First pass at button drawing implementation 2020-02-10 01:30:01 -08:00
Reed Nightingale
8239761a26 Move button declarations together, declaring rather than defining required functions 2020-02-10 01:17:47 -08:00
Reed Nightingale
25d5f3aaa9 Move most of the button stuff to a separate file 2020-02-10 01:02:03 -08:00
Reed Nightingale
4583288bfa Update functions to use new button array 2020-02-10 00:53:53 -08:00
Reed Nightingale
55096c9284 Fix most compiler errors 2020-02-10 00:53:17 -08:00
Reed Nightingale
8bd9d59e5a Displaying text should accept const char* 2020-02-10 00:51:58 -08:00
Reed Nightingale
2f9a0a3e04 Fix compile errors 2020-02-10 00:51:23 -08:00
Reed Nightingale
51288f1db3 Define types for formatFreq 2020-02-10 00:35:32 -08:00
Reed Nightingale
90d550a03a Prevent multi-include problems 2020-02-10 00:29:46 -08:00
Reed Nightingale
61b79ba06a Move formatFreq to utils 2020-02-10 00:16:15 -08:00
Reed Nightingale
5c0ecb087b Comment out submenus for now 2020-02-10 00:06:28 -08:00
Reed Nightingale
fa62b1136f Update morse playback to use new version of button 2020-02-10 00:06:11 -08:00
Reed Nightingale
1077b0bf84 First pass on main menu button conversion complete. Not expected to compile 2020-02-10 00:01:49 -08:00
Reed Nightingale
254b60d88f More button conversion 2020-02-09 23:52:02 -08:00
Reed Nightingale
1869e20cf0 Add some band utility functions for later use 2020-02-09 23:35:33 -08:00
Reed Nightingale
846c59e5cc Update more buttons 2020-02-09 23:32:22 -08:00
Reed Nightingale
dc5104324b Start converting buttons. Not expected to compile 2020-02-09 23:07:12 -08:00
Reed Nightingale
c4e9d3c156 Add function stub for drawing buttons 2020-02-09 23:06:44 -08:00
Reed Nightingale
6dacc470a2 Copy size of the actual object 2020-02-09 23:05:41 -08:00
Reed Nightingale
bc1e546ece const the size 2020-02-09 22:27:49 -08:00
Reed Nightingale
7a149a979d Add some comments to button and move char to the end 2020-02-09 22:19:54 -08:00
Reed Nightingale
bafe61d10a Update button struct to provide more functionality 2020-02-09 22:17:50 -08:00
Reed Nightingale
ecc3b9c1fb Add an init routine to the menu struct 2020-02-09 22:01:19 -08:00
Reed Nightingale
cc763a0dce Add endSelector util 2020-02-09 21:59:15 -08:00
Reed Nightingale
5c04e23504 Show puck on first selection 2020-02-09 21:45:51 -08:00
Reed Nightingale
9541c009be Extract progmem variables, and fix LIMITing on menu selection 2020-02-09 21:34:30 -08:00
Reed Nightingale
d4867ac35b Partial move of main menu buttons 2020-02-09 21:14:43 -08:00
Reed Nightingale
4483852742 Fix includes 2020-02-09 18:44:17 -08:00
Reed Nightingale
a92fdc7793 Update to use new touch functions 2020-02-09 18:41:47 -08:00
Reed Nightingale
26c670ce14 Include new point location 2020-02-09 18:41:26 -08:00
Reed Nightingale
947624518d Move touch functions to their own files 2020-02-09 18:41:13 -08:00
Reed Nightingale
72fc92b584 Add classes for button timing variables and point 2020-02-09 18:40:42 -08:00
Reed Nightingale
b29bd955ca Swap loop logic for new menu logic 2020-02-09 17:59:58 -08:00
Reed Nightingale
e50857f181 Have checkTouch support ButtonPress_e 2020-02-09 17:59:29 -08:00
Reed Nightingale
bd616caaba Have touch functions read to passed pointer 2020-02-09 17:58:45 -08:00
Reed Nightingale
d9667d6756 Add some debugging lines for now 2020-02-09 17:09:46 -08:00
Reed Nightingale
5e30551bf2 Move selector logic to menu_utils 2020-02-09 17:04:47 -08:00
Reed Nightingale
9e21faa6f1 morseText should take a const char* 2020-02-09 17:04:00 -08:00
Reed Nightingale
5a12a636be Make button aware of it's types 2020-02-09 17:03:37 -08:00
Reed Nightingale
79a37620c2 Play morse when selecting 2020-02-09 16:31:24 -08:00
Reed Nightingale
66a9d82308 Set size variable for main menu button array 2020-02-09 16:17:18 -08:00
Reed Nightingale
e42b21fdf3 Add touch button finding util 2020-02-09 16:16:57 -08:00
Reed Nightingale
49c5607dd3 Move menu utils to their own file 2020-02-09 16:01:52 -08:00
Reed Nightingale
e2249af826 Move color definitions to their own file 2020-02-09 15:56:43 -08:00
Reed Nightingale
5fb47187a6 Add definition for movePuck 2020-02-09 15:42:37 -08:00
Reed Nightingale
8faa9c58c7 Make function inputs const, and fix some compiler errors 2020-02-09 15:30:37 -08:00
Reed Nightingale
a7a2655f02 Give readTouch a standard type 2020-02-09 15:29:48 -08:00
Reed Nightingale
f42ad2bcd2 Move button definition 2020-02-09 15:28:46 -08:00
Reed Nightingale
21eb8978d5 Add selection 2020-02-09 14:52:21 -08:00
Reed Nightingale
801b1b08c1 Rough out the main menu 2020-02-09 14:10:32 -08:00
Reed Nightingale
b805761415 Start building a menu 'class' 2020-02-09 14:10:08 -08:00
Reed Nightingale
e35a9eecec Move LIMIT macro to utils 2020-02-08 14:59:06 -08:00
reedbn
1c60b8b63f
Merge pull request #11 from reedbn/cw-transmit-at-displayed-freq
Display TX (not RX) frequency in CW mode
2020-02-04 21:51:03 -08:00
reedbn
94aae2f289
Merge pull request #12 from reedbn/morse-menu
More work is probably needed here, but this is working better than nothing.
2020-02-04 21:50:16 -08:00
Reed Nightingale
225464c51a Remove TX offset in CW mode, and refactor frequency setting code so that it's (almost) all in one place. W0EB pointed out this issue, and it appears that it was discussed but never implemented in https://groups.io/g/BITX20/topic/20916360 2020-02-02 15:19:44 -08:00
Reed Nightingale
03406f62cc Merge branch 'master' into morse-menu 2020-01-30 20:25:30 -08:00
Reed Nightingale
bc5c39357f Fix inter-element timing 2020-01-30 20:23:17 -08:00
Reed Nightingale
d440be6c66 Rename INO file, so that it matches the repo name, and thus doesn't cause issues when checked out initially with default folder name 2020-01-29 21:44:46 -08:00
Reed Nightingale
13fa83d440 Update reported version 2020-01-28 20:54:02 -08:00
Reed Nightingale
0a880fe8a6 Merge master into morse-menu 2020-01-28 20:45:33 -08:00
reedbn
80454612c0
Merge pull request #10 from reedbn/text-justification
Text justification
2020-01-28 20:40:29 -08:00
Reed Nightingale
0ad20caa4b Merge master into text-justification 2020-01-28 20:34:23 -08:00
reedbn
87cf184fe6
Merge pull request #9 from reedbn/fixed-width-freqs
Fixed width freqs and replace MNU with gear symbol
2020-01-28 20:29:53 -08:00
Reed Nightingale
40c23919ed Add space between colon and frequency output 2020-01-28 20:26:34 -08:00
reedbn
fbc718b2ee
Merge pull request #8 from reedbn/cw-tx-issues
CW RX->TX responsiveness issues
2020-01-28 20:17:18 -08:00
Reed Nightingale
2d48b58461 Force a fix frequency width when possible 2020-01-28 02:31:14 -08:00
Reed Nightingale
6ddd46a853 Add spacers to keep frequency display a fixed width 2020-01-28 02:30:34 -08:00
Reed Nightingale
6313aa5adc Add two spacer characters 2020-01-28 02:29:37 -08:00
Reed Nightingale
1115441a57 Use gear icon for menu button 2020-01-28 01:52:31 -08:00
Reed Nightingale
04d836ab73 Removed unused function 2020-01-28 01:52:11 -08:00
Reed Nightingale
14d19ebbd8 Bigger settings icon 2020-01-28 01:42:59 -08:00
Reed Nightingale
015afb5cde First pass at menu icon 2020-01-28 01:25:19 -08:00
Reed Nightingale
21736fcc1d Forgot to add version files to the project. Silly me. 2020-01-27 23:11:32 -08:00
Reed Nightingale
03c159ba51 Render menus to the left, rather than center 2020-01-27 23:09:34 -08:00
Reed Nightingale
ceec8e2eb2 Push CW settings text to left, version to right 2020-01-27 23:09:05 -08:00
Reed Nightingale
0417907d03 Add justification option for text rendering 2020-01-27 23:08:32 -08:00
reedbn
c013281a31
Merge pull request #7 from reedbn/version-display
Add version string to bottom right corner
2020-01-27 22:38:54 -08:00
Reed Nightingale
13f4bdbe83 Add version string to bottom right corner 2020-01-27 22:36:01 -08:00
Reed Nightingale
8db6401bec Remove delay entirely. active_delay delays 10ms even when asked for 0 2020-01-27 21:51:34 -08:00
Reed Nightingale
e67885d8a0 disable delay before starting CW, allow straight key values through the paddle resistors, set correct timeout 2020-01-26 13:03:50 -08:00
reedbn
e35a2addf9
Merge pull request #6 from reedbn/band-handling-updates
Band handling updates
2020-01-26 11:28:55 -08:00
Reed Nightingale
95ea5b0ee2 Use unsigned ints for frequency 2020-01-26 11:26:18 -08:00
Reed Nightingale
fe1b8cfa34 Merge branch 'master' into band-handling-updates 2020-01-26 11:23:56 -08:00
Reed Nightingale
953a59f309 Don't play bools if morseMenu is off 2020-01-26 11:15:11 -08:00
Reed Nightingale
b0b11a93f0 Merge warning fixes, and fix 'new' warnings 2020-01-26 11:12:07 -08:00
reedbn
bbe34daff7
Merge pull request #5 from reedbn/warning-cleanup
Warning cleanup
2020-01-26 10:55:34 -08:00
Reed Nightingale
298bb479b5 Explicit cast to prevent integer overflow 2020-01-26 10:47:57 -08:00
Reed Nightingale
1505936713 Remove unused variable 2020-01-26 10:43:11 -08:00
Reed Nightingale
fe9f04d146 Remove unused static 2020-01-26 10:42:37 -08:00
Reed Nightingale
fea7b8d868 Move string to PROGMEM, and remove unused return value 2020-01-26 10:42:20 -08:00
Reed Nightingale
c38460576b A number of explicit casts, initializations, and type adjustments to avoid warnings 2020-01-26 10:40:59 -08:00
Reed Nightingale
6dd4b37d8a analogRead technically returns an int, but it only can be 0-1023, so practically it's an unsigned int. This matches types for the constants. Also removed cwAdcSTFrom, since the value will always be greater than 0 2020-01-26 10:40:03 -08:00
Reed Nightingale
f54320e8cb Match unsigned with unsigned 2020-01-26 10:39:01 -08:00
Reed Nightingale
b65e5eb90c un-name unused variable 2020-01-26 10:38:35 -08:00
Reed Nightingale
2ac24eb28d Minor whitespace adjustment, and remove unused variables 2020-01-26 10:22:20 -08:00
Reed Nightingale
e88ee1826e Remove unused variables and set default values for colors 2020-01-26 10:22:01 -08:00
Reed Nightingale
1175d1f7eb Add morseBool to main screen buttons 2020-01-25 16:32:04 -08:00
Reed Nightingale
9635d404b0 Add morseBool function 2020-01-25 16:31:32 -08:00
Reed Nightingale
c6b21bfa6d Instead of fast tuning, play back the current frequency upon selection 2020-01-25 15:54:47 -08:00
Reed Nightingale
68b85e6fa0 Add a brief pause before continuing to play the actual characters 2020-01-25 15:54:06 -08:00
Reed Nightingale
54e4d4bf06 When sideband mode is changed, immediately update tuning 2020-01-25 12:02:30 -08:00
Reed Nightingale
a8910e1ba2 Auto-update sideband mode based on keyed-in frequency 2020-01-25 11:57:04 -08:00
Reed Nightingale
0c575d222f Add a way to terminate playback 2020-01-25 11:27:58 -08:00
Reed Nightingale
6dae08e222 Merge branch 'master' into morse-menu 2020-01-25 11:13:12 -08:00
reedbn
20a4577ed2
Merge pull request #4 from reedbn/knob-acceleration
Knob acceleration
2020-01-25 11:12:42 -08:00
Reed Nightingale
1b8966103d Send audio for 'Exit' menu option correctly 2020-01-22 00:49:59 -08:00
Reed Nightingale
814b646ab0 Shuffle CW menu a bit, and reduce lots of menu name string lengths so that they play faster when in CW mode 2020-01-22 00:11:52 -08:00
Reed Nightingale
70153f77d2 Add morse menu playback to setup 2020-01-22 00:04:12 -08:00
Reed Nightingale
13df195efc Replace morse table with direct binary values so that there can't be mis-translations of the hex 2020-01-21 23:54:42 -08:00
Reed Nightingale
418acf644b Fix morse encoding for 'y' 2020-01-21 23:53:09 -08:00
Reed Nightingale
a9e73bb102 Instead of having long press of tuner enter menu, have it turn on morse menu readout mode instead 2020-01-21 23:19:00 -08:00
Reed Nightingale
d854a51184 Add start and end of transmission to morse list 2020-01-21 23:17:47 -08:00
Reed Nightingale
edbb17e4c4 Fix keypad frequency entry code 2020-01-21 22:59:48 -08:00
Reed Nightingale
1c099663fc Have morseLetter respect global playback setting 2020-01-21 22:07:11 -08:00
Reed Nightingale
b0193ca762 Add option to turn on and off morse menu playback 2020-01-21 22:06:37 -08:00
Reed Nightingale
88d16bb073 Add morse playback of main menu 2020-01-21 21:30:18 -08:00
Reed Nightingale
3087830bfb Expose single letter morse to other code areas 2020-01-21 21:21:50 -08:00
Reed Nightingale
bbdd6840c9 Reduce setting step size, since momentum can get places faster 2020-01-21 01:37:09 -08:00
Reed Nightingale
13f109c8b3 First pass at a momentum function 2020-01-21 01:13:36 -08:00
Reed Nightingale
4aea1b7126 Slightly change centering. Appears to ride a tiny bit high because it leaves room for below-line characters, like gjpqy 2020-01-19 22:54:42 -08:00
Reed Nightingale
755b2214f9 Add missing includes to compile 2020-01-19 22:53:00 -08:00
Reed Nightingale
9fb4efc5ac Move encoder functions to their own file 2020-01-19 22:49:59 -08:00
reedbn
59347af9ed
Merge pull request #3 from reedbn/menu-refactor
Menu refactor
2020-01-19 22:36:37 -08:00
Reed Nightingale
1f033b6af4 Save 102 progmem bytes by de-duping strings 2020-01-19 20:51:19 -08:00
Reed Nightingale
dd3a6162af Set global setting so that setFrequency will work correctly 2020-01-19 20:42:44 -08:00
Reed Nightingale
6d5fb97c62 Refactor reset-all setting 2020-01-19 20:35:43 -08:00
Reed Nightingale
d032664620 Re-order cw settings to match the menu 2020-01-19 20:21:51 -08:00
Reed Nightingale
850cda59e4 Refactor cw play speed setting 2020-01-19 20:20:03 -08:00
Reed Nightingale
ece69fff3a Let morseText play back at any desired rate, not just the default one 2020-01-19 20:15:01 -08:00
Reed Nightingale
4265d56d32 Remove unused setupCwTone 2020-01-19 19:50:54 -08:00
Reed Nightingale
9af0c1c965 Refactor keyer setting into new menu style 2020-01-19 19:50:11 -08:00
Reed Nightingale
dc1da20b13 Refactor cw switch delay 2020-01-19 19:40:03 -08:00
Reed Nightingale
58bfe55620 Move tone down to keep the menus in a somewhat orderly fashion 2020-01-19 19:26:31 -08:00
Reed Nightingale
b99e13eff4 Refactor BFO settings menu 2020-01-19 19:24:26 -08:00
Reed Nightingale
315d9348f0 Swap most string operations to bounded versions 2020-01-19 19:00:45 -08:00
Reed Nightingale
9b224699ee increase buffer b size to avoid heap smashing 2020-01-19 18:41:36 -08:00
Reed Nightingale
d0900aa392 Refactor local oscillator cal 2020-01-19 18:40:11 -08:00
Reed Nightingale
ec409d8805 Fix reset x for manual newlines 2020-01-19 13:05:02 -08:00
Reed Nightingale
a8e616637c Remove a bunch of debugging statements 2020-01-19 12:57:53 -08:00
Reed Nightingale
00f5d2691d Make text wrap work nicely 2020-01-19 12:56:47 -08:00
Reed Nightingale
e65f4d57a4 Implement the instructions text 2020-01-19 12:56:14 -08:00
Reed Nightingale
03fd8463eb Switch to named functions instead of lambdas, because apparently PROGMEM doesn't like lambdas 2020-01-19 10:48:07 -08:00
Reed Nightingale
8212ecf1e9 Proof of concept generic setting menu for cw tone 2020-01-18 23:34:41 -08:00
Reed Nightingale
f79ca7c693 Give a reset dialog so it can't be a single mistake to reset 2020-01-17 23:23:56 -08:00
Reed Nightingale
897b5c5de5 Add reset option 2020-01-17 23:10:53 -08:00
Reed Nightingale
e1d0ee1c43 If button is down, load defaults and go from there 2020-01-17 23:10:38 -08:00
Reed Nightingale
bc83bcd5d1 Move CW WPM and Hz to setup menu 2020-01-17 22:57:41 -08:00
Reed Nightingale
b100da145e Rename some of the menu items 2020-01-17 22:27:12 -08:00
Reed Nightingale
e0bc5999c8 De-duplicate keyer strings 2020-01-17 22:22:06 -08:00
Reed Nightingale
ebaf7c44de Do most of the refactoring required to make menus easy to expand 2020-01-17 21:41:52 -08:00
Reed Nightingale
f0e0640115 Swap tone button for menu button for faster dev 2020-01-17 19:05:16 -08:00
Reed Nightingale
6fe6185b19 Remove unused extern 2020-01-17 18:53:18 -08:00
reedbn
7b2057eabb
Merge pull request #2 from reedbn/one_stop_settings
Merging one stop settings branch
2020-01-17 18:47:28 -08:00
Reed Nightingale
692c862b6e Load actual setting, not just the defaulted 0 2020-01-17 00:16:31 -08:00
Reed Nightingale
c17136279e Fix .,? morse encodings, and use correct letter spacing based on settings, not hard coded 2020-01-16 23:36:58 -08:00
Reed Nightingale
79fd056af0 Rename oscillator variables so it's clear which is which not just from the math 2020-01-14 23:49:44 -08:00
Reed Nightingale
afeb056667 Move formatFreq to nano_gui so that both ubitx_ui and setup can use the single function 2020-01-14 23:33:11 -08:00
Reed Nightingale
d62ff59c90 Fix frequency display for VFOs 2020-01-14 23:27:42 -08:00
Reed Nightingale
9a2a25abe9 Fix rendering of BFO 2020-01-14 22:47:53 -08:00
Reed Nightingale
9a205522d9 Don't reset cals to fixed values when starting cal routine 2020-01-14 22:22:26 -08:00
Reed Nightingale
6e797adc70 Expand range of allowed sane frequencies to anything the tuner driver will actually tune 2020-01-14 22:07:24 -08:00
Reed Nightingale
2fc534f12a Merge master into one_stop_settings to get the interrupt encoders 2020-01-14 21:58:24 -08:00
reedbn
baf880c7ae
Merge pull request #1 from SmittyHalibut/pdq_gfx_update
Interrupt Encoder Update
2020-01-14 21:50:44 -08:00
Reed Nightingale
f6894e03f8 Add more debugging context (commented out) 2020-01-13 23:38:43 -08:00
Reed Nightingale
227cbe9b07 Load EEPROM settings immediately, and load defaults prior to loading EEPROM values so that we fall back on defaults rather than 0's 2020-01-13 23:38:12 -08:00
Reed Nightingale
f329950b6e Merge branch 'master' into one_stop_settings 2020-01-13 22:34:31 -08:00
Reed Nightingale
3598328918 Merge branch 'fix-morse-f' 2020-01-13 22:29:04 -08:00
Mark Smith
50a00ffb52 moved display update back to where it'll update on non-encoder-wheel changes. 2020-01-11 11:22:42 -08:00
Mark Smith
858205b4cd Fixed 255/-256 encoder bug. 2020-01-11 11:15:47 -08:00
Mark Smith
228b5d9589 Merged in @SmittyHalibut's interrupt driven encoder routines. 2020-01-11 11:00:44 -08:00
Reed Nightingale
44988f48fb Merge branch 'pdq_gfx_update' into one_stop_settings 2020-01-08 21:40:18 -08:00
Reed Nightingale
ef8036ee19 Fix 'F' morse code representation - currently is same as 'L'. First noticed by K7OME 2020-01-06 21:50:21 -08:00
Reed Nightingale
9a68846b58 Use correct version of strcat for flash strings 2020-01-04 22:51:29 -08:00
Reed Nightingale
b655704264 Update side band when band buttons are used 2020-01-04 02:53:33 -08:00
Reed Nightingale
a0abe466c0 Redraw VFO after mode change so that UI will reflect active setting 2020-01-04 02:52:56 -08:00
Reed Nightingale
4674230f51 Make more strings progmem. Probably should consolidate strings at some point so that we don't have lots of duplicate '.' and 'Hz' floating around 2020-01-04 02:41:06 -08:00
Reed Nightingale
a2109de63f Fix issue with RIT restore during VFO change 2020-01-04 02:14:21 -08:00
Reed Nightingale
073cfd4cb9 Merge pdq branch in again, with the RIT rendering fix 2020-01-04 02:08:51 -08:00
Reed Nightingale
8d34a3461b Fix ritToggle logic 2020-01-04 01:56:05 -08:00
Reed Nightingale
fbfb7c89d6 Fix merge issue 2020-01-04 01:50:17 -08:00
Reed Nightingale
4d7739d6dd Merge updated pdq_gfx_update branch into one_stop_settings 2020-01-04 01:45:35 -08:00
Reed Nightingale
4cefa08eaf Fix WPM calculations 2020-01-03 23:43:33 -08:00
Reed Nightingale
e9702bd955 Fix settings - some variables weren't being read quite right 2020-01-03 23:43:06 -08:00
Reed Nightingale
39168d663e Use settings in all files. Compiles, but untested 2020-01-03 23:11:55 -08:00
Reed Nightingale
0bf8424ab9 Get settings files compiling 2020-01-03 23:11:08 -08:00
Reed Nightingale
9dee71f5bd Offload settings to it's own set of files 2020-01-03 01:07:32 -08:00
73 changed files with 5313 additions and 3237 deletions

View File

@ -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)
};

View File

@ -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
View 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
View 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
View 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

View File

@ -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
View 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(&current_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
View 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
View 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
View 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
View File

@ -0,0 +1,7 @@
#pragma once
enum ButtonPress_e : uint8_t {
NotPressed,
ShortPress,
LongPress
};

5
button_timing.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
#pragma once
void enc_setup(void);
int enc_read(void);

232
keyer.cpp
View File

@ -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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const rootMenu;

637
menu_main_buttons.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const numpadMenu;

161
menu_numpad_buttons.cpp Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
#include "menu.h"
extern Menu_t* const quickListMenu;

137
menu_quicklist_buttons.cpp Normal file
View 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
View 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
View 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
View 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
View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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
View 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
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
struct Point {
int16_t x;
int16_t y;
};

34
push_button.cpp Normal file
View 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
View File

@ -0,0 +1,5 @@
#pragma once
#include "button_press_e.h"
ButtonPress_e CheckTunerButton();

13
scratch_space.h Normal file
View 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
View 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
View 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);

983
setup.cpp

File diff suppressed because it is too large Load Diff

9
setup.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "menu.h"
extern Menu_t* const setupMenu;
void setupTouch();
void runLocalOscSetting();
void runBfoSetting();

View File

@ -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
View 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

View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
#include "toneAC2/toneAC2.cpp"

139
touch.cpp Normal file
View 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
View 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
View 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
View 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
View File

@ -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

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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;

9
version.h Normal file
View 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 VERSION_STRING;