This is a PIC keyer I wrote several years ago that can be used as an outboard keyer, internal to an existing rig, or as a CW beacon. The code compiles for the Microchip 16F84, 16F84A, 16F628, 16F628A, and 16F648A running at 4 Mhz. (VE3VXO has also ported this code to the PIC18 series. Download here.) It also includes a frequency counter suitable for use with homebrew rigs. I no longer update this code as it’s been very stable and has all the features I needed at the time I developed it. I have a new CW keyer I have developed for the Arduino platform which is largely based on this original effort with the PIC.
Features include:
- 5 to 60 WPM speed
- Three memories: one for a callsign, and two longer memories, all non-volatile
- RF frequency counter
- Frequency-adjustable sidetone or on-off keying for an external sidetone oscillator
- Reverse paddle mode
- Random code practice generator
- Practice sending mode
- Iambic A and B modes
- Feature rich command mode
- Modular code design and compile-time selectable features
- 16F84, 16F84A, 16F628, 16F628A, 16F648A target devices (more possible due to modular code)
- Beacon mode for CW beacons
- Transmitter tuning mode
- Straight key mode
- Bug mode
- In circuit programming with a computer serial port and free software enables quick code updates or easy experimentation
Schematics
Complete Schematic
Quick and Dirty Schematic
( Ignore the outdated email address and web page URL on these schematics. These schematics were created with Eagle. Sorry, I do not have PCB artwork.)
Connections
-
Schematic Name Type Function DIT Input Connect to paddle; ground to activate dit (dot) keying DAH Input Connect to paddle; ground to activate dah (dash) keying TX_KEY Output This goes low (ground) on transmit and should be connected to the key input of the transmitter. ST_KEY Output Sidetone key. This goes high (+12 volts) to supply voltage to an external sidetone oscillator. SIDETONE Output Square wave sidetone output. Couple into an audio stage of the receiver. TX_WAKE Output This output is grounded when the transmitter should be turned on. QSK rigs don’t need this – this is intended more for beacon mode and could be used to turn off the rig to conserve power during idle times. If the key is used in beacon mode as a VHF “fox” for a foxhunt, this line can key transmit on an FM rig and the sidetone line can feed the transmit audio with appropriate attenuation. This line could also be used to key a T/R relay on non-QSK rigs. Programming Connector Input This can be connected to a serial port on a computer to do quick programming. Freq Count Input Frequency counter input. Connect to a VFO to use the frequency counter functionality.
Note that the ST_KEY line does not supply a square or sine wave sidetone but rather a voltage to key an external oscillator. For those who aren’t concerned with a pure sine wave sidetone and want to save parts, the SIDETONE line supplies a square wave signal suitable for coupling into a receiver audio stage. The square wave can be “softened” with a resistor and capacitor acting like a low pass filter if desired. I have also successfully used a capacitor and the winding of a junkbox audio transformer as a low pass filter with good results. The SIDETONE line with the coupling capacitor also can drive an 8 ohm speaker.
The 1N4148 diodes can be substituted with 1N914 diodes or most any general purpose diode.
Operation
Upon power up, the unit says “HI” in CW. This does not key the TX_KEY or TX_WAKE line. (To disable this, comment out #define include_needless_feature_code in the code inclusion section)
When the unit is initially turned on, it will default to a speed of 20 WPM. When the mode button is pressed, the unit goes into command mode. In this mode, you place commands by sending a letter in CW. Here are the commands:
-
Command Description Comments A Switch to Iambic A Mode B Switch to Iambic B Mode C CW Speed Calibration Check Expert Command D Disable sidetone during transmit Expert Command E Activate expert commands F Adjust sidetone frequency Expert Command H# Program Memory # K###X Program frequency counter offset Expert Command L Adjust frequency counter calibration Expert Command M# Play Memory # (Does not transmit) P Receive Code Practice Q Send Code Practice R Reverse paddles mode S CW speed adjust mode T Tuning mode U Bug mode V0 Deactive frequency counter offset Expert Command V1 Frequency counter reading = offset – measurement Expert Command V2 Frequency counter reading = measurement – offset Expert Command V3 Frequency counter reading = measurement + offset Expert Command W Weighting adjust mode X Exit command mode Y Announce frequency Z Beacon test mode Does not transmit ? Query CW speed , Toggle between three digit and eight digit frequency counter modes Expert command
If you send an unknown command, the PIC will send a question mark (?) in CW. When exiting command mode, the unit will emit a beep-boop (or a BK in CW if #define include_funky_tones_code is commented out). You can also exit command mode by hitting the mode button while in command mode.
A, B: Iambic A/B Modes
A and B commands switch between Iambic A and B modes. These commands are available if the code is compiled with #define include_iambic_b_code uncommented. If this code is not included, the keyer defaults to Iambic A behavior. (I’m not certain the iambic B code works well. Anyone who is familiar with iambic B, please test this and tell me what you think.)
C : Speed Calibration Check
This mode is used to verify or set the proper timing for CW sending. The keyer will send dits while the dit paddle is depressed. When released, the keyer will read back in CW the number of dits that were sent. The speed in words per minute equals the number of dots per minute divided by 25.
D: Disable Sidetone in Regular Mode
This turns the sidetone on and off in regular mode. It is intended for outboard keyer use with a transmitter that already has sidetone built in. In this instance, you would want to turn off the keyer sidetone during transmit so all you hear is the transmitter sidetone. During command mode, the keyer sidetone will continue to function.
F: Adjust Sidetone Frequency Mode
The keyer will turn on the sidetone. The dit paddle will increase the frequency and the dah paddle will lower it. Squeeze both paddles or hit the mode button to exit. The sidetone frequency is saved to EEPROM upon exit.
H#: Program Memory
# is the number of memory you want to program (0,1, or 2). After the units sounds a “boop beep”, use the paddle to program the memory with your CW. If you run out of space, the keyer will sound a continuous tone. To exit out of program mode, hit the mode switch. The transmitter is not keyed during programming. (requires #define include_m_and_h_cmd_code to be uncommented at compile time)
K###X: Program Frequency Counter Offset
This programs the offset for the the frequency counter (which is used only in three digit mode unders modes V1, V2, or V3). Subsitute ### with numbers. To terminate the command, send an X. A number up to 65,535 can be stored, but it’s likely you’ll only program a number from 000 to 1000. See details below for frequency counter operation and how to use the offset with your rig.
L: Frequency Counter Calibration Modes
This mode allows you to calibrate the frequency counter due to variations in PIC clock crystals. The frequency button can be pressed to announce the frequency in seven digit mode. The dit paddle increases the frequency readout, while the dah paddle decreases it. Hitting the mode button exits from the freuqnecy calibration mode. The calibration setting is non-volatile.
M#: Playback Memory
This simply plays back a memory without keying the transmitter. # = 0, 1, or 2 corresponding with the memory you want to play. (requires #define include_m_and_h_cmd_code to be uncommented at compile time)
P: CW Receive Practice Mode
This mode sends random cw in five character groups. Hit a button or paddle contact to exit. The transmitter is not keyed in this mode. ( #define include_cw_rx_practice_code must be uncommented at compile time to include this feature.)
Q: CW Send Practice Mode
This mode allows one to send code without transmitting. To exit, hit the mode button. ( #define include_cw_tx_practice_code must be uncommented at compile time to include this feature.)
R: Toggle Reverse Paddle Mode
This reverses the dit and dah contacts.
S: CW Speed Adjustment Mode
The keyer will send a string of dits. Pressing the dit paddle will increase the sending speed and the dah paddles decreases it. To exit speed mode, squeeze the paddles simultaneously or hit the mode button. Upon exiting speed mode, the keyer will send the speed in CW and save the setting to EEPROM. (Speed adjustment mode can also be activated by using the mode button hold shortcut below.)
T: Tuning Mode
The tuning mode is to be used in tuning up a rig or antenna tuner. Pressing the dit paddle will turn on the transmitter momentarily. Pressing the dah paddle will latch it on and off. To exit tuning mode, squeeze both paddles simultaneously, or hit the mode button. ( #define include_cw_tune_mode_code must be uncommented at compile time to include this feature.)
U: Bug Mode Toggle
This command turns bug mode on and off. In bug mode, the dit paddle works as normal, but the dah paddle acts like a straight key, emulating good old bug paddles for those folks who like them.
V0, V1, V2, V3: Frequency Counter Offset Mode
These commands configure the way the frequency that is announced in three digit mode is calculated.
V0 : Deactivate frequency counter offset
V1 : Frequency counter reading = offset – measurement
V2 : Frequency counter reading = measurement – offset
V3 : Frequency counter reading = measurement + offset
W: Adjust Weighting
In this mode, the weighting (the length ratio of dits to dahs, normally 1:3) can be adjusted. The dit contact decreases weighting and the dah contact increases it.
X: Exit Command Mode
Self explanatory.
Y: Readout Frequency
This command announces the current frequency in the current mode, either three or full seven/eight digits. The input frequency on pin 3 (RA4) is sampled for 250mS, and the internal resolution is 128 hertz. See the comma command below for more details.
#define include_freq_counter_code must be uncommented at compile time to include this code.
Z : Test Beacon Mode
This mode will play the beacon sequence once. Beacon mode is described further below.
? : Query Speed
The keyer sends in CW the current sending speed.
Comma : Frequency Counter Digits
This toggles the frequency counter between three digit and seven digit mode. In seven digit mode, all seven (or eight) counted digits are announced. In three digit mode only the 100, 10, and 1 kHz positions are announced with leading zeros truncated.
For example, in three digit mode:
7,040,000 Mhz would read 40
7,100,000 Mhz would read 100
7,000,000 Mhz would read 0
In seven digit mode:
7,040,000 would read 7040000
10,000,000 would read 10000000
Note that the resolution of the counter is 128 hertz, so often frequencies will not read exactly down to precisely zero hertz. In seven digit mode the offset (determined by the V1, V2, or V3 commands and the K command) is not applied. It is a straight reading.
Miscellaneous
If #define include_nnnnn_code is uncommented in the code, sending five Ns in a row will also put the keyer into command mode. I found this feature to be problematic and turn it off in production chips, but if you’re lacking a momentary button switch, this feature may be used to remove the need for that switch.
Memories and Stuff
Function 0 button plays the callsign memory (memory 0), function 1 button plays memory 1, and function 2 button plays memory 2. A single short press will play a memory once. Holding the button down for about a half second and releasing it will put the keyer into memory repeat mode. Pressing any memory key, the mode button, or either of the dit or dah contacts will exit from a memory playback, either in one shot or repeat mode.
To program a memory, hold down the mode button and press the memory button of the memory you want to program. After releasing both buttons the unit will beep and you can program the memory. To exit program memory mode, hit the mode button.
Memory programming can also be done in command mode using the H command shown above.
If an attempt is made to play an empty memory, the keyer will sound a low beep (or will send three dits if include_funky_beeps_code is commented out).
[ #define include_function_button_code must be uncommented at compilation time to include this functionality ]
Beacon
On power up, if the dit line is grounded, the unit will go into beacon mode. This mode is intended to be used with a transmitter to create a HF or VHF beacon. The sequence repeats continuously as follows:
VVV DE <callsign> /B <memory 1> VVV DE <callsign> <memory 2> VVV DE <callsign>
The unit will stay in beacon mode forever, until the power is turned off. The beacon sequence can be customized in code if you desire a different message.
[ #define include_beacon_code must be uncommented at compilation time to include this functionality ]
Straight Key Mode
If the dah pin is grounded (dah paddle is depressed) or mode button is pressed upon power up, the unit will going into straight key mode. In this mode, the dit line can be connected to a straight key and the keyer acts like a simple code oscillator. The mode button will also function as a straight key in this mode and is intended for emergencies if you forgot to bring your paddle along. The keyer will stay in straight key mode until the power is turned off.
Reset to Defaults
If both the dit and dah contacts are grounded upon power up, the unit will reset to defaults when the paddles are released. This will clear out memories and reset the CW speed to its default (20 wpm).
Mode Button Shortcuts
Holding the mode button and pressing a memory button will place the unit into program memory mode. Holding the mode button and hitting the dit or dah contacts will immediately adjust the sending speed.
Call CQ Feature
Holding the Memory 0 button and depressing dit will cause the keyer to call CQ using the callsign programmed in Memory 0. #define include_call_cq_code must be uncommented at compilation time to include this feature.
PIC Programming
I have used DL4YHF’s WinPic software for programming PICs and Microchip’s MPLAB software for compiling code for years, however the author has stopped maintaining the code. I’m in the process of switching over to WxPIC. I used to use David Tait’s Free FPP software . I can’t recall why I stopped using it, though it’s pretty old software now.
The DB-9 female connector on the schematic can be connected to the serial port on a computer for quick and easy programming. Using FPP, set the software for hardware “QandD (COM)” and set the appropriate COM port (usually COM1). Load the HEX file into FPP and click the program button. After closing S5, the programming switch, click OK in FPP. After about 40 seconds, the chip will be programmed. Open S5 and the chip should initialize. Note that this simple programming circuitry cannot be used to read the chip. I have not been able to get the serial port programming to work with WinPic.
If you have problems getting PICs to program correctly in FPP, try backing off the timing, in particular increasing “Prog Cycle Delay” and “Power Up Delay” in the Setup window.
At the time of this writing FPP cannot program the 16F648A, therefore I use PP06 with a printer (parallel) port programmer to program them. You can find several free parallel port programmer designs on the web. FPP, PP06, and WinPic will handle just about any programmer you build. I built one with junkbox parts (TTL 7404 chip type) without having to buy anything. Any hobbyist who buys a programmer these days is crazy! :-) Parallel port programmers can read the flash memory, too, which is good for verifying that your programming was successful, or for duplicating chips.
If you have issues with WinPic aborting programming due to errors, change the process priority of WinPic to “realtime” in Windows. This is done by right-clicking the task bar, selecting Task Manager, clicking the Processes tab, right-clicking the “WinPic.exe” process in the Image column, selecting Set Priority, Realtime. Click “OK” in the warning dialog box that comes up. Note that your cursor and your computer in general may become unresponsive when WinPic is programming a chip with the priority set to realtime as the operating system is giving WinPic all the CPU cycles it wants, right away.
Quick and Dirty Schematic
If you’re looking to build this quickly without all the bells and whistles, use the “Quick and Dirty” version above.
Recompile the code with the following code options commented out (place a semicolon in front of these lines):
#define include_function_button_code
#define include_freq_counter_code
#define include_freq_counter_button_code
Construction Notes
General
- Most any reasonable NPN transistor can be for all the 2N2222 transistors in this circuit. 1N914 diodes can be used in place of the 1N4148s.
Frequency Counter
- If you are installing this unit in a rig that has an IF that is a multiple of 4 Mhz, you may hear a beat note on the receiver caused by the PIC oscillator getting into the IF strip. If this occurs, reposition the PIC unit wiring to minimize the signal pickup. In one installation I was able to shift the PIC oscillator frequency out of the IF passband by removing C1 and C2. The oscillator and PIC still operated reliably, but your mileage may vary depending on the crystal. The 4 Mhz crystal undoubtedly could be replaced with a 3.57 Mhz one, although timing parameters in the code would need adjusted.
- A low input signal level frequency counter circuit is provided. This can be used to couple to a VFO in a rig and provide minimal VFO frequency pulling. Experiment with C17 and use the lowest value necessary for reliable operation. This will minimize the load on the VFO circuit.
- Adjust R19 so that the voltage on the collector of Q7 is around 2 to 2.5 volts with no signal.
Part Omissions
The following parts can be omitted if certain functionality is not required
-
Undesired Functionality Omit Parts Comments In circuit programming DB9, R12, R13, D1, S5 Connect R2 directly to +5V TX Wake line Q1, R8, C10 Only use is possibly in beacon mode External sidetone oscillator keying Q2, Q4, R6, R7, C6 Most people will use the wave sidetone Frequency Counter Q5, R15, C15, R14, S6, C16, D2, D3 Memories S2, S3, S4, C12, C13, C14 R9, R10, R11 can be removed as well if code is recompiled
I recommend buying parts from Mouser. I have always received good service from them and they have an excellent web interface. (I am not associated with Mouser other than being a satisfied customer.)
Source Code
There are various includes at the top of the code that can be commented out to omit certain features at compile time. I recommend buying 16F628 or 16F628A chips.
The 16F84 has only 1K of memory, so you’ll need to disable a lot of the features to make it fit on this chip. The entire code base does not fit on a 16F628, either, but you pretty much have to choose between beacon and code practice modes and the frequency counter. So, if you enable the frequency counter code, disable the beacon and code practice code, or vice versa, and it will fit.
Troubleshooting Hints
1. Verify that all of your voltages are correct. If something doesn’t look right, look for solder bridges between pins or pins that aren’t soldered at all.
2. Do you have the chip inserted into the socket correctly? Are any pins bent and not in the socket?
3. Verify that the crystal is oscillating. This can be done by connecting a high impedance oscilloscope or frequency counter to one of the pins of the crystal and observing a 4 Mhz signal. You may also be able to hear the 4 Mhz oscillator on a radio receiver (perhaps couple the crystal to the antenna with a 10 pF capacitor?) If your crystal is indeed a good crystal and you’ve double checked the wiring, but it still won’t oscillate, double check your configuration bits in your compiler and verify that you are not overriding the ones in code. The Oscillator (“OSC”) field should read “HS”.
4. Did you install all of the pull-up resistors needed for the particular functionality you compiled? If the function button code is included and you don’t have the pull up resistors for those pins on the chip installed, the lines will be low and the PIC will think you are pressing a button.
5. Did you compile for the right target chip? Did the compilation complete successfully? Are you using the right hex file on your hardrive? (Note that there are some pre-compiled files at the bottom of this page you can try.)
6. Verify the Configuration Bits in your compiler. You should not be overriding the configuration bits set in code.
Support
Unfortunately I no longer support new features for this code, however you can visit the Radio Artisan discussion group to discuss this project. (It’s been years since I touched assembly code and I’ve refocused my efforts on C programming on the Atmel AVR / Arduino platform. Sorry.)
Source Code
Source code and various compiled binaries, and the VE3VXO PIC18 port of this code can be downloaded here, or you can simply copy and paste the code below.
; Keyer 3.0.25 ; Goody K3NG ; 2008.12 ; This is a PIC based keyer. Currently supported devices are the 16F628, 16F628A, and 16F648A. This will compile for the ; 16F84 and 16F84A, however I don't test these anymore. The 16F628 doesn't cost much more, switch over to it! :-) ; ; http://anthony.good.googlepages.com ; anthony.good at gmail.com ; SOFTWARE LICENSE *************************************************************************************************************** ; ; This software is free to use and distribute freely, as long as you include this software license text. You may not use ; this software or a derivative work in a commercial product without the written permission of the author. (I will normally ; approve kitting requests.) ; ; You may not distribute, compile, alter, update, or upload onto a target device this software if you have ; ever in an Internet forum or on the air insulted, chastised, harassed, or belittled any hams because they have not taken a code ; test or do not wish to do so. ; ; If you disagree with this license, you can request a full refund of the price you paid for the source code from me. If you agree ; with this license, you also promise to show new hams at an appropriate opportunity the magic of CW, in a friendly and helping way. ; You also promise to use your knowledge of CW for good and not to increase the size of your ego or be condescending to your fellow ; amateur radio operators. ; ; The author assumes no responsibility for the use or misuse of this software, nor makes any warranty as to its fitness for any ; particular purpose. ; ; ******************************************************************************************************************************** ; TODO - code practice, random callsigns ; TODO - configurable memory repeat delay and store in eeprom ; TODO - RX mute line (use TX wake line) ; TODO - LCD Support ; TODO - DDS Support ; TODO - Configurable beacon sleep time ; TODO - include_beacon_fox_message ; IN PROGRESS IN THIS VERSION: AUX and RIT/XIT outputs. Halfway through enhanced memory support to support speed changes in memories ; 20080805 3.0.23 - fixed compile problem with include_weighting_code code ; 20080801 3.0.22 - Added big_memory_0 option ; ************************************** user configurable stuff ********************************************** ; processor definition - uncomment one and be sure to set your compiler for the right proc ;#define processor_16f84 ;#define processor_16f84a #define processor_16f628 ;#define processor_16f628a ;#define processor_16f648a ; *************************** ; * code inclusion * ; *************************** ; comment out these to reduce the code size or change features ; note that some modules of code depend on others; read the notes ; TODO: updated byte costs ; The Main Stuff #define include_wavesidetone_code ; cost: ? bytes - additional support of wavesidetone #define include_function_button_code ; cost: 160 bytes (includes some dependent code); support for buttons other than mode #define include_toggle_sidetone_on_off_code ; cost: 18 bytes - code to turn off sidetone during TX #define include_mode_button_hold_code ; cost: 43 bytes - code for mode button hold shortcuts (programming memories, quick speed adj) #define include_funky_beeps_code ; cost: 17 bytes - code for high and low beeps using wave sidetone; not applicable if using externally keyed sidetone oscillator (requires wavesidetone_code) ;#define include_m_and_h_cmd_code ; cost: 86 bytes - command modes M and H: program and playback memories in command mode ;#define include_eis_mh_cmd_code ; instead of 0,1,2 for memory select, it's e,i,s ; Frequency Counter Code #define include_freq_counter_code ; cost: 502 bytes - base frequency counter code #define include_freq_counter_button_code ; cost: 24 bytes - support for frequency counter button (requires freq_counter_code, function_button_code) ;#define include_freq_counter_calib_code ; cost: 56 bytes - code for frequency calibration mode, command L (reqs freq_counter_code, function_button_code) ; Beacon Code ;#define include_beacon_code ; cost: 60 bytes - beacon mode support ;#define include_beacon_play_mem_1_code ; cost: ?? bytes - play memory 1 in beacon cycle ;#define include_beacon_play_mem_2_code ; cost: ?? bytes - play memory 2 in beacon cycle ;#define include_beacon_sleep_time ;#define include_beacon_qrss_code ; cost: ?? bytes - adds a qrss beacon ID during each cycle ;#define include_beacon_dual_ant_code ; TODO cost: ?? bytes - line RB7 controls antenna relay ;#define include_beacon_dual_tx ; TODO cost: ?? bytes - line RA3 switches TX each beacon cycle ;#define include_beacon_power_ctrl ; TODO cost: ?? bytes - RA0 and RA1 control TX power ; Optional Features #define include_iambic_mode_code ; cost: 16 bytes - support for iambic a mode ;#define include_bug_code ; cost: 33 bytes - bug paddle mode #define include_call_cq_code ; cost: 55 bytes - call cq mode ;#define include_nnnnn_code ; cost: 23 bytes - this code allows you to go into command mode after five consecutive Ns are sent ;#define include_txwake_line_code ; cost: ? bytes - code for TX_WAKE line ;#define include_tune_mode_code ; cost: 70 bytes - code for tune mode ;#define include_weighting_code ; cost: 77 bytes - command mode W: dah weighting ;#define include_paddle_reverse_code ; cost: 39 bytes - command mode R: reverse paddles ;#define include_needless_feature_code ; cost: 31 bytes - unimportant stuff #define include_say_hi_code ; cost: ? bytes - say hi at startup ;#define include_calibration_code ; cost: 34 bytes - this adds another command mode for calibrating the speed counters ;#define include_cw_rx_practice_code ; cost: 113 bytes - command mode P: random five letter group code practice ;#define include_cw_tx_practice_code ; cost: 25 bytes - command mode Q: send code without transmitting ;#define big_memory_0 ; cost: 0 bytes - make memory 0 (callsign) larger (48 CW elements instead of 28) ; Experimental / Under Construction ;#define include_aux_line_code ; cost: ? - UNDER DEVELOPMENT ;#define enhanced_memory_support ; cost: ? bytes - UNDER DEVELOPMENT ;#define include_9850_dds_code ; experimental / under construction ;#define include_serial_cw_keyboard ; experimental ;#define include_beacon_serial_ctrl_code ; cost: ? bytes - control the beacon using a serial port ;#define include_fox_code ; under construction ; Debug code - turn all of these off for production chips ;#define include_debug_code ; misc debug code - comment out for production chips ;#define include_serial_rx_debug_code ;#define exclude_delays_debug ; ********************************************* ; * dependencies - do not change this section * ; ********************************************* ; include_serial_port_rx_code required by include_beacon_serial_ctrl_code, include_serial_cw_keyboard ifdef include_beacon_serial_ctrl_code #define include_serial_port_rx_code endif ifdef include_serial_cw_keyboard ifndef include_serial_port_rx_code #define include_serial_port_rx_code endif endif ; include_cw_table_long required by include_serial_port_rx_code, include_cw_rx_practice_code ifdef include_serial_port_rx_code #define include_cw_table_long endif ifdef include_cw_rx_practice_code ifndef include_cw_table_long #define include_cw_table_long endif endif ; include_rotateBCD012_left required by include_serial_port_rx_code, include_freq_counter_code ifdef include_serial_port_rx_code #define include_rotateBCD012_left endif ifdef include_freq_counter_code ifndef include_rotateBCD012_left #define include_rotateBCD012_left endif endif ; include_beacon_qrss_code requires include_beacon_code ifdef include_beacon_qrss_code ifndef include_beacon_code #define include_beacon_code endif endif ; include_aux_line_code requires include_function_button_code ifdef include_aux_line_code ifndef include_function_button_code #define include_function_button_code endif endif ; *************************** ; * various settings * ; *************************** #define _4_mhz_clock #define beacon_sleep_time_100ms d'1' ; the amount of time to wait in between beacon cycles, unit 100mS #define beacon_qrss_speed d'1' ; beacon qrss id cw wpm #define beacon_qrss_cycles d'3' ; qrss id after this many beacon cyles #define initial_speed_wpm d'20' ; this is the default wpm speed when the cw_unit it first powered up #define maxcwspeed d'40' ; this is the max cw speed the user can increment to #define initial_memory_repeat_delay d'35' ; this time in between repeating a memory in units of 100mS ; *************************** ; * chip pin aliases * ; *************************** #define mode_switch PORTB,0x00 ; momentary switch ; PORTB,0x01 ; Serial Port RX #define dit PORTB,0x02 ; dit of paddle #define dah PORTB,0x03 ; dah of paddle ifndef include_wavesidetone_code #define sidetone PORTB,0x04 ; keys external sidetone oscillator else #define wavesidetone PORTB,0x04 ; square wave sidetone output endif #define key PORTB,0x05 ; transmitter cw key ifdef include_txwake_line_code #define txwake PORTB,0x06 ; goes high to turn on TX if used as FM CW unit, or T/R relay endif ifdef include_function_button_code #define switch0 PORTA,0x00 #define switch1 PORTA,0x01 #define switch2 PORTA,0x02 endif ifdef include_aux_line_code #define aux PORTB,0x07 endif ; *************** don't adjust anything below this line unless you know what the hell you are doing ************* ; eeprom addresses - this defines eeprom memory locations #define eeprom_addr_speed_wpm 0x00 ; CW speed in binary format #define eeprom_wavesidetone_setting 0x01 ; sidetone frequency setting #define eeprom_settings1_location 0x02 ; various user settings, register eeprom_settings1 #define eeprom_freq_calib_high 0x03 ; frequency counter calibration, high byte #define eeprom_freq_calib_low 0x04 ; frequency counter calibration, low byte #define eeprom_freq_offset_high 0x05 ; frequency counter offset, lower nibble is BCD2 #define eeprom_freq_offset_low 0x06 ; frequency counter offset, upper nibble is BCD1, lower nibble is BCD0 ; first byte in memory location is a count of cw units stored in that memory ; the remaining bytes are the stored cw units ; a cw unit is two bits and is read MSB to LSB ifdef big_memory_0 ;--------------------- #define eeprom_memory_location_0 0x07 ; cw memory 0 ( callsign ) #define eeprom_memory_loc_0_cw_units d'48' ifdef processor_16f84 ; Eeprom space for CW memories: 0x07 to 0x3F #define eeprom_memory_location_1 0x14 ; cw memory 1 #define eeprom_memory_location_2 0x2A ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'84' ; four cw units can be stored per byte #define eeprom_memory_loc_2_cw_units d'84' #define processor_low_end endif ;processor_16f84 ifdef processor_16f84a #define eeprom_memory_location_1 0x14 ; cw memory 1 #define eeprom_memory_location_2 0x2A ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'84' ; four cw units can be stored per byte #define eeprom_memory_loc_2_cw_units d'84' #define processor_low_end endif ;processor_16f84a ifdef processor_16f628 ;eeprom space for CW memories: 0x07 to 0x7F #define eeprom_memory_location_1 0x14 ; cw memory 1 #define eeprom_memory_location_2 0x4A ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'212' #define eeprom_memory_loc_2_cw_units d'212' endif ;processor_16f628 ifdef processor_16f628a #define eeprom_memory_location_1 0x14 ; cw memory 1 #define eeprom_memory_location_2 0x4A ; cw memory 2 ( = eeprom_memory_location_1 + (eeprom_memory_loc_1_cw_units / 4) + 1 ) #define eeprom_memory_loc_1_cw_units d'212' #define eeprom_memory_loc_2_cw_units d'212' endif ;processor_16f628a ifdef processor_16f648a #define eeprom_memory_location_1 0x14 ; cw memory 1 #define eeprom_memory_location_2 0x4A ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'212' #define eeprom_memory_loc_2_cw_units d'212' endif ;processor_16f648a else ;big_memory_0 -------------------- #define eeprom_memory_location_0 0x07 ; cw memory 0 ( callsign ) #define eeprom_memory_loc_0_cw_units d'28' ifdef processor_16f84 #define eeprom_memory_location_1 0x0F ; cw memory 1 #define eeprom_memory_location_2 0x28 ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'96' ; four cw units can be stored per byte #define eeprom_memory_loc_2_cw_units d'92' #define processor_low_end endif ;processor_16f84 ifdef processor_16f84a #define eeprom_memory_location_1 0x0F ; cw memory 1 #define eeprom_memory_location_2 0x28 ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'96' ; four cw units can be stored per byte #define eeprom_memory_loc_2_cw_units d'92' #define processor_low_end endif ;processor_16f84a ifdef processor_16f628 #define eeprom_memory_location_1 0x0F ; cw memory 1 #define eeprom_memory_location_2 0x48 ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'224' #define eeprom_memory_loc_2_cw_units d'220' endif ;processor_16f628 ifdef processor_16f628a #define eeprom_memory_location_1 0x0F ; cw memory 1 #define eeprom_memory_location_2 0x48 ; cw memory 2 ( = eeprom_memory_location_1 + (eeprom_memory_loc_1_cw_units / 4) + 1 ) #define eeprom_memory_loc_1_cw_units d'224' #define eeprom_memory_loc_2_cw_units d'220' endif ;processor_16f628a ifdef processor_16f648a #define eeprom_memory_location_1 0x0F ; cw memory 1 #define eeprom_memory_location_2 0x48 ; cw memory 2 #define eeprom_memory_loc_1_cw_units d'224' #define eeprom_memory_loc_2_cw_units d'220' endif ;processor_16f648a endif ;big_memory_0 ; timing calibrations - these must be adjusted for clock speed ifdef _4_mhz_clock #define _100ms_counter1_calibration 0xff #define _100ms_counter2_calibration 0x78 #define _100ms_counter3_calibration 0x01 #define _cw_unit_wpm_factor d'20000' ; this adjustment control the time length of one CW unit ( a dit ) #define _beep_length d'20' #define _high_beep_tone d'20' #define _low_beep_tone d'55' #define _initial_wavesidetone_setting d'34' ; this is the default wavesidetone frequency (600Hz) #define _frequency_counter_calib d'48900' ; higher = shorter sampling duration (1/4 second) ; delay = 15 * 1uS * (65535 - _frequency_counter_calib) #define _txwake_delay_time d'30000' #define _txwake_delay_time_straight_key d'400' endif ;_4_mhz_clock ; processor declarations ifdef processor_16f84 processor 16f84 include <p16f84.inc> __config _HS_OSC & _WDT_OFF #define cblock_start 0x0c errorlevel -312 ; turn off nagging about device not needing page selection endif ;processor_16f84 ifdef processor_16f84a processor 16f84a include <p16f84a.inc> __config _HS_OSC & _WDT_OFF #define cblock_start 0x0c errorlevel -312 ; turn off nagging about device not needing page selection endif ;processor_16f84a ifdef processor_16f628 processor 16f628 include <p16f628.inc> __config _HS_OSC & _WDT_OFF & _LVP_OFF & _BODEN_OFF #define cblock_start 0x20 errorlevel -312 ; turn off nagging about device not needing page selection endif ;processor_16f628 ifdef processor_16f628a processor 16f628a include <p16f628a.inc> __config _HS_OSC & _WDT_OFF & _LVP_OFF & _BODEN_OFF #define cblock_start 0x20 errorlevel -312 ; turn off nagging about device not needing page selection endif ;processor_16f628a ifdef processor_16f648a processor 16f648a include <p16f648a.inc> __config _HS_OSC & _WDT_OFF & _LVP_OFF & _BODEN_OFF #define cblock_start 0x20 #define multi_memory_page_support endif ;processor_16f648a ; define registers cblock cblock_start speed_wpm:1 ;global bit_stuff1:1 ;global bit_stuff2:1 ;global bit_stuff3:1 ;global cw_unit_counter:2 ;loop_cw_unit counter1:1 ;pause_100_ms, loop_cw_unit counter2:1 ;pause_100_ms, loop_cw_unit counter3:1 ;pause_100_ms, send_cw cw_char_to_send:2 ;send_cw(parm) temp:1 ;send_cw dividend:2 ;divideby16bit divisor:2 ;divideby16bit quotient:2 ;divideby16bit remainder:2 ;divideby16bit count:1 ;divideby16bit binary2bcd_binary_in:2 ;binary2bcd BCD0:1 ;binary2bcd BCD1:1 ;binary2bcd BCD2:1 ;binary2bcd bcd_low_nibble:1 ;speed_mode bcd_high_nibble:1 ;speed_mode write_verify_data:1 ;write_eeprom write_verify_addr:1 ;write_eeprom command_mode_loop_count1:1 ; check_mode_button (enhanced) command_mode_loop_count2:1 ; check_mode_button (enhanced) command_mode_loop_count3:1 ; check_mode_button (enhanced) command_mode_buffer1:1 ; check_mode_button (enhanced) command_mode_buffer2:1 ; check_mode_button (enhanced) recording_loop_counter1:1 ; program_memory recording_loop_counter2:1 ; program_memory cw_unit_count:1 ; program_memory temp_memory:1 ; program_memory temp_memory_eeprom_pointer:1 ;program_memory temp_memory_mask:1 ; program_memory temp_w:1 ; program_memory.store_temp_memory_to_eeprom send_cw_temp:1 ; send_cw memory_number:1 ; program_memory, play_memory eeprom_memory_locations:3 ; program_memory, play_memory eeprom_memory_loc_limits:3 ; program_memory start_of_memory_location:1 ; program_memory, play_memory porta_temp:1 ; check_function_buttons eeprom_settings1:1 wavesidetone_counter wavesidetone_counter_setting endc ifdef enhanced_memory_support cblock cw_memory_element_count:1 cw_element_number:1 memory_location_element_number:1 cw_character:1 endc endif ;enhanced_memory_support ifdef include_txwake_line_code cblock txwakecounter:2 endc endif ;include_txwake_line_code ifdef include_nnnnn_code cblock ditdah_history1:1 ; global - include_nnnnn_code ditdah_history2:1 ; global - include_nnnnn_code ditdah_history_timer1:1 ; global - include_nnnnn_code endc endif ifdef include_weighting_code cblock cw_unit_counter_dah:2 cw_unit_counter_dit:2 speed_wpm_dah:1 endc ifdef include_beacon_qrss_code cblock speed_wpm_dah_beacon_temp:1 endc endif ;include_beacon_qrss_code endif ;include_weighting_code ifdef include_cw_rx_practice_code cblock randomnum:2 endc endif ifdef include_freq_counter_code cblock freqcount:3 BCD3:1 frequency_counter_calib:2 freq_offset_binary:2 L_temp:1 H_temp:1 endc bcd2binary_binary_out equ binary2bcd_binary_in ifdef include_debug_code bcd2binary_binary_out_2 equ binary2bcd_binary_in+1 ; for debugger work freq_offset_binary_2 equ freq_offset_binary+1 ; for debugger work endif ;include_debug_code endif ;include_freq_counter_code cblock beacon_cycle:1 endc ifdef include_beacon_qrss_code endif ;include_beacon_qrss_code #define dit_buffer bit_stuff1,0x00 ; used to store a dit in iambic operation #define dah_buffer bit_stuff1,0x01 ; used to store a dah #define key_tx_active bit_stuff1,0x02 ; 0 = do not turn on key_tx line when sending cw #define sending_dit bit_stuff1,0x03 ; 1 = send dit in send_dit_or_dah ifdef include_tune_mode_code #define tune_latch_mode bit_stuff1,0x04 ; 1 = tx is latched on endif #define temp_memory_dirty bit_stuff1,0x05 ; used by memory record functions #define paddle_was_hit bit_stuff1,0x06 ifdef include_function_button_code #define memory_playback_manual_exit bit_stuff1,0x07 endif #define send_cw_preemptible bit_stuff2,0x00 ; send_cw routine can be interrupted by user when this flag is set ifndef include_wavesidetone_code #define wavesidetone bit_stuff2,0x01 else #define sidetone bit_stuff2,0x01 endif #define expert_commands_on bit_stuff2,0x02 ; 1 = expert commands have been activated in command mode #define bit_temp bit_stuff2,0x03 ifdef include_freq_counter_code #define command_mode_v_cmd bit_stuff2,0x04 #define command_mode_k_cmd bit_stuff2,0x05 endif ifdef include_call_cq_code #define dit_hit bit_stuff2,0x06 endif ;include_call_cq_code ifdef include_m_and_h_cmd_code #define command_mode_m_cmd bit_stuff2,0x07 #define command_mode_h_cmd bit_stuff3,0x00 endif ;include_m_and_h_cmd_code #define squeeze_detected bit_stuff3,0x01 #define in_beacon_mode bit_stuff3,0x02 ;eeprom_settings1 is used for settings that need to be stored in nonvolatile memory ;they are written to eeprom using call write_eeprom_settings1 #define sidetone_off_during_tx eeprom_settings1,0x00 ; 1 = no sidetone during TX (for internal use in rigs that alread have sidetone) ifdef include_iambic_mode_code #define iambic_b_mode eeprom_settings1,0x01 ; 1 = iambic b mode activated endif ifdef include_paddle_reverse_code #define paddle_reverse eeprom_settings1,0x02 ; 1 = paddle reverse mode activated endif ifdef include_freq_counter_code #define eight_digit_mode eeprom_settings1,0x03 ; 1 = 8 digit frequency counter mode #define offset0 eeprom_settings1,0x04 ; offset0 and 1 stores frequency counter offset mode (table below) #define offset1 eeprom_settings1,0x05 endif ifdef include_bug_code #define bug_mode_on eeprom_settings1,0x06 ; 1 = bug mode activated endif ; frequency counter offset table ; offset0 offset1 ; 0 0 no offset ; 0 1 reading = offset - measurement ; 1 0 reading = measurement - offset ; 1 1 reading = measurement + offset org 0x00 pagesel main_program_start errorlevel -306 goto main_program_start errorlevel +306 ; Tables ------------------------------------------------------------- cw_table ; this is a lookup table for sending numbers in CW using send_cw routine ; 00 = termination ; 01 = dit ; 11 = dah ; stream goes MSB -> LSB (MSB first) ; two bytes long addwf PCL, F retlw b'11111111' ;0 0 retlw b'11000000' retlw b'01111111' ;1 1 retlw b'11000000' retlw b'01011111' ;2 2 retlw b'11000000' retlw b'01010111' ;3 3 retlw b'11000000' retlw b'01010101' ;4 4 retlw b'11000000' retlw b'01010101' ;5 5 retlw b'01000000' retlw b'11010101' ;6 6 retlw b'01000000' retlw b'11110101' ;7 7 retlw b'01000000' retlw b'11111101' ;8 8 retlw b'01000000' retlw b'11111111' ;9 9 retlw b'01000000' ifdef include_cw_table_long retlw b'01110000' ;A 10 retlw b'00000000' retlw b'11010101' ;B 11 retlw b'00000000' retlw b'11011101' ;C 12 retlw b'00000000' retlw b'11010100' ;D 13 retlw b'00000000' retlw b'01000000' ;E 14 retlw b'00000000' retlw b'01011101' ;F 15 retlw b'00000000' retlw b'11110100' ;G 16 retlw b'00000000' retlw b'01010101' ;H 17 retlw b'00000000' retlw b'01010000' ;I 18 retlw b'00000000' retlw b'01111111' ;J 19 retlw b'00000000' retlw b'11011100' ;K 20 retlw b'00000000' retlw b'01110101' ;L 21 retlw b'00000000' retlw b'11110000' ;M 22 retlw b'00000000' retlw b'11010000' ;N 23 retlw b'00000000' retlw b'01111101' ;P 24 retlw b'00000000' retlw b'11110111' ;Q 25 retlw b'00000000' retlw b'01110100' ;R 26 retlw b'00000000' retlw b'01010100' ;S 27 retlw b'00000000' retlw b'11000000' ;T 28 retlw b'00000000' retlw b'01011100' ;U 29 retlw b'00000000' retlw b'01010111' ;V 30 retlw b'00000000' retlw b'01111100' ;W 31 retlw b'00000000' retlw b'11010111' ;X 32 retlw b'00000000' retlw b'11011111' ;Y 33 retlw b'00000000' retlw b'11110101' ;Z 34 retlw b'00000000' endif ;include_cw_table_long ; Subroutines ------------------------------------------------------------- check_paddles banksel PORTA pagesel $ ifdef include_paddle_reverse_code btfsc paddle_reverse goto check_paddles_reverse endif ;include_paddle_reverse_code btfss dit ; if dit is not keyed, skip next bsf dit_buffer ; set dit_buffer btfss dah ; if dah is not keyed, skip next bsf dah_buffer ; set dah_buffer goto squeeze_check ifdef include_paddle_reverse_code check_paddles_reverse btfss dit ; if dit is not keyed, skip next bsf dah_buffer ; set dah_buffer btfss dah ; if dah is not keyed, skip next bsf dit_buffer ; set dit_buffer endif ;include_paddle_reverse_code squeeze_check ifdef include_iambic_mode_code btfsc iambic_b_mode ; don't do squeeze check if we're in iambic b mode return endif ;include_iambic_mode_code btfsc dit ; if both dit and dah are squeezed, set the squeeze_detected flag return btfsc dah return bsf squeeze_detected return ; ------------------ send_w_BCD_in_cw ; this sends the lower nibble of w (BCD binary coded decimal) in CW movwf temp_memory addwf temp_memory, F ; double it so we're in the right spot in cw_table movfw temp_memory ; put it in W for lookup table call cw_table movwf cw_char_to_send+0 movfw temp_memory addlw 0x01 ; add 1 to get next byte in table call cw_table movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 return ; ------------------ ifdef include_funky_beeps_code beep movwf temp_w ; store w temporarily movfw wavesidetone_counter_setting ; store real wavesidetone setting in temp_memory movwf temp_memory movfw temp_w ; get temp_w and put in for wavesidetone setting movwf wavesidetone_counter_setting movlw _beep_length movwf counter1 movwf counter2 bsf sidetone beep_loop call produce_wavesidetone decfsz counter1, F goto beep_loop decfsz counter2, F goto beep_loop bcf sidetone movfw temp_memory ; restore real wavesidetone setting movwf wavesidetone_counter_setting return ; ------------------ high_beep ifndef exclude_delays_debug movlw _high_beep_tone call beep endif ;exclude_delays_debug return ; ------------------ low_beep ifndef exclude_delays_debug movlw _low_beep_tone call beep endif ;exclude_delays_debug return ; ------------------ beep_boop ifndef exclude_delays_debug call high_beep call low_beep endif ;exclude_delays_debug return ; ------------------ boop_beep call low_beep call high_beep return endif ;include_funky_beeps_code ; ------------------ divide16bit ; dividend / divisor = quotient ; this code from Mark Sullivan www.markworld.com (thanks for saving me hours of work ! :-) banksel PORTA pagesel $ movlw d'16' movwf count clrf quotient+0 ;quotient clrf quotient+1 clrf remainder+0 ;remainder clrf remainder+1 udiv1 bcf STATUS,C rlf dividend+1,F rlf dividend+0,F rlf remainder+1,F rlf remainder+0,F movf divisor+0,W subwf remainder+0,W btfss STATUS,Z goto udiv2 movf divisor+1,W subwf remainder+1,W btfss STATUS,C goto udiv4 movwf remainder+1 movf divisor+0,W subwf remainder+0,F goto udiv3 udiv2 btfss STATUS,C goto udiv4 movf divisor+1,W subwf remainder+1,F btfss STATUS,C decf remainder+0,F movf divisor+0,W subwf remainder+0,F udiv3 bsf STATUS,C udiv4 rlf quotient+1,F rlf quotient+0,F decfsz count,F goto udiv1 retlw 0 ; ------------------ ifdef include_freq_counter_code bcd2binary ; BCD to 16 bit binary converter ; adapted from Microchip AN526 ; http://www.microchip.com ; thanks, Microchip ! ;input parameters: ;bcd2binary+0 : MSB ;bcd2binary+1 : LSB ;output: ;BCD0 = most significant digits ;BCD1 ;BCD2 = least significant digits ;each BCD register contains and upper and lower nibble BCD digit banksel PORTA pagesel $ clrf bcd2binary_binary_out+0 movf BCD0,W andlw 0x0F movwf bcd2binary_binary_out+1 call mpy10a ; result = 10a+b swapf BCD1,W call mpy10b ; result = 10[10a+b] movf BCD1,W call mpy10b ; result = 10[10[10a+b]+c] swapf BCD2,W call mpy10b ; result = 10[10[10[10a+b]+c]+d] movf BCD2,W andlw 0x0F addwf bcd2binary_binary_out+1, F btfsc STATUS,C incf bcd2binary_binary_out+0, F ; result = 10[10[10[10a+b]+c]+d]+e return ; BCD to binary conversion done ; subroutines mpy10b andlw 0x0F addwf bcd2binary_binary_out+1, F btfsc STATUS,C incf bcd2binary_binary_out+0, F mpy10a bcf STATUS,C ; multiply by 2 rlf bcd2binary_binary_out+1,W movwf L_temp rlf bcd2binary_binary_out+0,W ; (H_temp,L_temp) = 2*N movwf H_temp bcf STATUS,C ; multiply by 2 rlf bcd2binary_binary_out+1, F rlf bcd2binary_binary_out+0, F bcf STATUS,C ; multiply by 2 rlf bcd2binary_binary_out+1, F rlf bcd2binary_binary_out+0, F bcf STATUS,C ; multiply by 2 rlf bcd2binary_binary_out+1, F rlf bcd2binary_binary_out+0, F ; (bcd2binary_binary_out+0,bcd2binary_binary_out+1) = 8*N movf L_temp,W addwf bcd2binary_binary_out+1, F btfsc STATUS,C incf bcd2binary_binary_out+0, F movf H_temp,W addwf bcd2binary_binary_out+0, F return ; (bcd2binary_binary_out+0,bcd2binary_binary_out+1) = 10*N endif ;include_freq_counter_code ; ------------------ binary2bcd ; 16 bit binary to bcd converter ; adapted from Microchip AN526 ; http://www.microchip.com ; thanks, Microchip ! banksel PORTA pagesel $ bcf STATUS, C ; clear the carry bit movlw .16 movwf count clrf BCD0 ; most significant digit clrf BCD1 clrf BCD2 ; least significant digit loop16 rlf binary2bcd_binary_in+1, F ; LSB rlf binary2bcd_binary_in+0, F ; MSB rlf BCD2, F rlf BCD1, F rlf BCD0, F decfsz count, F goto adjDEC retlw 0 adjDEC movlw BCD2 movwf FSR call adjBCD movlw BCD1 movwf FSR call adjBCD movlw BCD0 movwf FSR call adjBCD goto loop16 adjBCD movlw 3 addwf INDF, W movwf temp btfsc temp,3 ; test if result > 7 movwf INDF movlw 30 addwf INDF, W movwf temp btfsc temp,7 ; test if result > 7 movwf INDF ; save as MSD retlw 0 ; ------------------ write_eeprom pagesel $ ; save EEDATA and EEADR for write verify errorlevel -302 banksel EEDATA movfw EEDATA banksel PORTA movwf write_verify_data banksel EEADR movfw EEADR banksel PORTA movwf write_verify_addr errorlevel +302 start_write errorlevel -302 banksel INTCON bcf INTCON, GIE ; disable interrupts banksel EECON1 bsf EECON1, WREN ; enable write movlw 0x55 ; required write sequence movwf EECON2 ; required write sequence movlw 0xaa ; required write sequence movwf EECON2 ; required write sequence bsf EECON1,WR ; write wait_to_complete_write btfsc EECON1, WR ; watch for WR flag to clear goto wait_to_complete_write ; check if write failed, loop a couple times to try again banksel write_verify_addr movfw write_verify_addr ; select the address banksel EEADR movwf EEADR banksel EECON1 bsf EECON1, RD ; read it banksel EEDATA movfw EEDATA ; get the data banksel PORTA subwf write_verify_data, W ; compare it btfss STATUS, Z ; skip next if its equal goto write_again ; otherwise, write it again write_eeprom_blowout banksel INTCON bsf INTCON, GIE ; enable interrupts banksel EECON1 bcf EECON1, WREN ; clear write enable flag bcf EECON1, EEIF ; clear EE Interrupt flag for the hell of it banksel PORTA return write_again banksel write_verify_data movfw write_verify_data ; reload EEDATA and EEADR with the original values banksel EEDATA movwf EEDATA banksel write_verify_addr movfw write_verify_addr banksel EEADR movwf EEADR banksel PORTA goto start_write errorlevel -302 ; ------------------ calculate_cw_unit_values banksel PORTA pagesel $ movlw LOW _cw_unit_wpm_factor movwf dividend+1 movlw HIGH _cw_unit_wpm_factor movwf dividend+0 movfw speed_wpm movwf divisor+1 ; lsb - divisor movlw 0x00 movwf divisor+0 ; msb - divisor call divide16bit movf quotient+1, W ;load LSB of result movwf cw_unit_counter+1 movf quotient+0, W ;load MSB of result movwf cw_unit_counter+0 ifdef include_weighting_code movlw LOW _cw_unit_wpm_factor movwf dividend+1 movlw HIGH _cw_unit_wpm_factor movwf dividend+0 movfw speed_wpm_dah movwf divisor+1 ; lsb - divisor movlw 0x00 movwf divisor+0 ; msb - divisor call divide16bit movf quotient+1, W ;load LSB of result movwf cw_unit_counter_dah+1 movf quotient+0, W ;load MSB of result movwf cw_unit_counter_dah+0 endif ;include_weighting_code return ; ------------------ ifdef include_mode_button_hold_code banksel PORTA pagesel $ wait_for_mode_switch_release btfss mode_switch goto wait_for_mode_switch_release call pause_100_ms call pause_100_ms bcf key_tx_active return endif ; ------------------ pause_100_ms banksel PORTA ; for good measure movlw _100ms_counter1_calibration movwf counter1 movlw _100ms_counter2_calibration movwf counter2 movlw _100ms_counter3_calibration movwf counter3 looplooploop ifndef exclude_delays_debug decfsz counter1, F goto looplooploop decfsz counter2, F goto looplooploop decfsz counter3, F goto looplooploop endif ;exclude_delays_debug return ; ------------------ pause_w_100ms ; pause W * 100 milliseconds banksel PORTA ; for good measure movwf count ifndef exclude_delays_debug loop_decrement call pause_100_ms decfsz count, F goto loop_decrement endif ;exclude_delays_debug return ; ------------------ produce_wavesidetone btfss sidetone ; is sidetone on right now ? goto wavesidetone_off ; no, go turn the wavesidetone line off incf wavesidetone_counter, F movfw wavesidetone_counter_setting subwf wavesidetone_counter, W btfss STATUS, Z goto wavesidetone_not_equal btfss wavesidetone ; see what state we are in and toggle it goto on_and_reset_wavesidetone_ctr ; wavesidetone is off right now, jump up and set it nop bcf wavesidetone ; wavesidetone is on right now, turn it off goto reset_wavesidetone_counter ; jump ahead and reset counter on_and_reset_wavesidetone_ctr bsf wavesidetone ; turn on wavesidetone line reset_wavesidetone_counter clrf wavesidetone_counter goto end_of_wavesidetone ;------------------------------------------- wavesidetone_off bcf wavesidetone nop ; time padding with nops nop ; this evens things out so each run through this nop ; soubroutine should be the same amount of time nop ; regardless of the state or branches nop wavesidetone_not_equal nop nop nop nop nop nop nop end_of_wavesidetone return ; ------------------ loop_cw_unit ; this subroutine loops for one CW unit (i.e. a dit) ; if the sidetone line has been set high it will produce a square wave sidetone signal on the wavesidetone line ; during the looping, the dit and dah paddles are checked and dit_buffer and dah_buffer set if either are pressed ; this enables dit and dah insertion for iambic operation ; initialize counters with calculated values movfw cw_unit_counter+0 movwf counter1 ;MSB movfw cw_unit_counter+1 movwf counter2 ;LSB ifdef include_paddle_reverse_code btfsc paddle_reverse ; are we in reverse paddle mode? goto loop_cw_unit_reverse_paddle ; yes, go to subroutine for reverse paddle mode endif loop_de_loop_init movlw d'3' movwf counter3 loop_de_loop btfss sending_dit ; skip next line if we are sending dit goto check_for_dit ; if we are sending dit, check dah paddle btfss dah ; check the dah paddle, if = 1, skip over next line bsf dah_buffer ; dah is keyed, set buffer goto skip_over_check_for_dit ; skip over check_for_dit check_for_dit ; we're sending a dah, check the dit paddle btfss dit ; if dit is not keyed, skip over next line bsf dit_buffer ; set the dit buffer nop skip_over_check_for_dit call produce_wavesidetone decfsz counter3,F goto loop_de_loop decfsz counter2, F goto loop_de_loop_init decfsz counter1, F goto loop_de_loop_init return ifdef include_paddle_reverse_code loop_cw_unit_reverse_paddle loop_de_loop_init_rev movlw d'3' movwf counter3 loop_de_loop_rev btfss sending_dit ; skip next line if we are sending dit goto check_for_dit_rev ; if we are sending dit, check dah paddle btfss dit ; check the dit paddle, if = 1, skip over next line bsf dah_buffer ; dah is keyed, set buffer goto skip_over_check_for_dit_rev ; skip over check_for_dit check_for_dit_rev ; we're sending a dah, check the dit paddle btfss dah ; if dah is not keyed, skip over next line bsf dit_buffer ; set the dit buffer nop skip_over_check_for_dit_rev call produce_wavesidetone decfsz counter3,F goto loop_de_loop_rev decfsz counter2, F goto loop_de_loop_init_rev decfsz counter1, F goto loop_de_loop_init_rev return endif ;include_reverse_paddle_code ; ------------------ loop_6_cw_units call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit return ; ------------------ send_dit_or_dah ; send a dit or dah ; parms: ; ; sending_dit ; 1 = send dit ; 0 = send dah ; key_tx_active ; 1 = key tx line ; 0 = do not key tx line ; ; notes: ; ; - sidetone line is always keyed (unless sidetone_off_during_tx is true and key_tx_active is true) ifdef include_toggle_sidetone_on_off_code btfsc sidetone_off_during_tx btfss key_tx_active endif bsf sidetone ; turn on sidetone btfsc key_tx_active ; if key_tx is clear, jump - don't key tx line bsf key ; key tx line btfss sending_dit ; are we sending a dit ? goto send_a_dah ; if not, jump to send_a_dah call loop_cw_unit ; loop for one unit, a dit goto clear_lines ; get out and clear all the lines send_a_dah ifdef include_weighting_code movfw cw_unit_counter+1 ; store the regular counter values movwf cw_unit_counter_dit+1 movfw cw_unit_counter+0 movwf cw_unit_counter_dit+0 movfw cw_unit_counter_dah+1 ; switch in the dah values for weighting movwf cw_unit_counter+1 movfw cw_unit_counter_dah+0 movwf cw_unit_counter+0 endif ;include_weighting_code call loop_cw_unit ; key for three lengths (dah) call loop_cw_unit call loop_cw_unit ifdef include_weighting_code movfw cw_unit_counter_dit+1 ; put regular counter values back movwf cw_unit_counter+1 movfw cw_unit_counter_dit+0 movwf cw_unit_counter+0 endif ;include_weighting_code clear_lines bcf sidetone ; deactivate sidetone bcf key ; deactivate key line call loop_cw_unit ; loop while unkeyed for inter-cw_unit spacing ifdef include_nnnnn_code ; this code blows, so I keep it turned off. ; only use if you're too cheap to buy a mode switch ; keep a history of dits and dahs ; history is stored in a word consisting of ditdah_history1 and 2 ; each bit is one cw_unit : 0 = dit, 1 = dah ; m l m l ; s s s s ; b b b b ; cw_units are pushed like this --> ditdah_history1 --> ditdah_history2 btfss key_tx_active ; are we actually keying the tx ? return ; if not, blow out and do not add to history bsf STATUS, C ; set the carry bit, 1 = dah btfsc sending_dit ; did we just send a dit ? bcf STATUS, C ; if so, clear the carry bit, 0 = dit rrf ditdah_history1, F ; place bit into history register rrf ditdah_history2, F ; carry over into the second byte movlw 0xff movwf ditdah_history_timer1 endif ;include_nnnnn_code return ; ------------------ ifdef include_freq_counter_code store_freq_offset_in_eeprom errorlevel -302 movlw eeprom_freq_offset_high banksel EEADR movwf EEADR banksel freq_offset_binary movfw freq_offset_binary+0 banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom movlw eeprom_freq_offset_low banksel EEADR movwf EEADR banksel freq_offset_binary movfw freq_offset_binary+1 banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom return errorlevel +302 endif ;include_freq_counter_code ; ------------------ initialize_eeprom_first_time errorlevel -302 movlw eeprom_addr_speed_wpm ; address 0x00 last wpm speed banksel EEADR movwf EEADR movlw initial_speed_wpm banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom ; write wavesidetone freq to eeprom movlw eeprom_wavesidetone_setting banksel EEADR movwf EEADR movlw _initial_wavesidetone_setting banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom ;clean out memory locations movlw eeprom_memory_location_0 banksel EEADR movwf EEADR movlw 0xff banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom movlw eeprom_memory_location_1 banksel EEADR movwf EEADR banksel PORTA call write_eeprom movlw eeprom_memory_location_2 banksel EEADR movwf EEADR banksel PORTA call write_eeprom errorlevel +302 ifdef include_freq_counter_code errorlevel -302 movlw eeprom_freq_calib_high banksel EEADR movwf EEADR movlw high _frequency_counter_calib banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom movlw eeprom_freq_calib_low banksel EEADR movwf EEADR movlw low _frequency_counter_calib banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom call store_freq_offset_in_eeprom errorlevel +302 endif ;include_freq_counter_code ; clear eeprom_settings1 location clrf eeprom_settings1 call write_eeprom_settings1 ifndef exclude_delays_debug ifdef include_funky_beeps_code call beep_boop call beep_boop call beep_boop endif ;include_funky_beeps_code endif ;exclude_delays_debug banksel PORTA return ; ------------------ write_eeprom_settings1 errorlevel -302 movlw eeprom_settings1_location banksel EEADR movwf EEADR banksel eeprom_settings1 movfw eeprom_settings1 banksel EEDATA movwf EEDATA banksel PORTA call write_eeprom errorlevel +302 banksel PORTA return ; ------------------ initialize ifndef exclude_delays_debug movlw d'2' ; pause to make sure everything is settled down on power up call pause_w_100ms endif ;exclude_delays_debug ;bcf OPTION_REG,NOT_RBPU errorlevel -302 ifndef processor_low_end banksel CMCON movlw 0x07 ; setup Port A for I/O, not comparator mode movwf CMCON endif ;processor_low_end errorlevel +302 ; initialize IO Ports errorlevel -302 banksel TRISA movlw B'00010111' movwf TRISA movlw B'00001101' ; 1 = input, 0 = output movwf TRISB errorlevel -302 banksel PORTA bcf dit_buffer bcf dah_buffer bcf key bcf sidetone ifdef include_txwake_line_code bcf txwake endif bsf key_tx_active ifdef include_nnnnn_code clrf ditdah_history1 clrf ditdah_history2 endif ; see if eeprom has been written to before movlw 0x00 errorlevel -302 banksel EEADR movwf EEADR banksel EECON1 bsf EECON1, RD ; read address 0x00 banksel EEDATA btfsc EEDATA,0x07 ; is bit 7 set high ? call initialize_eeprom_first_time ; if so, we got a new chip here banksel PORTA errorlevel +302 ; read the last wpm stored in eeprom movlw eeprom_addr_speed_wpm call read_eeprom movwf speed_wpm ifdef include_weighting_code movwf speed_wpm_dah endif call calculate_cw_unit_values ; setup eeprom memory locations movlw eeprom_memory_location_0 movwf eeprom_memory_locations+0 movlw eeprom_memory_location_1 movwf eeprom_memory_locations+1 movlw eeprom_memory_location_2 movwf eeprom_memory_locations+2 movlw eeprom_memory_loc_0_cw_units movwf eeprom_memory_loc_limits+0 movlw eeprom_memory_loc_1_cw_units movwf eeprom_memory_loc_limits+1 movlw eeprom_memory_loc_2_cw_units movwf eeprom_memory_loc_limits+2 ;read eeprom_settings1 from eeprom movlw eeprom_settings1_location call read_eeprom movwf eeprom_settings1 movlw eeprom_wavesidetone_setting call read_eeprom movwf wavesidetone_counter_setting clrf wavesidetone_counter ifdef include_interrupt_code ; set up interrupt stuff errorlevel -302 banksel OPTION_REG bcf OPTION_REG, T0CS ; TIMER0 clock source set to internal clock (clock / 4) bcf OPTION_REG, PSA ; assign prescaler to TIMER0 clock bsf OPTION_REG, PS0 ; set TIMER0 prescaler to 111 ( / 256 ) bsf OPTION_REG, PS1 bsf OPTION_REG, PS2 banksel TMR0 movlw d'150' movwf TMR0 banksel INTCON bsf INTCON, T0IE ; enable TIMER0 overflow interrupt bsf INTCON, GIE ; enable interrupts errorlevel -302 banksel PORTA endif ifdef include_cw_rx_practice_code movlw 0x30 ; seed random number generator movwf randomnum+0 movlw 0x45 movwf randomnum+1 endif ifdef include_freq_counter_code movlw eeprom_freq_calib_high call read_eeprom movwf frequency_counter_calib+0 movlw eeprom_freq_calib_low call read_eeprom movwf frequency_counter_calib+1 ; read freq_offset from EEPROM movlw eeprom_freq_offset_high call read_eeprom movwf freq_offset_binary+0 movlw eeprom_freq_offset_low call read_eeprom movwf freq_offset_binary+1 endif ;include_freq_counter_code ifdef include_serial_port_rx_code errorlevel -302 banksel TRISB bsf TRISB,0x01 ; make RB1 an input banksel TXSTA ; bsf TXSTA,BRGH ; high speed bit generator bcf TXSTA,BRGH ; low speed bit generator movlw d'25' ; 9600 baud with 4Mhz clock / 2400 with low speed bit generator banksel SPBRG movwf SPBRG banksel TXSTA bcf TXSTA,SYNC ; asyncronous mode ;bcf STATUS,RP0 ; select bank 0 banksel RCSTA bsf RCSTA,SPEN bsf RCSTA,CREN ; enable serial receive read_rx_fifo_loop call pause_100_ms banksel PIR1 btfss PIR1,RCIF ; do we have something in the buffer? goto read_rx_fifo_loop_exit ; no, blow out of here banksel RCREG movfw RCREG ; pull byte out of the serial rx FIFO goto read_rx_fifo_loop read_rx_fifo_loop_exit errorlevel +302 endif ;include_serial_port_code banksel PORTA bcf send_cw_preemptible bcf squeeze_detected bcf in_beacon_mode ifdef include_aux_line_code bcf aux ;set aux line low initially endif ;include_aux_line_code retlw 0 ; ------------------ ; ------------------ ifdef include_serial_cw_keyboard check_serial_cw_keyboard btfsc PIR1,OERR ; did we overrun the buffer? goto handle_overrun btfsc PIR1,FERR ; did we have a frame error? goto handle_frame_error btfss PIR1,RCIF ; do we have something in the buffer? return ; no, blow out of here movfw RCREG ; pull byte out of the serial rx FIFO ifdef include_serial_rx_debug_code ; debug code - send received byte in decimal CW movwf binary2bcd_binary_in+1 clrf binary2bcd_binary_in+0 call binary2bcd call rotate_BCD012_left call rotate_BCD012_left call send_BCD2_in_cw call rotate_BCD012_left call rotate_BCD012_left call send_BCD2_in_cw call rotate_BCD012_left call rotate_BCD012_left call send_BCD2_in_cw return else sublw d'49' ; convert ascii code to cw table lookup 0-9 movwf temp_memory ; double it addwf temp_memory, F movfw temp_memory call cw_table ; get the first byte of code movwf cw_char_to_send+0 movfw temp_memory addlw 0x01 ; increment it to get the next byte call cw_table movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 return endif ;include_serial_rx_debug_code handle_overrun movfw RCREG ifdef include_debug_code call low_beep endif return handle_frame_error bcf RCSTA,CREN bsf RCSTA,CREN ifdef include_debug_code call low_beep endif return endif ;include_serial_port_rx_code ; ------------------ check_straight_key_mode ; this checks if the dah paddle is keyed or mode button is pressed, if so, we loop in straight key mode forever btfss mode_switch goto go_into_straight_key_mode btfsc dah ; if dah = 0 go to straight_key_loop retlw 0 ; otherwise, return go_into_straight_key_mode bsf sending_dit movlw d'60' movwf speed_wpm call calculate_cw_unit_values ifdef include_txwake_line_code bcf txwake endif ;include_txwake_line_code straight_key_loop call loop_cw_unit btfss mode_switch goto key_on btfsc dit ; if dit = 0 jump to key_on goto key_off ; else go to key_off key_on ifdef include_txwake_line_code ;call reset_txwake_timer movlw HIGH _txwake_delay_time_straight_key movwf txwakecounter+0 movlw LOW _txwake_delay_time_straight_key movwf txwakecounter+1 bsf txwake endif bsf key ; key the TX ifdef include_toggle_sidetone_on_off_code btfss sidetone_off_during_tx endif ;include_toggle_sidetone_on_off_code bsf sidetone ; key the sidetone goto straight_key_loop key_off ifdef include_txwake_line_code bcf dit_buffer bcf dah_buffer pagesel check_txwake_clear_time errorlevel -306 call check_txwake_clear_time errorlevel +306 endif bcf key ; unkey the TX bcf sidetone ; unkey the sidetone goto straight_key_loop retlw 0 ; ------------------ send_buffer btfss dit_buffer ; if dit_buffer is set, skip next line goto check_dah_buffer ifdef include_txwake_line_code bsf txwake endif bcf dit_buffer bsf sending_dit call send_dit_or_dah ifdef include_iambic_mode_code btfsc iambic_b_mode goto check_dah_buffer btfss squeeze_detected goto check_dah_buffer btfss dit goto check_dah_buffer btfss dah goto check_dah_buffer bcf squeeze_detected bcf dit_buffer bcf dah_buffer endif ;include_iambic_mode_code check_dah_buffer btfss dah_buffer ; if dah_buffer is set, skip next line return ifdef include_txwake_line_code bsf txwake endif bcf dah_buffer bcf sending_dit ifdef include_bug_code ; HACK ALERT: special hack to accomodate bug mode btfss bug_mode_on goto no_bug_mode movfw speed_wpm ; temporarily store the current wpm movwf temp_memory movlw d'60' ; jack up the wpm to 60 so loop is responsive movwf speed_wpm call calculate_cw_unit_values bsf sending_dit bug_loop ifdef include_toggle_sidetone_on_off_code btfss sidetone_off_during_tx endif ;include_toggle_sidetone_on_off_code bsf sidetone bsf key call loop_cw_unit btfss dah goto bug_loop ; loop as long as dah is on bcf sidetone bcf key movfw temp_memory ; restore original wpm movwf speed_wpm call calculate_cw_unit_values bcf dah_buffer return no_bug_mode endif ;include_bug_code call send_dit_or_dah ifdef include_iambic_mode_code btfsc iambic_b_mode return btfss squeeze_detected return btfss dit return btfss dah return bcf squeeze_detected bcf dit_buffer bcf dah_buffer endif ;include_iambic_mode_code return ; ------------------ decrement_wpm movfw speed_wpm sublw d'4' ; 5 wpm lower limit btfsc STATUS, C ;if 4 - speed_wpm > 0, return and do not decrement return decf speed_wpm, F ifdef include_weighting_code decf speed_wpm_dah, F endif call calculate_cw_unit_values return ; ------------------ increment_wpm movfw speed_wpm sublw maxcwspeed ; max wpm upper limit btfss STATUS, C ;if maxcwspeed - speed_wpm < 0, return and do not increment return incf speed_wpm, F ifdef include_weighting_code incf speed_wpm_dah, F endif call calculate_cw_unit_values return ; ------------------ send_cw ; sends two bytes of CW ; parms: ; cw_char_to_send:2 ; 00 = terminator = end of character or string, return ; 01 = dit ; 11 = dah ; 10 = intercharacter spacing - pause for two more cw_units lengths and go on ; cw_char_to_send+0 is sent first, cw_char_to_send+1 is sent second; MSB is the first sent movlw cw_char_to_send-1 ; setup pointer, set to first byte - 1 movwf FSR movlw 0x02 ; loop through twice movwf count loop_initialize incf FSR, F movlw b'11000000' ; initialize send_cw_temp movwf send_cw_temp cw_byte_decode btfss send_cw_preemptible goto no_send_cw_preemptible call check_for_button_hit ; see if a button was hit for us to exit out sublw 0x01 btfsc STATUS,Z goto send_cw_button_hit_loop no_send_cw_preemptible movfw send_cw_temp movwf temp ;make a copy of send_cw_temp movfw INDF andwf send_cw_temp, W btfsc STATUS, Z ; if 0, we have a terminator, exit goto loop_2_cw_units_and_exit subwf send_cw_temp, W btfsc STATUS, Z ; if 0, we have a dah goto send_cw_dah_byte ; check for 10 movlw b'10101010' ; create a bit mask andwf temp, F ; and it with the temp register so we get 10 in the right position movfw INDF ; get a fresh byte of cw again subwf temp, W ; subtract btfsc STATUS, Z ; if we have zero, this is 10 - an intercharacter space goto loop_two_cw_units ; zero set = 10 bsf sending_dit ; zero not set = 01 - send a dit call send_dit_or_dah goto cw_byte_decode_next send_cw_dah_byte bcf sending_dit call send_dit_or_dah cw_byte_decode_next ; get things ready to read the next two bits to the right bcf STATUS, C ; rotate bits to the right twice rrf send_cw_temp, F ; to select the next two bits rrf send_cw_temp, F btfss STATUS, C ; if a bit gets into carry flag, we're at zero and we're done goto cw_byte_decode ; otherwise, go back for more with this byte decf count, F ; we're done with this byte, decrement the count btfss STATUS, Z ; did count hit zero ? goto loop_initialize loop_2_cw_units_and_exit ; loop two cw_units lengths so we have proper inter character spacing call loop_cw_unit call loop_cw_unit return loop_two_cw_units call loop_cw_unit call loop_cw_unit goto cw_byte_decode_next send_cw_button_hit_loop call loop_check_for_button_hit ifdef include_function_button_code bsf memory_playback_manual_exit endif return ;------------- send_BCD2_in_cw swapf BCD2,F ; send upper nibble movfw BCD2 andlw b'00001111' call send_w_BCD_in_cw swapf BCD2,F ; send lower nibble movfw BCD2 andlw b'00001111' call send_w_BCD_in_cw return ;------------- announce_speed_wpm ; convert speed_wpm to BCD movlw 0x00 movwf binary2bcd_binary_in+0 movfw speed_wpm movwf binary2bcd_binary_in+1 call binary2bcd call send_BCD2_in_cw return ;------------- speed_mode ; this mode allows you to change the sending speed ; dit paddle speeds up ; dah paddle slows down ; mode button or paddle squeeze exits bsf sending_dit movlw 0x04 call pause_w_100ms dit_loop call send_dit_or_dah ; sound a dit btfss dit ; dit is pressed, dah is not btfss dah goto skip_increment_wpm call increment_wpm goto dit_loop skip_increment_wpm btfss dah ; dah is pressed, dit is not btfss dit goto skip_decrement_wpm call decrement_wpm goto dit_loop skip_decrement_wpm btfss mode_switch ; if switch is pressed, get out of loop goto loop_while_pressed2 btfss dit ; if both paddles are pressed, this also exits btfsc dah goto dit_loop loop_while_pressed2 ; wait for mode_switch, dit and dah to be released call pause_100_ms btfss mode_switch goto loop_while_pressed2 btfss dit goto loop_while_pressed2 btfss dah goto loop_while_pressed2 movlw 0x05 call pause_w_100ms call write_speed_wpm_to_eeprom movlw 0x05 call pause_w_100ms bcf dit_buffer bcf dah_buffer return ; ------------------ write_speed_wpm_to_eeprom movlw eeprom_addr_speed_wpm errorlevel -302 banksel EEADR movwf EEADR banksel speed_wpm movfw speed_wpm banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA call write_eeprom return ; ------------------ ifdef include_tune_mode_code tune_mode ; this mode is used for tuning up a rig ; left paddle intermittently turns on TX ; right paddle latches TX ; mode switch or paddle squeeze exits bcf tune_latch_mode movlw 0x02 call pause_w_100ms bsf sending_dit call send_dit_or_dah movfw speed_wpm ; temporarily store the current wpm movwf temp_memory movlw d'60' ; jack up the wpm to 60 so loop is responsive movwf speed_wpm call calculate_cw_unit_values tune_mode_loop call loop_cw_unit btfss dit ; is dit pressed ? goto tune_dit_pressed ; if so, go to tune_dit_pressed btfss dah ; is dah pressed ? goto tune_dah_pressed ; if so, go to tune_dah_pressed btfsc tune_latch_mode ; are we in latch mode ? goto tune_mode_check_for_exit ; if so jump over bcf key ; we're not in latch mode and dit is not pressed bcf sidetone ; so clear key and sidetone ifdef include_txwake_line_code bcf txwake endif tune_mode_check_for_exit btfss mode_switch ; if switch is pressed, get out of loop goto tune_mode_loop_while_pressed btfss dit ; if both paddles are pressed, this also exits btfsc dah goto tune_mode_loop tune_mode_loop_while_pressed ; wait for mode_switch, dit and dah to be released bcf key bcf sidetone ifdef include_txwake_line_code bcf txwake endif call pause_100_ms btfss mode_switch goto tune_mode_loop_while_pressed btfss dit goto tune_mode_loop_while_pressed btfss dah goto tune_mode_loop_while_pressed movfw temp_memory ; restore original wpm movwf speed_wpm call calculate_cw_unit_values movlw 0x02 ; exiting dit call pause_w_100ms bsf sending_dit call send_dit_or_dah bcf dit_buffer ; clean out buffers in case they got set in loop_cw_unit bcf dah_buffer return tune_dit_pressed bcf tune_latch_mode ; get out of latch mode ifdef include_txwake_line_code bsf txwake endif bsf key ; key tx ifdef include_toggle_sidetone_on_off_code btfss sidetone_off_during_tx endif ;include_toggle_sidetone_on_off_code bsf sidetone ; key sidetone call loop_cw_unit ; goto tune_mode_check_for_exit tune_dah_pressed bsf tune_latch_mode ; go into latch mode btfss key goto tune_key bcf key bcf sidetone call pause_100_ms ifdef include_txwake_line_code bcf txwake endif goto tune_dah_pressed_loop tune_key ifdef include_txwake_line_code bsf txwake endif bsf key ifdef include_toggle_sidetone_on_off_code btfss sidetone_off_during_tx endif ;include_toggle_sidetone_on_off_code bsf sidetone call loop_cw_unit ; tune_dah_pressed_loop ; wait for dah to be released call loop_cw_unit btfss dit ; if dit is pressed, goto tune_mode_check_for_exit ; blow out of the loop because both dit and dah are squeezed btfss dah ; if dah is not pressed, skip over goto tune_dah_pressed_loop goto tune_mode_check_for_exit endif ;include_tune_mode_code ; ------------------ ifdef include_calibration_code calibration_check_mode ; this mode allows you to check the cw unit timing calibration ; hold down the dit paddle for a minute or half minute ; after releasing the paddle, the number of dits will be announced in CW ; use the formula below to determine the proper number of dits for the wpm setting ; speed in wpm = dits per min / 25 ; 20 WPM equals 500 dits per minute or 250 dits per 30 seconds clrf binary2bcd_binary_in+0 clrf binary2bcd_binary_in+1 bsf sending_dit calibration_check_mode_wait_loop btfsc dit ; loop while waiting for dit to be pressed goto calibration_check_mode_wait_loop calibration_dit_loop call send_dit_or_dah ; send a dit incf binary2bcd_binary_in+1, F ; increment the dit counter btfsc STATUS, Z ; did the lower byte roll over ? incf binary2bcd_binary_in+0, F ; if so, increment the upper byte btfss dit ; if dit is still pressed goto calibration_dit_loop ; continue to loop movlw 0x05 call pause_w_100ms call binary2bcd ; break out lower nibble of middle BCD digit movfw BCD1 andlw b'00001111' movwf bcd_low_nibble addwf bcd_low_nibble, F ; double it so we're in the right spot in cw_table movfw bcd_low_nibble ; send the low nibble BCD digit in CW call cw_table movwf cw_char_to_send+0 movfw bcd_low_nibble addlw 0x01 call cw_table movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 ; send least signficant BCD digit call send_BCD2_in_cw return endif ; ------------------ program_memory ; parms ; memory_number (0 = callsign memory) ; ; calls ; send_dit_or_dah ; check_paddles ; pause_w_100ms ; divide16bit ; send a dit or boop_beep to signify start movlw 0x02 call pause_w_100ms ifdef include_funky_beeps_code call boop_beep else bsf sending_dit call send_dit_or_dah endif ; initialize counters clrf cw_unit_count bcf temp_memory_dirty movlw b'11000000' movwf temp_memory_mask clrf temp_memory ; get parameters for this memory movlw eeprom_memory_locations ; get the *address* of memory location array movwf FSR movfw memory_number ; add for the appropriate memory location addwf FSR, F movfw INDF movwf temp_memory_eeprom_pointer ; initialize the live eeprom pointer movwf start_of_memory_location ; initialize the static starting location incf temp_memory_eeprom_pointer, F ; actual recording starts at +1 initial_recording_loop ; check if mode button was hit pagesel we_are_done_with_programming errorlevel -306 btfss mode_switch goto we_are_done_with_programming errorlevel +306 pagesel check_paddles errorlevel -306 call check_paddles errorlevel +306 btfsc dit_buffer goto add_dit_or_dah_to_memory btfsc dah_buffer goto add_dit_or_dah_to_memory goto initial_recording_loop init_recording_loop_counters movlw 0x00 movwf recording_loop_counter1 movwf recording_loop_counter2 recording_loop ifdef include_function_button_code btfss switch0 goto we_are_done_with_programming btfss switch1 goto we_are_done_with_programming btfss switch2 goto we_are_done_with_programming endif pagesel we_are_done_with_programming errorlevel -306 btfss mode_switch goto we_are_done_with_programming errorlevel +306 pagesel check_paddles errorlevel -306 call check_paddles errorlevel +306 btfsc dit_buffer goto store_cw_unit_in_temp_memory btfsc dah_buffer goto store_cw_unit_in_temp_memory ; nothing is active right now, increment the inactive counters incfsz recording_loop_counter1, F goto recording_loop movfw recording_loop_counter2 sublw 0xFF btfss STATUS, Z incf recording_loop_counter2, F goto recording_loop we_are_done_with_programming ; store the last byte if temp_memory is dirty btfsc temp_memory_dirty call store_temp_memory_to_eeprom movfw cw_unit_count ;see if cw_unit_count is zero btfsc STATUS,Z decf cw_unit_count,F ;if so, store FF in cw_unit_count to signify blank memory ; store the cw_unit_count in the first byte movfw start_of_memory_location errorlevel -302 banksel EEADR movwf EEADR errorlevel +302 banksel PORTA movfw cw_unit_count errorlevel -302 banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA call write_eeprom movlw 0x03 call pause_w_100ms ifdef include_funky_beeps_code call beep_boop else bsf sending_dit call send_dit_or_dah endif return ; we are totally done ;------------- sub-subroutines store_cw_unit_in_temp_memory ; store the silent timee before the cw_unit ; first, calculate the ratio between the silent and the cw unit time movfw cw_unit_counter+1 movwf divisor+1 movfw cw_unit_counter+0 movwf divisor+0 movfw recording_loop_counter1 ; recording_loop_counter / cw_unit_counter = space ratio movwf dividend+1 ; this is used below to determine if we have an intercharacter space movfw recording_loop_counter2 ; or an interword space movwf dividend+0 call divide16bit movfw quotient+0 ; check if quotient MSB is zero btfsc STATUS, Z goto measure_up_silent_time ; if so, skip over this stuff movlw 0xff ; if not, set LSB to FF movwf quotient measure_up_silent_time movfw quotient+1 ; see how many cw_unit lengths we had silent before this cw_unit sublw d'1' ; is this an intercharacter space ? was 10 btfsc STATUS, C goto add_dit_or_dah_to_memory ; no, it's negative, don't add a space cw_unit movfw quotient+1 ; is this an interword space ? sublw d'14' ; was 70 btfsc STATUS, C goto skip_over_1 ; result is negative, we have less than 6 cw_units movlw b'00000000' ; 00 = 6 cw_unit space goto skip_over_2 skip_over_1 movlw b'10101010' ; 10 = 2 cw_unit space skip_over_2 call store_w_in_temp_memory add_dit_or_dah_to_memory btfss dit_buffer goto skip_over_dit bsf sending_dit call send_dit_or_dah movlw b'01010101' ; 01 = dit bcf dit_buffer ; clean out dit buffer goto skip_over_dah skip_over_dit bcf sending_dit call send_dit_or_dah movlw b'11111111' ; 11 = dah bcf dah_buffer ; clean out dah buffer skip_over_dah call store_w_in_temp_memory ; enforce cw unit limit count so we don't run over into another memory ; get cw_unit count limit movlw eeprom_memory_loc_limits+0 movwf FSR movfw memory_number ; add for the appropriate memory location addwf FSR, F movfw INDF subwf cw_unit_count, W ; does cw_unit_count = cw_unit count limit ? btfss STATUS, Z goto init_recording_loop_counters ; no, go back for more bsf sidetone ; yes, lock up the sidetone wait_for_user_to_realize ifdef include_funky_beeps_code call low_beep endif call pause_100_ms btfss dit goto wait_for_user_to_realize btfss dah goto wait_for_user_to_realize bcf sidetone bcf dit_buffer bcf dah_buffer goto we_are_done_with_programming ;cleanup store_w_in_temp_memory andwf temp_memory_mask, W ; apply the mask to W addwf temp_memory, F ; add the cw_unit to temp_memory ;increment cw_unit count incf cw_unit_count, F ;setup_for_next_store_w bsf temp_memory_dirty rrf temp_memory_mask, F ; move the bit mask over rrf temp_memory_mask, F btfsc STATUS, C ; if we carried, we need to start a new byte call store_temp_memory_to_eeprom return store_temp_memory_to_eeprom movwf temp_w ; preserve w ; write the temp memory byte to eeprom movfw temp_memory_eeprom_pointer errorlevel -302 banksel EEADR movwf EEADR errorlevel +302 banksel PORTA movfw temp_memory errorlevel -302 banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA errorlevel -306 pagesel check_paddles call check_paddles errorlevel +306 call write_eeprom errorlevel -306 pagesel check_paddles call check_paddles errorlevel +306 ;get things ready for new byte movlw b'11000000' movwf temp_memory_mask clrf temp_memory ; point to the next eeprom byte to be written incf temp_memory_eeprom_pointer, F ; TODO - check if we're at the limit for this memory movfw temp_w ; restore w bcf temp_memory_dirty return ; endif ; ------------------ read_eeprom ; read eeprom address in w ; return data in w errorlevel -302 banksel EEADR movwf EEADR banksel EECON1 bsf EECON1, RD banksel EEDATA movfw EEDATA errorlevel +302 banksel PORTA return ; ------------------ check_for_button_hit btfss dit retlw 1 btfss dah retlw 1 btfss mode_switch retlw 1 ifdef include_function_button_code btfss switch0 retlw 1 btfss switch1 retlw 1 btfss switch2 retlw 1 endif retlw 0 ; ------------------ loop_check_for_button_hit call check_for_button_hit sublw 0x01 btfsc STATUS,Z goto loop_check_for_button_hit call pause_100_ms return ; ------------------ ;UNDER CONSTRUCTION MARKER ;****************************************************************************************** ;****************************************************************************************** ;****************************************************************************************** ;****************************************************************************************** ;****************************************************************************************** ;****************************************************************************************** ifdef enhanced_memory_support get_cw_memory_character ; this subroutine returns one cw element from a memory location ; input parameters: ; memory_number = the number of the CW memory to access (0,1,2) ; cw_element_number = the number of the character to retrive (0-253) ; output: ; returns the CW element in W register ; 00 (0x00) = 6 CW unit space ; 10 (0x02) = 2 CW unit space ; 11 (0x03) = dah ; 01 (0x01) = dit ; 0xFF = memory location is empty ; 0x04 = invalid CW element location ; ; "local" variables ; ; ; CW Memory Organization ; ; 8 7 6 5 4 3 2 1 ; +-+-+-+-+-+-+-+-+ ; | | <-- first byte is number of CW elements in the memory (1 = 1 CW element, 2 = 2 CW elements, 0xFF = empty memory) ; +-+-+-+-+-+-+-+-+ ; | 1 | 2 | 3 | 4 | <-- second byte is first four CW elements (each element is two bits) ; +-+-+-+-+-+-+-+-+ ; | 5 | 6 | 7 | 8 | <-- third byte is CW elements 5 through 8 ; +-+-+-+-+-+-+-+-+ ; ; get number of CW elements for this memory movlw eeprom_memory_locations ; get the *address* of memory location array (a "pointer") movwf FSR movfw memory_number ; add the memory_number (0,1,2) to move the pointer to the right eeprom memory location in the array addwf FSR, F movfw INDF ; pull the *value* of the array register into W call read_eeprom ; get the first byte in the memory - this is the number of CW elements currently in the memory movwf cw_memory_element_count ; copy the element count in a temporary register ; check if memory location is empty sublw 0xFF ; is the register FF meaning empty memory? btfsc STATUS, Z retlw 0xFF ; yes - return with 0xFF ; check if an invalid CW element number was requested movfw cw_element_number subwf cw_memory_element_count, 0 ; calculate cw_memory_element_count - cw_element_number btfss STATUS, C ; if Carry is set, the result is positive or zero retlw 0x04 ; invalid CW element location requested, return with 0x04 ; calculate the element number in the byte: 0, 1, 2, 3 decf cw_element_number, F movfw cw_element_number iorlw 0x03 ; get the two least significant bits movwf memory_location_element_number ; 00 = 1st element in a location, 01 = 2nd, 10 = 3rd, 11 = fourth ;calculate the memory location in eeprom bcf STATUS, C ; here we rotate the two least significant bits off; the remaining bits will be the byte location in memory rrf cw_element_number, F bcf STATUS, C rrf cw_element_number, F ; this is the memory location (first location = 0) incf cw_element_number, F ; add one to it so we skip the first memory byte which is the CW element count ; get the memory byte from eeprom movlw eeprom_memory_locations ; get the *address* of memory location array (a "pointer") movwf FSR movfw memory_number ; add the memory_number (0,1,2) to move the pointer to the right eeprom memory location in the array addwf FSR, F movfw INDF ; pull the *value* of the array register into W addwf cw_element_number, 0 ; call read_eeprom ; go get the byte ; get the CW element out of the byte and return it movwf cw_character ; place the byte we read from eeprom above in a temporary register incf memory_location_element_number, F ; increment this register to set it up for a loop get_the_two_bits_loop movlw 0x04 subwf memory_location_element_number, 0 btfsc STATUS, Z goto we_are_at_the_right_two_bits rrf cw_character, F rrf cw_character, F incf memory_location_element_number, F goto get_the_two_bits_loop we_are_at_the_right_two_bits movlw 0x03 ; mask the two least significant bits only iorwf cw_character, 0 return ; and return them in W ; ------------------ play_memory ; enhanced version ; this subroutine plays one of the CW memories ; parameters: ; memory_number = number of memory to play (first one = 0) ifdef include_function_button_code bcf memory_playback_manual_exit ; clear flag to tell calling subroutines if user manaully exited endif ; get parameters for this memory movlw eeprom_memory_locations ; get the *address* of memory location array movwf FSR movfw memory_number ; add for the appropriate memory location addwf FSR, F movfw INDF movwf temp_memory_eeprom_pointer ; initialize the live eeprom pointer ; read the cw_unit count for this memory call read_eeprom movwf cw_unit_count ; check for blank memory movlw 0xFF subwf cw_unit_count, W ; is the cw_unit count 0xFF ? btfss STATUS, Z goto byte_loop ; no, skip over this ifdef include_funky_beeps_code call low_beep else bsf sending_dit ; yes, send three dits and blow out bcf key_tx_active call send_dit_or_dah call send_dit_or_dah call send_dit_or_dah bsf key_tx_active endif ;include_funky_beeps_code return byte_loop ; get a byte from eeprom incf temp_memory_eeprom_pointer, F ; move to the next byte movfw temp_memory_eeprom_pointer call read_eeprom movwf temp_memory movlw 0x04 ; four cw_units in a byte movwf temp_memory_mask ; use temp_memory_mask as a cw_unit within a byte counter cw_unit_loop ; TODO - exit if a switch or paddle is hit rlf temp_memory, F btfsc STATUS, C goto first_bit_1 ;first_bit_0 rlf temp_memory, F btfsc STATUS, C goto cw_unit_01 ;cw_unit_00 - 6 cw_unit space call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit goto next_cw_unit_setup first_bit_1 rlf temp_memory, F btfsc STATUS,C goto cw_unit_11 ;cw_unit_10 - 2 cw_unit space call loop_cw_unit call loop_cw_unit goto next_cw_unit_setup cw_unit_11 ; dah bcf sending_dit call send_dit_or_dah goto next_cw_unit_setup cw_unit_01 bsf sending_dit call send_dit_or_dah next_cw_unit_setup decf cw_unit_count, F btfsc STATUS, Z ; did we do the last cw_unit ? return ; yes, blow out btfsc in_beacon_mode goto skip_button_check call check_for_button_hit sublw 0x01 btfsc STATUS,Z goto button_hit_loop skip_button_check decf temp_memory_mask, F ; no btfsc STATUS, Z ; did we do the last cw_unit in this byte ? goto byte_loop ; yes, grab another byte goto cw_unit_loop ; no, grab another cw_unit in this byte return button_hit_loop call loop_check_for_button_hit ifdef include_function_button_code bsf memory_playback_manual_exit endif return endif ;enhanced_memory_support ; ------------------ ; ------------------ ifndef enhanced_memory_support play_memory ; regular version without enhanced memory support ; this subroutine plays one of the CW memories ; parameters: ; play_memory = number of memory to play (first one = 0) ifdef include_function_button_code bcf memory_playback_manual_exit ; clear flag to tell calling subroutines if user manaully exited endif ; get parameters for this memory movlw eeprom_memory_locations ; get the *address* of memory location array movwf FSR movfw memory_number ; add for the appropriate memory location addwf FSR, F movfw INDF movwf temp_memory_eeprom_pointer ; initialize the live eeprom pointer ; read the cw_unit count for this memory call read_eeprom movwf cw_unit_count ; check for blank memory movlw 0xFF subwf cw_unit_count, W ; is the cw_unit count 0xFF ? btfss STATUS, Z goto byte_loop ; no, skip over this ifdef include_funky_beeps_code call low_beep else bsf sending_dit ; yes, send three dits and blow out bcf key_tx_active call send_dit_or_dah call send_dit_or_dah call send_dit_or_dah bsf key_tx_active endif ;include_funky_beeps_code return byte_loop ; get a byte from eeprom incf temp_memory_eeprom_pointer, F ; move to the next byte movfw temp_memory_eeprom_pointer call read_eeprom movwf temp_memory movlw 0x04 ; four cw_units in a byte movwf temp_memory_mask ; use temp_memory_mask as a cw_unit within a byte counter cw_unit_loop ; TODO - exit if a switch or paddle is hit rlf temp_memory, F btfsc STATUS, C goto first_bit_1 ;first_bit_0 rlf temp_memory, F btfsc STATUS, C goto cw_unit_01 ;cw_unit_00 - 6 cw_unit space call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit call loop_cw_unit goto next_cw_unit_setup first_bit_1 rlf temp_memory, F btfsc STATUS,C goto cw_unit_11 ;cw_unit_10 - 2 cw_unit space call loop_cw_unit call loop_cw_unit goto next_cw_unit_setup cw_unit_11 ; dah bcf sending_dit call send_dit_or_dah goto next_cw_unit_setup cw_unit_01 bsf sending_dit call send_dit_or_dah next_cw_unit_setup decf cw_unit_count, F btfsc STATUS, Z ; did we do the last cw_unit ? return ; yes, blow out btfsc in_beacon_mode goto skip_button_check call check_for_button_hit sublw 0x01 btfsc STATUS,Z goto button_hit_loop skip_button_check decf temp_memory_mask, F ; no btfsc STATUS, Z ; did we do the last cw_unit in this byte ? goto byte_loop ; yes, grab another byte goto cw_unit_loop ; no, grab another cw_unit in this byte return button_hit_loop call loop_check_for_button_hit ifdef include_function_button_code bsf memory_playback_manual_exit endif return endif ;enhanced_memory_support ; ------------------ program_memory_0 clrf memory_number call program_memory return ; ------------------ program_memory_1 movlw 0x01 movwf memory_number call program_memory return ; ------------------ program_memory_2 movlw 0x02 movwf memory_number call program_memory return ; endif ; ------------------ playback_memory_0 clrf memory_number call play_memory return ; ------------------ playback_memory_1 movlw 0x01 movwf memory_number call play_memory return ; ------------------ playback_memory_2 movlw 0x02 movwf memory_number call play_memory return ; --------------------------------------------- ifdef multi_memory_page_support org 0800h endif ;---------------------------------------------- check_mode_button banksel PORTA pagesel $ ifdef include_nnnnn_code ; see if ditdah_history time has expired ; TODO - make this driven by speed_wpm. This may get falsely triggered at higher speeds decf ditdah_history_timer1, F btfsc STATUS, Z clrf ditdah_history1 ; check ditdah_history for NNNNN movfw ditdah_history1 sublw b'10101010' ; check for dit, dah, dit, dah, dit, dah, dit, dah btfss STATUS, Z goto check_the_mode_switch ; we didn't find it, check the mode button movfw ditdah_history2 ; we did find it, check the next byte andlw b'11000000' ; apply a bit mask so we get just two bits sublw b'10000000' ; check for dit, dah btfsc STATUS, Z goto loop_while_mode_pressed ; if we found it, go into command mode endif ; include_nnnnn_code check_the_mode_switch btfsc mode_switch ; if mode switch is pressed (0), keep going return ; otherwise there's nothing to do, blow out of here loop_while_mode_pressed ifdef include_cw_rx_practice_code call get_random_number16 ; while we're not doing anything, randomize endif ifdef include_mode_button_hold_code ; ---- special shortcuts by holding the mode button ---- ifdef include_function_button_code btfss switch0 goto call_program_memory_0_hold btfss switch1 goto call_program_memory_1_hold btfss switch2 goto call_program_memory_2_hold endif ;include_function_button_code btfss dit goto speed_mode_increment btfss dah goto speed_mode_decrement goto no_other_buttons_pressed call_program_memory_0_hold errorlevel -306 pagesel wait_for_mode_switch_release call wait_for_mode_switch_release pagesel program_memory_0 call program_memory_0 errorlevel +306 goto blow_out_of_here_no_bk call_program_memory_1_hold errorlevel -306 pagesel wait_for_mode_switch_release call wait_for_mode_switch_release pagesel program_memory_1 call program_memory_1 errorlevel +306 goto blow_out_of_here_no_bk call_program_memory_2_hold errorlevel -306 pagesel wait_for_mode_switch_release call wait_for_mode_switch_release pagesel program_memory_2 call program_memory_2 errorlevel +306 goto blow_out_of_here_no_bk quick_speed_mode_check btfss dit goto speed_mode_increment btfss dah goto speed_mode_decrement btfss mode_switch goto quick_speed_mode_check ; mode button is still depressed, keep looping errorlevel -306 pagesel write_speed_wpm_to_eeprom call write_speed_wpm_to_eeprom ; mode button not pressed, save the speed setting and blow out errorlevel +306 goto blow_out_of_here_no_bk speed_mode_increment bcf key_tx_active errorlevel -306 pagesel increment_wpm call increment_wpm errorlevel +306 bsf sending_dit errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 goto quick_speed_mode_check speed_mode_decrement bcf key_tx_active errorlevel -306 pagesel decrement_wpm call decrement_wpm errorlevel +306 bsf sending_dit errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 goto quick_speed_mode_check no_other_buttons_pressed ; ---- end of mode button hold shortcuts ---- endif ;include_mode_button_hold_code errorlevel -306 pagesel pause_100_ms call pause_100_ms ; button debounce time errorlevel +306 pagesel $ btfss mode_switch ; wait until switch is released goto loop_while_mode_pressed banksel PORTA bcf key_tx_active ; disable tx keying line bcf expert_commands_on ; initialize some flags ifdef include_freq_counter_code bcf command_mode_v_cmd bcf command_mode_k_cmd endif ;include_freq_counter_code ifdef include_m_and_h_cmd_code bcf command_mode_m_cmd bcf command_mode_h_cmd endif ;include_m_and_h_cmd_code ifdef include_funky_beeps_code errorlevel -306 pagesel boop_beep call boop_beep errorlevel +306 else bsf sending_dit ; send dit to announce command mode errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 endif initialize_command_mode pagesel $ banksel PORTA bcf paddle_was_hit clrf command_mode_buffer1 clrf command_mode_buffer2 ; command_mode_buffer1 and 2 hold the dits and dahs that are sent in command mode ; each element (dit or dah) takes up two bits ; dit(01)/dah(11) -> command_mode_buffer1 -> command_mode_buffer2 ; so B equals 01010111 00000000 ; , equals 11110101 11110000 ; now, the real stuff begins init_command_mode_loop_vars ; TODO - calculate directly using wpm movfw cw_unit_counter+1 ; initialize command mode loop variables movwf command_mode_loop_count1 movfw cw_unit_counter+0 movwf command_mode_loop_count2 movlw 0x01 movwf command_mode_loop_count3 user_input_loop_mode ; ---- collect the user's command --- pagesel $ banksel PORTA btfss mode_switch ; if mode switch is pressed, get out of command mode goto loop_while_mode_pressed_exit errorlevel -306 pagesel check_paddles call check_paddles errorlevel +306 pagesel $ banksel PORTA btfsc dit_buffer ; was dit pressed ? goto dit_pressed btfsc dah_buffer ; was dah pressed ? goto dah_pressed btfss paddle_was_hit ; has a paddle been hit already ? goto user_input_loop_mode ; if not, loop back without decrementing counters decf command_mode_loop_count1, F btfss STATUS, Z goto user_input_loop_mode decf command_mode_loop_count2, F btfss STATUS, Z goto user_input_loop_mode decf command_mode_loop_count3, F btfss STATUS, Z goto user_input_loop_mode figure_out_the_command ; figure out what command was sent movlw 0x04 errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 banksel PORTA pagesel $ ifdef include_freq_counter_code btfsc command_mode_v_cmd ; V commands (freq counter offset mode) are two characters goto command_mode_v_commands ; go to a special handler for these btfsc command_mode_k_cmd ; K command is several chanracters goto command_mode_k_commands ; go to a special handler for these endif ;include_freq_counter_code ifdef include_m_and_h_cmd_code btfsc command_mode_m_cmd ; M commands (freq counter offset mode) are two characters goto command_mode_m_commands ; go to a special handler for these btfsc command_mode_h_cmd ; H command is several chanracters goto command_mode_h_commands ; go to a special handler for these endif ;include_m_and_h_cmd_code movfw command_mode_buffer2 btfss STATUS, Z ; is second buffer byte 0 ? goto more_than_four_elements ; no, go direct to the more than four element commands ; yes, decode only the first byte ; ---- one character, four element regular commands ---- banksel PORTA pagesel $ ifdef include_iambic_mode_code movlw b'11010000' ; a = iambic a mode subwf command_mode_buffer1, W btfsc STATUS, Z goto set_iambic_a_mode movlw b'01010111' ; b = iambic b mode subwf command_mode_buffer1, W btfsc STATUS, Z goto set_iambic_b_mode endif movlw b'01000000' ; e = activate expert commands subwf command_mode_buffer1, W btfsc STATUS, Z goto activate_expert_commands ifdef include_needless_feature_code movlw b'01111100' subwf command_mode_buffer1, W btfsc STATUS, Z goto easter_egg endif ifdef include_paddle_reverse_code movlw b'01110100' ; r = toggle paddle reverse subwf command_mode_buffer1, W btfsc STATUS, Z goto call_paddle_reverse_toggle endif movlw b'01010100' ; s = speed mode subwf command_mode_buffer1, W btfsc STATUS, Z goto call_speed_mode ifdef include_tune_mode_code movlw b'11000000' ; t = tuning mode subwf command_mode_buffer1, W btfsc STATUS, Z goto call_tune_mode endif ;include_tune_mode_code ifdef include_bug_code movlw b'11010100' ; u = toggle bug mode subwf command_mode_buffer1, W btfsc STATUS, Z goto call_toggle_bug_mode endif ifdef include_m_and_h_cmd_code movlw b'11110000' ; m = playback memory subwf command_mode_buffer1, W btfsc STATUS, Z goto command_mode_m_activate endif ;include_m_and_h_cmd_code ifdef include_cw_rx_practice_code movlw b'01111101' ; p = rx code practice subwf command_mode_buffer1, W btfsc STATUS, Z goto call_code_rx_practice_mode endif ifdef include_cw_tx_practice_code movlw b'11011111' ; q = send code practice subwf command_mode_buffer1, W btfsc STATUS, Z goto call_code_tx_practice_mode endif ifdef include_m_and_h_cmd_code movlw b'01010101' ; h = program memory subwf command_mode_buffer1, W btfsc STATUS, Z goto command_mode_h_activate endif ;include_m_and_h_cmd_code ifdef include_weighting_code movlw b'11110100' ; w = weighting adjust subwf command_mode_buffer1, W btfsc STATUS, Z goto call_weighting_adjust endif ifdef include_freq_counter_code movlw b'11110111' ; y = frequency counter subwf command_mode_buffer1, W btfsc STATUS, Z goto call_count_frequency endif ifdef include_beacon_code movlw b'01011111' ; z = play beacon once subwf command_mode_buffer1, W btfsc STATUS, Z goto call_play_beacon endif movlw b'11010111' ; x = exit subwf command_mode_buffer1, W btfsc STATUS, Z goto blow_out_of_here btfss expert_commands_on goto wtf ;-------------------- below here is four element or less expert commands ---------------------------------- ifdef include_calibration_code movlw b'01110111' ; c = calibration check subwf command_mode_buffer1, W btfsc STATUS, Z goto call_calibration_check_mode endif ifdef include_toggle_sidetone_on_off_code movlw b'01011100' ; d = toggle sidetone on/off subwf command_mode_buffer1, W btfsc STATUS, Z goto call_sidetone_on_off endif ifdef include_wavesidetone_code movlw b'01110101' ; f = sidetone frequency adjust subwf command_mode_buffer1, W btfsc STATUS, Z goto call_sidetone_freq_adj endif ifdef include_freq_counter_calib_code movlw b'01011101' ; l = frequency counter calibration subwf command_mode_buffer1, W btfsc STATUS, Z goto call_freq_counter_calib_mode endif ;include_freq_counter_calib_code ifdef include_freq_counter_code movlw b'11010101' ; v = V command (two character command) subwf command_mode_buffer1, W btfsc STATUS, Z goto command_mode_v_activate endif ;include_freq_counter_code ifdef include_freq_counter_code movlw b'11011100' ; k = K command (two character command) subwf command_mode_buffer1, W btfsc STATUS, Z goto command_mode_k_activate endif ;include_freq_counter_code goto wtf ;-------------------- end of four element or less expert commands ---------------------------------- more_than_four_elements ;------------------ more than four element commands -------------------------- banksel PORTA pagesel $ movlw b'01010000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_question_command movlw b'01011111' ; ? = query speed subwf command_mode_buffer1, W btfsc STATUS, Z goto call_announce_speed_wpm skip_over_question_command btfss expert_commands_on goto wtf more_than_four_elements_expert ;---------------------------- more than four element expert commands ------------- ifdef include_freq_counter_code movlw b'11110000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_comma_command movlw b'11110101' ; comma = toggle 8 digit/3 digit mode subwf command_mode_buffer1, W btfsc STATUS, Z goto call_toggle_digit_mode skip_over_comma_command endif ;--------------------- end of all commands ---------------------------- wtf ; I don't know what the hell you sent movlw b'01011111' ; send ? movwf cw_char_to_send+0 movlw b'01010000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 banksel PORTA pagesel $ ifdef include_freq_counter_code bcf command_mode_v_cmd ; clear out any in progress multicharacter commands bcf command_mode_k_cmd endif ;include_freq_counter_code goto initialize_command_mode ; ---- exit routines after commands ---- blow_out_of_here ifdef include_funky_beeps_code errorlevel -306 pagesel beep_boop call beep_boop errorlevel +306 banksel PORTA pagesel $ else movlw b'11010101' ; send BK in CW movwf cw_char_to_send+0 ;movlw b'11011100' movlw b'10110111' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 banksel PORTA pagesel $ endif ;include_funky_beeps_code blow_out_of_here_no_bk ;set things back to normal bcf dit_buffer bcf dah_buffer bsf key_tx_active ifdef include_nnnnn_code clrf ditdah_history1 clrf ditdah_history2 endif return loop_while_mode_pressed_exit btfss mode_switch ; wait until switch is released goto loop_while_mode_pressed_exit errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 banksel PORTA ;bsf key_tx_active pagesel blow_out_of_here goto blow_out_of_here ; ---- handle dits and dahs from user ---- dit_pressed ; sound a dit bsf sending_dit errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 banksel PORTA pagesel $ bsf paddle_was_hit bcf dit_buffer pagesel add_to_buffer goto add_to_buffer dah_pressed ; sound a dah bcf sending_dit errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 banksel PORTA pagesel $ bsf paddle_was_hit bcf dah_buffer add_to_buffer bsf STATUS, C rrf command_mode_buffer1, F rrf command_mode_buffer2, F bcf STATUS, C ; if it's a dit, put a 0 in next btfss sending_dit bsf STATUS, C ; otherwise, put a 1 in next rrf command_mode_buffer1, F rrf command_mode_buffer2, F pagesel init_command_mode_loop_vars goto init_command_mode_loop_vars ;--------------------- double character commands --------------------------- ifdef include_m_and_h_cmd_code command_mode_m_commands ifdef include_eis_mh_cmd_code ;-----------------------New Code from W0ANM---------------------- movlw b'01000000' ; me EIS code uses e,i and s instead of 0,1, and 2 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_0 movlw b'01010000' ; mi subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_1 movlw b'01010100' ; ms subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_2 goto wtf else ;include_eis_mh_cmd_code ;-----------------------Original Code-------------------------------- movlw b'11000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_m0_command movlw b'11111111' ; m0 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_0 skip_over_m0_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_m1_command movlw b'11111111' ; m1 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_1 skip_over_m1_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto wtf movlw b'11111101' ; m2 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_playback_memory_2 goto wtf endif ;include_eis_mh_cmd_code endif ;include_m_and_h_cmd_code ifdef include_m_and_h_cmd_code command_mode_h_commands ifdef include_eis_mh_cmd_code ;-----------------------New Code from W0ANM ---------------------- movlw b'01000000' ; he EIS code uses e,i and s instead of 0,1, and 2 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_0 movlw b'01010000' ; hi subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_1 movlw b'01010100' ; hs subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_2 goto wtf else ;include_eis_mh_code ;------------------------Original Code---------------------------------- movlw b'11000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_h0_command movlw b'11111111' ; h0 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_0 skip_over_h0_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_h1_command movlw b'11111111' ; h1 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_1 skip_over_h1_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto wtf movlw b'11111101' ; h2 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_program_memory_2 goto wtf endif ;include_eis_mh_cmd_code endif ;include_m_and_h_cmd_code ifdef include_freq_counter_code command_mode_v_commands ; V commands : frequency counter offset mode movlw b'11000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_v0_command movlw b'11111111' ; v0 = no freq counter offset subwf command_mode_buffer1, W btfsc STATUS, Z goto call_v0_mode skip_over_v0_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_v1_command movlw b'11111111' ; v1 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_v1_mode skip_over_v1_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto skip_over_v2_command movlw b'11111101' ; v2 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_v2_mode skip_over_v2_command movlw b'01000000' subwf command_mode_buffer2, W btfss STATUS,Z goto wtf movlw b'11110101' ; v3 subwf command_mode_buffer1, W btfsc STATUS, Z goto call_v3_mode goto wtf endif ;include_freq_counter_code ifdef include_freq_counter_code command_mode_k_commands ; K command : set frequency counter offset ; the K command is K + digits, terminated with an X (Example: K123X movfw command_mode_buffer2 ; check for the X which means time to blow out btfss STATUS,Z goto not_x_character movlw b'11010111' subwf command_mode_buffer1, W btfsc STATUS, Z goto exit_out_of_k_command not_x_character movlw b'11000000' subwf command_mode_buffer2, W btfsc STATUS,Z goto k_6_7_8_9_0 movlw b'01000000' subwf command_mode_buffer2, W btfsc STATUS,Z goto k_1_2_3_4_5 goto wtf k_1_2_3_4_5 movlw b'11111111' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_1 movlw d'1' goto add_to_freq_offset skip_k_1 movlw b'11111101' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_2 movlw d'2' goto add_to_freq_offset skip_k_2 movlw b'11110101' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_3 movlw d'3' goto add_to_freq_offset skip_k_3 movlw b'11010101' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_4 movlw d'4' goto add_to_freq_offset skip_k_4 movlw b'01010101' subwf command_mode_buffer1, W btfss STATUS, Z goto wtf movlw d'5' goto add_to_freq_offset k_6_7_8_9_0 movlw b'01010101' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_6 movlw d'6' goto add_to_freq_offset skip_k_6 movlw b'01010111' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_7 movlw d'7' goto add_to_freq_offset skip_k_7 movlw b'01011111' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_8 movlw d'8' goto add_to_freq_offset skip_k_8 movlw b'01111111' subwf command_mode_buffer1, W btfss STATUS, Z goto skip_k_9 movlw d'9' goto add_to_freq_offset skip_k_9 movlw b'11111111' subwf command_mode_buffer1, W btfss STATUS, Z goto wtf movlw d'0' goto add_to_freq_offset add_to_freq_offset movwf temp_w ; save w which is the number that was sent movlw d'4' ; rotate the BCD digits to the left four bits to make room for new digit movwf counter1 bcf STATUS,C add_to_freq_offset_loop rlf BCD2,F rlf BCD1,F rlf BCD0,F decf counter1,F btfss STATUS,Z goto add_to_freq_offset_loop movfw temp_w ; add the new char to lower nibble of BCD2 addwf BCD2,F pagesel initialize_command_mode goto initialize_command_mode exit_out_of_k_command errorlevel -306 pagesel bcd2binary call bcd2binary ;convert what was sent (now in BCD) to two byte binary errorlevel +306 movfw bcd2binary_binary_out+0 movwf freq_offset_binary+0 movfw bcd2binary_binary_out+1 movwf freq_offset_binary+1 errorlevel -306 pagesel store_freq_offset_in_eeprom call store_freq_offset_in_eeprom ;save to EEPROM errorlevel +306 bcf command_mode_k_cmd ; clear out K command mode flag goto acknowledgement_dit endif ;include_freq_counter_code ;------------------ end of double character commands --------------------------- ; ---- command mode activates ---- ; these commands are multiple character commands ; bit flags are set to indicate we're in the middle of a multiple character command ifdef include_freq_counter_code command_mode_v_activate ; V command : frequency counter offset mode bsf command_mode_v_cmd goto initialize_command_mode endif ;include_freq_counter_code ifdef include_freq_counter_code command_mode_k_activate ; K command : set frequency counter offset bsf command_mode_k_cmd clrf BCD0 clrf BCD1 clrf BCD2 goto initialize_command_mode endif ;include_freq_counter_code ifdef include_m_and_h_cmd_code command_mode_m_activate ; M command : play memory # bsf command_mode_m_cmd goto initialize_command_mode endif ;include_m_and_h_cmd_code ifdef include_m_and_h_cmd_code command_mode_h_activate ; H command : program memory # bsf command_mode_h_cmd goto initialize_command_mode endif ;include_m_and_h_cmd_code activate_expert_commands ; E command : activates expert commands bsf expert_commands_on ifdef include_funky_beeps_code errorlevel -306 pagesel high_beep call high_beep pagesel high_beep call high_beep pagesel high_beep call high_beep errorlevel +306 banksel PORTA pagesel $ else bsf sending_dit call send_dit_or_dah call send_dit_or_dah call send_dit_or_dah endif ;include_funky_beeps_code pagesel initialize_command_mode goto initialize_command_mode ; --- calls to command subroutines ---- ifdef include_bug_code call_toggle_bug_mode call toggle_bug_mode call write_eeprom_settings1 goto acknowledgement_dit endif ifdef include_freq_counter_code call_count_frequency errorlevel -306 pagesel count_frequency call count_frequency errorlevel +306 goto initialize_command_mode call_toggle_digit_mode call toggle_digit_mode errorlevel -306 pagesel write_eeprom_settings1 call write_eeprom_settings1 errorlevel +306 goto initialize_command_mode call_v0_mode call v0_mode bcf command_mode_v_cmd goto acknowledgement_dit call_v1_mode call v1_mode bcf command_mode_v_cmd goto acknowledgement_dit call_v2_mode call v2_mode bcf command_mode_v_cmd goto acknowledgement_dit call_v3_mode call v3_mode bcf command_mode_v_cmd goto acknowledgement_dit endif ;include_freq_counter_code call_speed_mode errorlevel -306 pagesel speed_mode call speed_mode errorlevel +306 errorlevel -306 pagesel announce_speed_wpm call announce_speed_wpm errorlevel +306 goto initialize_command_mode call_announce_speed_wpm errorlevel -306 pagesel announce_speed_wpm call announce_speed_wpm errorlevel +306 goto initialize_command_mode ifdef include_tune_mode_code call_tune_mode call tune_mode goto initialize_command_mode endif ;include_tune_mode_code ifdef include_calibration_code call_calibration_check_mode call calibration_check_mode goto initialize_command_mode endif ; include_calibration_code ifdef include_m_and_h_cmd_code call_program_memory_0 errorlevel -306 pagesel program_memory_0 call program_memory_0 errorlevel +306 bcf command_mode_h_cmd goto initialize_command_mode call_program_memory_1 errorlevel -306 pagesel program_memory_1 call program_memory_1 errorlevel +306 bcf command_mode_h_cmd goto initialize_command_mode call_program_memory_2 errorlevel -306 pagesel program_memory_2 call program_memory_2 errorlevel +306 bcf command_mode_h_cmd goto initialize_command_mode call_playback_memory_0 errorlevel -306 pagesel playback_memory_0 call playback_memory_0 errorlevel +306 bcf command_mode_m_cmd goto initialize_command_mode call_playback_memory_1 errorlevel -306 pagesel playback_memory_1 call playback_memory_1 errorlevel +306 bcf command_mode_m_cmd goto initialize_command_mode call_playback_memory_2 errorlevel -306 pagesel playback_memory_2 call playback_memory_2 errorlevel +306 bcf command_mode_m_cmd goto initialize_command_mode endif ;include_m_and_h_cmd_code ifdef include_beacon_code call_play_beacon errorlevel -306 pagesel play_beacon call play_beacon errorlevel +306 goto initialize_command_mode endif ;include_beacon_code ifdef include_iambic_mode_code set_iambic_a_mode bcf iambic_b_mode errorlevel -306 pagesel write_eeprom_settings1 call write_eeprom_settings1 errorlevel +306 goto acknowledgement_dit set_iambic_b_mode bsf iambic_b_mode errorlevel -306 pagesel write_eeprom_settings1 call write_eeprom_settings1 errorlevel +306 goto acknowledgement_dit endif ;include_iambic_mode_code ifdef include_wavesidetone_code call_sidetone_freq_adj errorlevel -306 pagesel sidetone_freq_adj call sidetone_freq_adj errorlevel +306 goto initialize_command_mode endif ;include_wavesidetone_code ifdef include_toggle_sidetone_on_off_code call_sidetone_on_off call sidetone_on_off errorlevel -306 pagesel write_eeprom_settings1 call write_eeprom_settings1 errorlevel +306 goto acknowledgement_dit endif ;include_toggle_sidetone_on_off_code ifdef include_weighting_code call_weighting_adjust call weighting_adjust goto initialize_command_mode endif ;include_weighting_code ifdef include_paddle_reverse_code call_paddle_reverse_toggle call paddle_reverse_toggle goto acknowledgement_dit endif ;include_paddle_reverse_code ifdef include_needless_feature_code easter_egg movlw b'11011100' ; K movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'01010111' ; 3 movwf cw_char_to_send+0 movlw b'11000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'11010000' ; N movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'11110100' ; G movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 goto initialize_command_mode endif ;include_needless_feature_code ifdef include_cw_rx_practice_code call_code_rx_practice_mode call code_rx_practice_mode goto initialize_command_mode endif ;include_cw_rx_practice_code ifdef include_cw_tx_practice_code call_code_tx_practice_mode call code_tx_practice_mode goto initialize_command_mode endif ;include_cw_tx_practice_code ifdef include_freq_counter_calib_code call_freq_counter_calib_mode ifdef include_function_button_code call freq_counter_calib_mode endif ;include_function_button_code goto initialize_command_mode endif ;include_freq_counter_calib_code ; ---- end of calls to command subroutines ---- acknowledgement_dit movlw 0x03 errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 banksel PORTA bsf sending_dit errorlevel -306 pagesel send_dit_or_dah call send_dit_or_dah errorlevel +306 banksel PORTA pagesel initialize_command_mode goto initialize_command_mode ; ------------------ end of check_mode_button --------------------------------------------------- ifdef include_freq_counter_code v0_mode bcf offset0 bcf offset1 return v1_mode bcf offset0 bsf offset1 return v2_mode bsf offset0 bcf offset1 return v3_mode bsf offset0 bsf offset1 return endif ;include_freq_counter_code ; ------------------ ifdef include_freq_counter_calib_code ifdef include_function_button_code freq_counter_calib_mode bcf bit_temp ; store eight_digit_mode so it can be restored later btfsc eight_digit_mode bsf bit_temp bsf eight_digit_mode freq_counter_calib_mode_loop btfsc switch0 ; is freq button pressed ? goto freq_calib_check_paddles errorlevel -306 pagesel count_frequency btfss switch1 call count_frequency errorlevel +306 freq_calib_check_paddles btfss dit ; check dit and dah paddles goto decrement_freq_calib btfss dah goto increment_freq_calib btfsc mode_switch goto freq_counter_calib_mode_loop ; write settings to eeprom movlw eeprom_freq_calib_high errorlevel -302 banksel EEADR movwf EEADR errorlevel +302 banksel PORTA movfw frequency_counter_calib+0 errorlevel -302 banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA errorlevel -306 pagesel write_eeprom call write_eeprom errorlevel +306 movlw eeprom_freq_calib_low errorlevel -302 banksel EEADR movwf EEADR errorlevel +302 banksel PORTA movfw frequency_counter_calib+1 errorlevel -302 banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA errorlevel -306 pagesel write_eeprom call write_eeprom errorlevel +306 ; restore original eight_digit_mode value btfsc bit_temp return bcf eight_digit_mode return decrement_freq_calib ifdef include_funky_beeps_code errorlevel -306 pagesel high_beep call high_beep errorlevel +306 else bsf sending_dit call send_dit_or_dah endif errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 movlw 0xff decf frequency_counter_calib+1, F subwf frequency_counter_calib+1, W ; did we roll over to FF ? btfsc STATUS,Z decf frequency_counter_calib+0, F goto freq_counter_calib_mode increment_freq_calib ifdef include_funky_beeps_code errorlevel -306 pagesel low_beep call low_beep errorlevel +306 else bsf sending_dit call send_dit_or_dah endif errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 incfsz frequency_counter_calib+1, F goto freq_counter_calib_mode incf frequency_counter_calib+0, F goto freq_counter_calib_mode endif ;include_function_button_code endif ;include_freq_counter_calib_code ; ------------------ ifdef include_freq_counter_code toggle_digit_mode btfsc eight_digit_mode goto go_to_three_digit_mode bsf eight_digit_mode ifdef include_funky_beeps_code errorlevel -306 pagesel high_beep call high_beep errorlevel +306 else bsf sending_dit call send_dit_or_dah call send_dit_or_dah endif ;include_funky_beeps_code return go_to_three_digit_mode bcf eight_digit_mode ifdef include_funky_beeps_code errorlevel -306 pagesel low_beep call low_beep errorlevel +306 else bsf sending_dit call send_dit_or_dah call send_dit_or_dah endif ;include_funky_beeps_code return endif ;include_freq_counter_code ; ------------------ ifdef include_bug_code toggle_bug_mode btfsc bug_mode_on goto clear_bug_mode bsf bug_mode_on return clear_bug_mode bcf bug_mode_on return endif ;include_bug_code ; ------------------ ifdef include_cw_tx_practice_code code_tx_practice_mode bsf sending_dit call send_dit_or_dah code_tx_practice_mode_loop errorlevel -306 pagesel check_paddles call check_paddles ; check if the paddles are active and fill buffers as needed errorlevel +306 call send_buffer ; send any dits and dahs if the buffers have stuff btfsc mode_switch goto code_tx_practice_mode_loop code_tx_mode_sw_loop btfss mode_switch goto code_tx_mode_sw_loop bcf dit_buffer bcf dah_buffer call pause_100_ms bsf sending_dit call send_dit_or_dah return endif ;include_cw_tx_practice_code ; ------------------ ifdef include_cw_rx_practice_code get_random_number16 rlf randomnum+0,W xorwf randomnum+0,W movwf temp_w rlf temp_w, F movfw temp_w swapf randomnum+0, F swapf randomnum+1,W bcf STATUS,C movwf temp_w rlf temp_w, F movfw temp_w xorwf randomnum+0,W swapf randomnum+0, F andlw 0x01 rlf randomnum+1, F xorwf randomnum+1, F rlf randomnum+0, F return ; ------------------ code_rx_practice_mode clrf BCD0 ; used to keep track of number of characters sent in a group code_rx_practice_loop ;check paddles and mode button call check_for_button_hit sublw 0x01 btfsc STATUS,Z goto code_rx_practice_exit call get_random_number16 ; get random number movfw randomnum movwf temp_memory movlw b'00111111' ; get it under 65 andwf temp_memory, F ; with a bit mask movlw d'34' ; upper limit of the number we want subwf temp_memory, W ; test it btfsc STATUS,C ; is result negative ? goto code_rx_practice_loop ; number wasn't within limit, get another movfw temp_memory ; double the validated random number addwf temp_memory, F ; so we're in the right spot in the lookup table ;send code movfw temp_memory call cw_table ; get the first byte of code movwf cw_char_to_send+0 movfw temp_memory addlw 0x01 ; increment it to get the next byte call cw_table movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 ; pause after five character group incf BCD0,F movfw BCD0 sublw 0x05 btfss STATUS,Z goto code_rx_practice_loop call loop_6_cw_units clrf BCD0 goto code_rx_practice_loop code_rx_practice_exit call loop_check_for_button_hit ; loop until all buttons are off return endif ;include_cw_rx_practice_code ; ------------------ ifdef include_paddle_reverse_code paddle_reverse_toggle btfss paddle_reverse goto set_paddle_reverse bcf paddle_reverse return set_paddle_reverse bsf paddle_reverse return endif ;include_paddle_reverse_code ; ------------------ ifdef include_weighting_code weighting_adjust bsf sending_dit call send_dit_or_dah bcf sending_dit call send_dit_or_dah btfss dit ; dit is pressed, dah is not btfss dah goto skip_decrease_weight call decrease_weight goto weighting_adjust skip_decrease_weight btfss dah ; dah is pressed, dit is not btfss dit goto skip_increase_weight call increase_weight goto weighting_adjust skip_increase_weight btfss mode_switch ; if switch is pressed, get out of loop goto loop_while_pressed3 btfss dit ; if both paddles are pressed, this also exits btfsc dah goto weighting_adjust loop_while_pressed3 ; wait for mode_switch, dit and dah to be released call pause_100_ms btfss mode_switch goto loop_while_pressed3 btfss dit goto loop_while_pressed3 btfss dah goto loop_while_pressed3 ; clear buffers bcf dit_buffer bcf dah_buffer return increase_weight movfw speed_wpm_dah sublw d'4' ; 5 wpm lower limit btfsc STATUS, C ;if 4 - speed_wpm > 0, return and do not decrement return decf speed_wpm_dah, F call calculate_cw_unit_values return decrease_weight movfw speed_wpm_dah sublw d'60' ; 60 wpm upper limit btfss STATUS, C ;if 60 - speed_wpm < 0, return and do not increment return incf speed_wpm_dah, F call calculate_cw_unit_values return endif ;include_weighting_code ; ------------------ ifdef include_toggle_sidetone_on_off_code sidetone_on_off errorlevel -306 pagesel clear_sidetone_off_during_tx btfsc sidetone_off_during_tx goto clear_sidetone_off_during_tx errorlevel +306 bsf sidetone_off_during_tx return clear_sidetone_off_during_tx bcf sidetone_off_during_tx return endif ;include_toggle_sidetone_on_off_code ; ------------------ ifdef include_wavesidetone_code freq_up movfw wavesidetone_counter_setting sublw d'19' ; lower limit btfss STATUS, Z decf wavesidetone_counter_setting, F return endif ; ------------------ ifdef include_wavesidetone_code freq_down movfw wavesidetone_counter_setting sublw d'77' ; upper limit btfss STATUS, Z incf wavesidetone_counter_setting, F return endif ; ------------------ ifdef include_wavesidetone_code sidetone_freq_adj bcf sending_dit bsf sidetone sidetone_loop errorlevel -306 pagesel loop_cw_unit call loop_cw_unit errorlevel +306 btfss dit ; dit is pressed, dah is not btfss dah goto skip_freq_up call freq_up goto sidetone_loop skip_freq_up btfss dah ; dah is pressed, dit is not btfss dit goto skip_freq_down call freq_down goto sidetone_loop skip_freq_down errorlevel -306 pagesel loop_while_pressed_freq_adj btfss mode_switch ; if switch is pressed, get out of loop goto loop_while_pressed_freq_adj errorlevel +306 btfss dit ; if both paddles are pressed, this also exits btfsc dah errorlevel -306 pagesel sidetone_loop goto sidetone_loop errorlevel +306 loop_while_pressed_freq_adj ; wait for mode_switch, dit and dah to be released bcf sidetone bcf wavesidetone errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 btfss mode_switch goto loop_while_pressed_freq_adj btfss dit goto loop_while_pressed_freq_adj btfss dah goto loop_while_pressed_freq_adj ; write wavesidetone freq to eeprom movlw eeprom_wavesidetone_setting errorlevel -302 banksel EEADR movwf EEADR errorlevel +302 banksel PORTA movfw wavesidetone_counter_setting errorlevel -302 banksel EEDATA movwf EEDATA errorlevel +302 banksel PORTA errorlevel -306 pagesel write_eeprom call write_eeprom errorlevel +306 bcf dit_buffer bcf dah_buffer return endif ; ------------------ ifdef include_beacon_code check_beacon_mode ; see if we should go into infinite loop beacon mode btfsc dit ; if dit is keyed, go into beacon loop return bsf in_beacon_mode ifdef include_beacon_qrss_code ; initialize beacon cycle counter if qrss ids are enabled movlw beacon_qrss_cycles movwf beacon_cycle endif ;include_beacon_qrss_code beacon_mode_loop ; loop through the beacon continuosly ifdef include_txwake_line_code bsf txwake ;endif movlw d'3' ; wait a bit to allow tx to come up errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 endif call play_beacon ; run the beacon ifdef include_txwake_line_code movlw d'4' ; txwake hang time errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 ;ifdef include_txwake_line_code bcf txwake endif ifdef include_beacon_serial_ctrl_code call check_serial_beacon_control endif ;include_beacon_serial_ctrl_code ifdef include_beacon_sleep_time movlw beacon_sleep_time_100ms ; beacon sleep time errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 endif ;include_beacon_sleep_time goto beacon_mode_loop endif ;include_beacon_code ; ------------------ ifdef include_beacon_serial_ctrl_code check_serial_beacon_control btfsc PIR1,OERR ; did we overrun the buffer? goto handle_ser_beacon_overrun btfsc PIR1,FERR ; did we have a frame error? goto handle_ser_beacon_frame_error btfss PIR1,RCIF ; do we have something in the buffer? return ; no, blow out of here movfw RCREG ; pull byte out of the serial rx FIFO sublw d'48' ; was a zero sent? btfsc STATUS,Z goto beacon_sleep_loop ; yes, sleep in a loop return ; no, get out of here handle_ser_beacon_overrun movfw RCREG return handle_ser_beacon_frame_error bcf RCSTA,CREN bsf RCSTA,CREN return beacon_sleep_loop btfsc PIR1,OERR ; did we overrun the buffer? call handle_ser_beacon_overrun btfsc PIR1,FERR ; did we have a frame error? call handle_ser_beacon_frame_error btfss PIR1,RCIF ; do we have something in the buffer? goto beacon_sleep_loop movfw RCREG ; pull byte out of the serial rx FIFO sublw d'49' ; was a one sent? btfsc STATUS,Z return ; yes, get out of here goto beacon_sleep_loop ; no, continue in loop endif ;include_beacon_serial_ctrl_code ; ------------------ ifdef include_fox_code check_fox_mode ; see if we should go into infinite loop fox beacon mode btfsc dit ; if dit is keyed, check mode button return btfsc mode_switch ; if mode_switch is low, go into fox beacon mode return fox_mode_loop ifdef include_txwake_line_code bsf txwake endif movlw d'3' ; wait a bit to allow tx to come up errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 call play_beacon ; run the beacon movlw d'4' ; txwake hang time errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 ifdef include_txwake_line_code bcf txwake endif movlw d'10' ; beacon sleep time errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 goto fox_mode_loop endif ;include_fox_code ; ------------------ ifdef include_beacon_code play_beacon ; this subroutine plays the beacon once pagesel send_vvv_de_id errorlevel -306 call send_vvv_de_id errorlevel +306 ifdef include_beacon_serial_ctrl_code call check_serial_beacon_control endif ;include_beacon_serial_ctrl_code ifdef include_beacon_play_mem_1_code errorlevel -306 pagesel playback_memory_1 call playback_memory_1 errorlevel +306 ifdef include_beacon_serial_ctrl_code call check_serial_beacon_control endif ;include_beacon_serial_ctrl_code errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units errorlevel +306 ;pagesel send_vvv_de_id ;errorlevel -306 ;call send_vvv_de_id ;errorlevel +306 ifdef include_beacon_serial_ctrl_code call check_serial_beacon_control endif ;include_beacon_serial_ctrl_code endif ;include_beacon_play_mem_1_code ifdef include_beacon_play_mem_2_code errorlevel -306 pagesel playback_memory_2 call playback_memory_2 errorlevel +306 ifdef include_beacon_serial_ctrl_code call check_serial_beacon_control endif ;include_beacon_serial_ctrl_code errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units errorlevel +306 ;pagesel send_vvv_de_id ;errorlevel -306 ;call send_vvv_de_id ;errorlevel +306 ifdef include_beacon_serial_ctrl_code pagesel check_serial_beacon_control errorlevel -306 call check_serial_beacon_control errorlevel +306 endif ;include_beacon_serial_ctrl_code endif ;include_beacon_play_mem_2_code ifdef include_beacon_qrss_code decf beacon_cycle,F btfss STATUS,Z goto no_qrss_id_this_time movfw speed_wpm ; store the normal wpm value in temp register movwf temp ifdef include_weighting_code movfw speed_wpm_dah movwf speed_wpm_dah_beacon_temp movlw beacon_qrss_speed ; shift to qrss speed movwf speed_wpm_dah endif ;include_weighting_code movlw beacon_qrss_speed movwf speed_wpm pagesel calculate_cw_unit_values call calculate_cw_unit_values pagesel playback_memory_0 call playback_memory_0 ; send callsign pagesel loop_cw_unit call loop_cw_unit pagesel loop_cw_unit call loop_cw_unit pagesel loop_cw_unit call loop_cw_unit errorlevel +306 movfw temp ; restore normal wpm value movwf speed_wpm ifdef include_weighting_code movfw speed_wpm_dah_beacon_temp movwf speed_wpm_dah endif ;include_weighting_code pagesel calculate_cw_unit_values call calculate_cw_unit_values movlw beacon_qrss_cycles movwf beacon_cycle no_qrss_id_this_time endif ;include_beacon_qrss_code return send_vvv_de_id movlw b'00000000' movwf cw_char_to_send+1 movlw b'01010111' ; V movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'01010111' ; V movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'01010111' ; V movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'11010100' ; D movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'01000000' ; E movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw pagesel playback_memory_0 call playback_memory_0 ; send callsign pagesel loop_cw_unit call loop_cw_unit pagesel loop_cw_unit call loop_cw_unit pagesel loop_cw_unit call loop_cw_unit errorlevel +306 movlw b'11010111' ; / movwf cw_char_to_send+0 movlw b'01000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'11010101' ; B movwf cw_char_to_send+0 movlw b'00000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw pagesel loop_6_cw_units call loop_6_cw_units errorlevel +306 return endif ;include_beacon_code ; ------------------ ifdef include_function_button_code playback_memory_loop_delay movlw initial_memory_repeat_delay movwf temp_memory bcf memory_playback_manual_exit playback_memory_loop errorlevel -306 pagesel check_for_button_hit call check_for_button_hit ; check for user exit errorlevel +306 sublw 0x01 btfsc STATUS,Z goto playback_memory_loop_user_exit errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 decfsz temp_memory,F goto playback_memory_loop return playback_memory_loop_user_exit bsf memory_playback_manual_exit return endif ;include_function_button_code ; ------------------ ifdef include_aux_line_code toggle_aux_line ; this changes the state of the aux line btfss aux goto set_aux_high bcf aux return set_aux_high bsf aux return endif ;include_aux_line_code ; ------------------ ifdef include_function_button_code check_function_buttons ; this checks if any memory buttons are pressed ; single hit = play memory ifdef include_call_cq_code bcf dit_hit endif ;include_call_cq_code ;check if any button lines are low btfss switch0 goto a_function_button_is_pressed btfss switch1 goto a_function_button_is_pressed btfss switch2 goto a_function_button_is_pressed return ; no buttons pressed, blow out of here a_function_button_is_pressed movlw b'00000111' movwf porta_temp movlw d'30' movwf count function_button_read_loop ; this loop reads the switch lines over and over again to prevent bounce and misreads movfw PORTA ; get port a register andwf porta_temp, F ; and it with a temporary register decfsz count,F goto function_button_read_loop ifdef include_freq_counter_button_code ifdef include_freq_counter_code btfsc porta_temp,0 ; freq counter button: switch0 = 0, switch1 = 0, switch2 = 1 goto no_freq_counter_button_press btfsc porta_temp,1 goto no_freq_counter_button_press btfss porta_temp,2 goto no_freq_counter_button_press bcf key_tx_active call count_frequency bsf key_tx_active goto get_out no_freq_counter_button_press endif ;include_freq_counter_code endif ;include_freq_counter_button_code ifdef include_aux_line_code btfsc porta_temp,0 ; freq counter button: switch0 = 0, switch1 = 0, switch2 = 0 goto aux_button_not_pressed btfsc porta_temp,1 goto aux_button_not_pressed btfsc porta_temp,2 goto aux_button_not_pressed aux_button_wait_loop ; wait for all three lines to go high so we're sure the button has been let go btfss porta_temp,0 goto aux_button_wait_loop btfss porta_temp,1 goto aux_button_wait_loop btfss porta_temp,2 goto aux_button_wait_loop call pause_100_ms call toggle_aux_line goto get_out aux_button_not_pressed endif ;include_aux_line_code btfss switch0 goto handle_switch0 btfss switch1 goto handle_switch1 btfss switch2 goto handle_switch2 return handle_switch0 ifdef include_call_cq_code btfss dit ; was dit hit ? bsf dit_hit ; if so, set bit_temp endif ;include_call_cq_code incf count,F ; button hold check code btfsc switch0 goto switch0_off_now errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 goto handle_switch0 switch0_off_now ifdef include_call_cq_code btfsc dit_hit ; check if dit was hit goto call_call_cq endif ;include_call_cq_code movlw 0x05 subwf count,F btfsc STATUS,C ; 5 or more 100 mS equals a button hold goto playback_memory_0_loop errorlevel -306 pagesel playback_memory_0 call playback_memory_0 errorlevel +306 goto get_out playback_memory_0_loop errorlevel -306 pagesel playback_memory_0 call playback_memory_0 ; play the memory once errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited goto get_out call playback_memory_loop_delay ; do a delay of silence btfsc memory_playback_manual_exit ; check if the user exited goto get_out goto playback_memory_0_loop handle_switch1 incf count,F ; button hold check code btfsc switch1 goto switch1_off_now errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 goto handle_switch1 switch1_off_now movlw 0x05 subwf count,F btfsc STATUS,C ; 5 or more 100 mS equals a button hold goto playback_memory_1_loop errorlevel -306 pagesel playback_memory_1 call playback_memory_1 errorlevel +306 goto get_out playback_memory_1_loop errorlevel -306 pagesel playback_memory_1 call playback_memory_1 ; play the memory once errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited goto get_out call playback_memory_loop_delay ; do a delay of silence btfsc memory_playback_manual_exit ; check if the user exited goto get_out goto playback_memory_1_loop handle_switch2 incf count,F ; button hold check code btfsc switch2 goto switch2_off_now errorlevel -306 pagesel pause_100_ms call pause_100_ms errorlevel +306 goto handle_switch2 switch2_off_now movlw 0x05 subwf count,F btfsc STATUS,C ; 5 or more 100 mS equals a button hold goto playback_memory_2_loop errorlevel -306 pagesel playback_memory_2 call playback_memory_2 ; play the memory once errorlevel +306 goto get_out playback_memory_2_loop errorlevel -306 pagesel playback_memory_2 call playback_memory_2 ; play the memory once errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited goto get_out call playback_memory_loop_delay ; do a delay of silence btfsc memory_playback_manual_exit ; check if the user exited goto get_out goto playback_memory_2_loop get_out bcf dit_buffer bcf dah_buffer return ifdef include_call_cq_code call_call_cq bsf send_cw_preemptible call call_cq bcf send_cw_preemptible goto get_out endif ;include_call_cq_mode endif ;include_function_button_code ; ------------------ ifdef include_call_cq_code call_cq bcf memory_playback_manual_exit call send_cq btfsc memory_playback_manual_exit ; check if the user exited return call send_cq btfsc memory_playback_manual_exit ; check if the user exited return call send_cq btfsc memory_playback_manual_exit ; check if the user exited return movlw b'11010100' ; D movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited return movlw b'01000000' ; E movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited return errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units pagesel playback_memory_0 call playback_memory_0 errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited return errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units pagesel playback_memory_0 call playback_memory_0 errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited return errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units pagesel playback_memory_0 call playback_memory_0 errorlevel +306 btfsc memory_playback_manual_exit ; check if the user exited return errorlevel -306 pagesel loop_6_cw_units call loop_6_cw_units errorlevel +306 movlw b'01110111' ; AR movwf cw_char_to_send+0 movlw b'01000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 return send_cq movlw b'11011101' ; C movwf cw_char_to_send+0 movlw b'00000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'11110111' ; Q movwf cw_char_to_send+0 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 return endif ;include_call_cq_mode ; ------------------ ifdef include_freq_counter_code ifdef include_freq_counter_button_code ifdef include_function_button_code check_freq_counter_mode ; check if frequency button is pressed ; if so, loop forever in frequency counter readout mode ; (this subroutine is called at power up only) btfsc switch0 return btfsc switch1 return bcf key_tx_active freq_counter_loop call count_frequency movlw d'5' errorlevel -306 pagesel pause_w_100ms call pause_w_100ms errorlevel +306 goto freq_counter_loop endif ;include_function_button_code endif ;include_freq_counter_button_code endif ;include_freq_counter_code ; ------------------ ifdef include_txwake_line_code check_txwake_clear_time ;if there's stuff in a buffer, reset the timer btfsc dit_buffer goto reset_txwake_timer btfsc dah_buffer goto reset_txwake_timer decfsz txwakecounter+1, F return decfsz txwakecounter+0, F return bcf txwake return reset_txwake_timer movlw HIGH _txwake_delay_time movwf txwakecounter+0 movlw LOW _txwake_delay_time movwf txwakecounter+1 return endif ; ------------------ check_startup_modes ; see if any of the special startup modes have been invoked by the user at power up btfss dit btfsc dah goto check_other_modes goto reset_to_defaults ; if both dit and dah lines are low, reset to defaults check_other_modes ifdef include_fox_code pagesel check_fox_mode errorlevel -306 call check_fox_mode errorlevel +306 endif errorlevel -306 pagesel check_straight_key_mode call check_straight_key_mode ; check if the user wants to go into brain dead straight key mode errorlevel +306 ifdef include_beacon_code errorlevel -306 pagesel check_beacon_mode call check_beacon_mode ; check if the user wants to go into beacon mode errorlevel +306 endif ;include_beacon_code ifdef include_freq_counter_button_code ifdef include_freq_counter_code ifdef include_function_button_code call check_freq_counter_mode ; check if the user is holding the freq button and go into freq count loop mode endif ;include_function_button_code endif ;include_freq_counter_code endif ;include_freq_counter_button_code return ; no startup mode is needed, blow out of here ; ------------------ reset_to_defaults btfss dit ; loop until one paddle is released goto reset_to_defaults btfss dah goto reset_to_defaults errorlevel -306 pagesel initialize_eeprom_first_time call initialize_eeprom_first_time pagesel initialize call initialize errorlevel +306 return ; ------------------ ifdef include_freq_counter_code ; 1. Read the frequency on pin RA4 for 250 mS. The signal goes through the prescaler /32 ; 2. Multiply measurement by 128 [ /32 (prescaler) + /4 (250 mS sample time) ] ; 3. Convert the three byte binary reading to eight BCD digits ; 4. Calculate the reading by applying the offset if applicable ; 5. Readout the frequency in CW either in eight or three digit mode, with leading zero suppression count_frequency errorlevel -302 banksel OPTION_REG movlw b'00110100' ; bit 5 : 1 = RA4 input ; bit 4 : 0 = low to high transistion ; bit 3 : 0 = prescaler assigned to TIMER0 ; bit 2-0 : set prescaler to 100 (/32) movwf OPTION_REG errorlevel +302 banksel PORTA movfw frequency_counter_calib+1 movwf counter1 movfw frequency_counter_calib+0 movwf counter2 clrf freqcount+0 clrf freqcount+1 clrf freqcount+2 bcf INTCON,T0IF clrf TMR0 ; clear TIMER0 and prescaler loop_250_ms movfw TMR0 btfss INTCON,T0IF ; did TIMER0 overflow? goto no_timer0_overflow bcf INTCON,T0IF incfsz freqcount+1, F goto no_overflow_freqcount_1 incf freqcount+0, F goto overflow_freqcount_1 no_timer0_overflow nop nop nop no_overflow_freqcount_1 nop nop overflow_freqcount_1 incf counter1, F btfsc STATUS,Z incf counter2, F btfss STATUS,Z goto loop_250_ms loop_250_ms_done ;movfw TMR0 ; get timer0 as least significant byte movwf freqcount+2 ;multiple by 128 by rotating everything right 7 times movlw d'7' movwf count multiply_loop bcf STATUS,C rlf freqcount+2, F rlf freqcount+1, F rlf freqcount+0, F decfsz count,F goto multiply_loop ;convert 24 bit freqcount to 8 digit BCD bcf STATUS,C ; clear the carry bit movlw d'24' movwf count clrf BCD0 ; most significant clrf BCD1 clrf BCD2 clrf BCD3 ; least significant loop24 rlf freqcount+2, F rlf freqcount+1, F rlf freqcount+0, F rlf BCD3,F rlf BCD2,F rlf BCD1,F rlf BCD0,F decfsz count, F goto adjDEC24 goto calculate_the_reading ; goto send_the_digits_in_cw adjDEC24 movlw BCD3 movwf FSR call adjBCD24 movlw BCD2 movwf FSR call adjBCD24 movlw BCD1 movwf FSR call adjBCD24 movlw BCD0 movwf FSR call adjBCD24 goto loop24 adjBCD24 movlw 0x03 addwf INDF,W movwf temp_memory btfsc temp_memory,3 ; test if result > 7 movwf INDF movlw 0x30 addwf INDF,W movwf temp_memory btfsc temp_memory,7 ; test if result > 7 movwf INDF ; save as MSD return calculate_the_reading ; calculate the reading using the offset btfsc eight_digit_mode ; if we are in eight digit mode, there is no offset applied goto send_the_digits_in_cw btfsc offset0 goto offset0_is_1 btfss offset1 goto send_the_digits_in_cw ; there is no offset activated, skip over pagesel calc_offset_minus_measurement errorlevel -306 goto calc_offset_minus_measurement errorlevel +306 offset0_is_1 pagesel calc_measurement_plus_offset errorlevel -306 btfsc offset1 goto calc_measurement_plus_offset pagesel calc_measurement_minus_offset goto calc_measurement_minus_offset errorlevel +306 send_the_digits_in_cw btfss eight_digit_mode ; are we in eight digit mode ? goto skip_BCD0 ; no, skip over BCD0 ;send BCD0 H swapf BCD0,F movfw BCD0 andlw b'00001111' btfsc STATUS,Z goto skip_BCD0H errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 skip_BCD0H ;send BCD0 L swapf BCD0,F movfw BCD0 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 skip_BCD0 btfsc eight_digit_mode goto no_BCD1H_zero_suppress movfw BCD1 btfsc STATUS,Z goto BCD1_is_zeros no_BCD1H_zero_suppress ;send BCD1 H swapf BCD1,F movfw BCD1 andlw b'00001111' pagesel send_w_BCD_in_cw btfss eight_digit_mode ; skip over zero suppresion if we're in 8 digit mode btfss STATUS,Z ; zero suppression errorlevel -306 call send_w_BCD_in_cw errorlevel +306 skip_BCD1H ;send BCD1 L swapf BCD1,F movfw BCD1 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 BCD1_is_zeros swapf BCD2,F movfw BCD2 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 btfss eight_digit_mode return ; if we are in three digit mode, blow out of here swapf BCD2,F movfw BCD2 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 swapf BCD3,F movfw BCD3 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 swapf BCD3,F movfw BCD3 andlw b'00001111' errorlevel -306 pagesel send_w_BCD_in_cw call send_w_BCD_in_cw errorlevel +306 return ; these routines calculate the frequency readout when the offset is configured ; 0 7 , 0 4 0 , 0 0 0 hertz ;+-----+-----+-----+-----+-----+-----+-----+-----+ ;|BCD0H|BCD0L|BCD1H|BCD1L|BCD2H|BCD2L|BCD3H|BCD3L| ;+-----+-----+-----+-----+-----+-----+-----+-----+ ; | | | ; +-----+-----+-----+ ; | freq_offset | ; +-----+-----+-----+ calc_offset_minus_measurement clrf BCD0 ; in this offset mode we the 10 Mhz and 1 Mhz digits errorlevel -306 pagesel rotate_BCD012_right call rotate_BCD012_right ; rotate the BCD digits one position to the right errorlevel +306 clrf BCD0 errorlevel -306 pagesel binary2bcd call bcd2binary ; convert the BCD measurement to binary errorlevel +306 movfw freq_offset_binary+1 ; store offset in temp registers movwf L_temp movfw freq_offset_binary+0 movwf H_temp movfw bcd2binary_binary_out+1 subwf L_temp,F btfss STATUS,C decf H_temp,F movfw binary2bcd_binary_in+0 subwf H_temp,F movfw L_temp movwf binary2bcd_binary_in+1 movfw H_temp movwf binary2bcd_binary_in+0 errorlevel -306 pagesel binary2bcd call binary2bcd pagesel rotate_BCD012_left call rotate_BCD012_left pagesel send_the_digits_in_cw goto send_the_digits_in_cw errorlevel +306 calc_measurement_minus_offset call rotate_BCD012_right ; rotate the BCD digits one position to the right errorlevel -306 pagesel binary2bcd call bcd2binary ; convert the BCD measurement to binary errorlevel +306 movfw freq_offset_binary+1 subwf bcd2binary_binary_out+1,F btfss STATUS,C decf bcd2binary_binary_out+0,F movfw freq_offset_binary+0 subwf bcd2binary_binary_out+0,F ;bcd2binary_binary_out becomes input parameters for binary2bcd routine errorlevel -306 pagesel binary2bcd call binary2bcd pagesel rotate_BCD012_left call rotate_BCD012_left pagesel send_the_digits_in_cw goto send_the_digits_in_cw errorlevel +306 calc_measurement_plus_offset call rotate_BCD012_right ; rotate the BCD digits one position to the right errorlevel -306 pagesel binary2bcd call bcd2binary ; convert the BCD measurement to binary errorlevel +306 movfw freq_offset_binary+1 addwf bcd2binary_binary_out+1,F btfsc STATUS,C incf bcd2binary_binary_out+0,F movfw freq_offset_binary+0 addwf bcd2binary_binary_out+0,F ;bcd2binary_binary_out becomes input parameters for binary2bcd routine errorlevel -306 pagesel binary2bcd call binary2bcd pagesel rotate_BCD012_left call rotate_BCD012_left pagesel send_the_digits_in_cw goto send_the_digits_in_cw errorlevel +306 endif ;include_freq_counter_code ; ------------------ ifdef include_freq_counter_code rotate_BCD012_right clrf temp_w movlw d'4' movwf count rotate_BCD012_right_loop bcf STATUS,C rrf BCD0,F rrf BCD1,F rrf BCD2,F rrf temp_w,F decf count,F btfss STATUS,Z goto rotate_BCD012_right_loop movfw temp_w ; store the original lower nibble of BCD2 in upper nibble of BCD0 addwf BCD0,F return endif ;include_freq_counter_code ifdef include_rotateBCD012_left ; ------------------ rotate_BCD012_left ;this rotates ; BCD0 <- BCD1 <- BCD2 <- ; | | ; +--------------------+ ; four bits clrf temp_w movlw d'4' movwf count rotate_BCD012_left_loop bcf STATUS,C rlf BCD2,F rlf BCD1,F rlf BCD0,F rlf temp_w,F decf count,F btfss STATUS,Z goto rotate_BCD012_left_loop movfw temp_w ; store the upper nibble of BCD0 in lower nibble of BCD2 addwf BCD2,F return endif ;include_rotateBCD012_left ; ------------------ ifdef include_say_hi_code say_hi ;say hi bcf key_tx_active movlw b'01010101' ; H movwf cw_char_to_send+0 movlw b'00000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 movlw b'01010000' ; I movwf cw_char_to_send+0 movlw b'00000000' movwf cw_char_to_send+1 errorlevel -306 pagesel send_cw call send_cw errorlevel +306 bsf key_tx_active return endif ;include_say_hi_code ; Main Program ------------------------------------------------------------ main_program_start ; this is where the whole shebang starts errorlevel -306 pagesel initialize call initialize ; initialize registers, read from eeprom, etc. pagesel check_startup_modes call check_startup_modes ; see if the user is invoking a special startup mode errorlevel +306 ifdef include_say_hi_code pagesel say_hi call say_hi endif ;include_say_hi_code ifdef include_funky_beeps_code ; this is for debugging / programming purposes ifdef include_debug_code ; do a beep at the beginning to make sure the programming worked errorlevel -306 pagesel low_beep call beep_boop ; this is needed when using in circuit serial programming ;call low_beep ; as there's no way to tell if code was successfully loaded pagesel high_beep call high_beep errorlevel +306 endif ;include_debug_code endif ;include_funky_beeps_code ; now we're done with all the startup stuff, let's send some CW main_loop banksel PORTA ; for good measure errorlevel -306 pagesel check_mode_button call check_mode_button ; check if the mode button is pressed and take care of it pagesel check_paddles call check_paddles ; check if the paddles are active and fill buffers as needed pagesel send_buffer call send_buffer ; send any dits and dahs if the buffers have stuff errorlevel +306 ifdef include_function_button_code pagesel check_function_buttons errorlevel -306 call check_function_buttons ; check if any of the function buttons are pressed and take care of them errorlevel +306 endif ;include_function_button_code ifdef include_txwake_line_code pagesel check_txwake_clear_time errorlevel -306 call check_txwake_clear_time errorlevel +306 endif ifdef include_serial_cw_keyboard pagesel check_serial_cw_keyboard errorlevel -306 call check_serial_cw_keyboard errorlevel +306 endif ;include_serial_cw_keyboard pagesel main_loop goto main_loop ; lather, rinse, repeat end ; End Main Program --------------------------------------------------------