CW Keying Adapter / Morse keyer with USB Virtual COM Port

Web:    www.qsl.net/dl4yhf/USB_CW_Keying_Adapter/index.htm
Author: Wolfgang Büscher, DL4YHF
Date:   2025-11-14

Under construction !


This file describes a microcontroller project for a CW Keying Adapter for the USB port, with a Virtual COM Port implemented in firmware (to eliminate the need for a USB/UART adapter like the famous FTDI chips). Features:


Contents

  1. Operation
    1. Standalone operation (without USB connection)
    2. USB operation (connected to PC via USB)
  2. Appendix
    1. Firmware
    2. Sourcecode
    3. Block- and Circuit diagram
    4. Links (external)
    5. Disclaimer

1. Operation

1.1 Standalone operation (without USB connection)

1.2 USB operation (connected to PC via USB)

When connected to a PC via USB, the keyer / keying adapter presents itself to the system as a Virtual COM Port (precisely: as a "USB CDC ACM VSP" = "USB Communication Class Device, Abstract Control Model, Virtual Serial Port").
On a Windows PC, the Virtual COM Port will be enumerated like any other "COM" port. Some applications will not only show a new "COM" port in e.g. a list of selectable devices, but also the USB PRODUCT NAME, which is, for the sake of brevity, just "CW Keying Adapter":


Screenshot of the author's 'Remote CW Keyer' with the USB CW Keying Adapter selected.

  ToDo: Describe the other features here ...

4. Appendix

4.1 Firmware

The firmware for CW Keying Adapter was originally developed for a PIC18F25J50, because it was available in homebrew-friendly DIL-28 housing, which unfortunately cannot be said about more advanced microcontrollers with USB.
Anyway, despite being based on a very old 8-bit architecture, this PIC18F family was fast enough for running the USB "CDC" (Communication Class Device) driver, plus the basic 'keyer firmware', and a few extra gadgets that will be described here in future.
  Download the compiled firmware for the PIC18F25J50 here.

Unfortunately, despite having a USB controller built inside, this chip doesn't contain a USB bootloader when shipped from the manufacturer (Microchip) or distributor (e.g. Digi-Key, Mouser, etc).
So you will need a programming adapter supported by Microchip's PIC programmer (or even the IDE, if you want to extend and compile the firmware yourself). Depending on the budget, I suggest a PICkit 4, which is not only a programming adapter but also an in-circuit debugger.
The PICkit 4 is a bit overpriced, so an "MPLAB SNAP" (another programmer / debugger by Microchip) may be an alternative (lower price than a PICkit 3, 4, or 5).
Please note:
The author neither sells kits nor does he run a 'PIC programming service',
so please don't ask me to send a programmed PIC.
Besides the IDE for PIC firmware development and programming (downloading firmware into the target), there's a "standalone" utility named IPE (Integrated Programming Environment) available at Microchip which should make this a bit easier than trying to 'flash' an imported hex file with MPLAB X.


4.2 Sourcecode

... is too ugly for publication yet, but will be available when finished ...

4.3 Block- and Circuit diagram

There's no nice circuit diagram yet, since due to a lack of time, and the author's transition from Eagle to KiCad hasn't taken place yet.
Fortunately, with the PIC18F25J50-I/SP's homebrew friendly 28-pin standard DIL package (through-hole mount, not SMD), it's easy to build on perfboard. For the moment, there is just this snippet from the main module's C sourcecode (!) with an ASCII-drawing of the most vital parts:

// WB's breadboard sketched below, based on the PIC18F46J50/PIC18F25J50
//          datasheet, DS39931D, Page 5 (Pin Diagram for 28-pin SPDIP).
//
//    Not shown here: Application-specific use of the "remaining"
//    I/O pins, e.g. inputs from the Morse key, keying output,
//                   sidetone / "beeper" output,
//                   rotary encoder, TFT display or LED indicators,
//                   'analog potentiometer', SPI, I2C, UART, etc.
//    Even the "XTAL" (external crystal on OSC1/RA7 + OSC2/RA6)
//    later turned out unnecessary, because even "USB Full Speed"
//    worked flawlessly when the 48 MHz clock was provided by
//    the PIC's *internal* oscillator.
// 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//   100 ____
//   ,--|____|-------------------------------------------------------------<-- (1) PICkit4 RESET output
//   |    ,-------------------------------------------------------------,      (2) PICkit4 Target-Vdd (measured)
//   *->--|  1 /MCLR (Reset input, see text)       RB7/KBI3/PGD/RP10 28 |--<-> (4) PICkit4 DATA via 100 Ohm
//        |  2 RA0/AN0/C1INA/ULPWU/RP0              RB6/KBI2/PGC/RP9 27 |--<-- (5) PICkit4 CLOCK via 100 Ohm
//        |  3 RA1/AN1/C2INA/RP1              RB5/KBI1/SDI1/SDA1/RP8 26 |      (3) PICkit4 GROUND
//        |  4 RA2/AN2/Vref-/CVref/C2INB RB4/PMA1/KBI0/SCK1/SCL1/RP7 25 |      _|_ (see PICkit4 pinout below)
// 10uF   |  5 RA3/AN3/Vref+/C1INB             RB3/AN9/CTED2/VPO/RP6 24 |          
//  ,-||--|  6 VddCORE/Vcap               RB2/AN8/CTED1/VMO/REFO/RP5 23 |
//  |     |  7 RA5 /AN4/SS1/HLVDIN/RCV/RP2         RB1/AN10/RTCC/RP4 22 |
//  *-----|  8 Vss (aka GROUND)                    RB0/AN12/INT0/RP3 21 |
// _|_ ,--|  9 OSC1/CLKI/RA7                                     Vdd 20 |--------*---O +3.3V 
//     |,-| 10 OSC2/CLKO/RA6                        Vss (aka GROUND) 19 |----,  _|_
//  XTAL? | 11 RC0/T1OSO/T1CKI/RP11            RC7/RX1/DT1/SDO1/RP18 18 |    |  ___ 100 nF
//        | 12 RC1/T1OSI/UOE/RP12                   RC6/TX1/CK1/RP17 17 |   _|_ _|_  GND
// 3.3V   | 13 RC2/AN11/CTPLS/RP13                         RC5/D+/VP 16 |--> USB D+ : GREEN
//  O--*--| 14 Vusb              PIC18F25J50-I/SP          RC4/D-/VM 15 |--> USB D- : WHITE
//    _|_ '-------------------------------------------------------------'    USB +5V: RED
//    ___ 100 nF (see DS39931D page 360, "USB Internal Transceiver".         USB_GND: BLACK
//    _|_         The PICs "Vusb" MUST NOT BE CONNECTED to the "USB +5V"!)
//
//  PICkit4 pinout: 1=/MCLR/Vpp, 2=Target-Vdd, 3=GROUND, 4=PGD, 5=PGC, 6..9: DON'T CONNECT
//  For 'standalone' operation without debugger control, add this 
//  passive circuitry for /MCLR shown in the PIC18F46J50 datasheet
//  (DS39931D page 29, "FIGURE 2-1: RECOMMENDED MINIMUM CONNECTIONS") :
//                      ____         ____
//   Vdd (+3.3 V) O----|____|---*---|____|----> "/MCLR" (PIC pin 1)
//                     100 kOhm |   1 kOhm
//                             _|_        \__ prevents shunting "MCLR/Vpp"
//                             ___ 100 nF     to ground via the cap, because 
//                              |             ICSP needs fast switching of MCLR !
//                             _|_ GND
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

For strange reasons, the official "Universal Serial Bus Communications Class Subclass Specification for PSTN Devices" (PSTN120.pdf) does not support the RS-232 "CTS" signal in what the spec calls "UART State Bitmap Values".
People speculated that bit 7 of the "UART State Bitmap" (byte) can be used for CTS, but when tried, this neither worked with Windows nor with Linux.
Thus the keyer's paddle in- and outputs were mapped to the following inputs of the Virtual COM Port (USB CDC), and 'real' (digital) in- and outputs of the first prototypes (note the absence of "CTS"):
 
   Keyer signal | USB CDC signal | PIC18F25J50 pin number, port 
----------------+----------------+---------------------------------
 "Dot"  input   |  DCD           | 25, "RB4", with internal pull-up
 "Dash" input   |  DSR           | 26, "RB5", with internal pull-up
 Keying output  |   -   (**)     | 23, "RB2" H=carrier on, L=off   (*)
 PTT    output  |   -   (**)     | 24, "RB3" H=transmit, L=receive (*)


(*) These "digital" outputs at the PIC are intended to drive
    NPN transistors or N-Channel MOSFETs, thus "H" = active.

(**) At the time of this writing, it wasn't clear if the two
     precious 'emulated outputs' (DTR, RTS, from host to device)
     would be used to drive these 'digital outputs' on the PIC,
     in addition to driving them from the planned 'Elbug emulator'.
     Most likely, the keying- and PTT output can be controlled
     from the host via special test command (character string),
     to reduce the risk of the transceiver starting to send
     just because an application was 'talking to the wrong COM
     port'.


PIC Notes by DL4YHF   (with a lot of curses about a bulky IDE, and a long journey to get along with its bugs)

4.6 Disclaimer

The author provides this software "AS IS" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose.
The entire risk as to the quality and performance is with you. In no event unless required by applicable law will the author and/or any other party who may modify and/or redistribute this software be liable to you for damages, including any lost profits, lost monies, or other special, incidental or consequential damages arising out of the use or inability to use this package, or for any claim by any other party.

This program is still "under construction", and there are certainly a number of bugs lurking in the code. The entire risk is with you. You may find udates at the DL4YHF website.