This is a part 3 in my WWVB time signal series (part 2, part 4).
When connecting custom hardware to a computer, I love using a serial port interface. It's widely supported and there are so many tools you can use to connect to it. While it's common to use UART-USB interface chips (e.g. notorious FTDI or my current favorite MCP2221A), I chose to skip that part. As project was using PIC16F1454 that has USB capabilities, it seemed quite sensible to roll one's own serial port interface.
The serial port code is based on Microchip's CDC example but with all unnecessary parts removed. Since I wanted direct communication, all that baud rate stuff could go out. Yes, you can talk to the hardware at any baud rate you want - USB doesn't care nor does my code. A bit unusually, I used polling instead of interrupts for USB. It actually doesn't impact the USB communication speed for this use case and it leaves interrupts clean for timer code that's much more sensitive to timings.
And this code is all about timing. The carrier signal is generated using PWM functionality. Based on the built-in frequency signal (48 MHz), it was actually possible to hit 40 kHz (JJY) and 60 kHz (WWVB, MSF, JJY) PWM frequency exactly. But this was not possible for 77.5 kHz (DCF77) so there we have about 0.1% error. Not great, not terrible.
Once we have the carrier sorted out, we need to have a separate time tracking for attenuation. This is in interrupt happening every 0.2 ms until we "collect" 100 ms. At that moment, a decision is made whether to keep the carrier signal as it is, turn it off, or just attenuate it a bit.
The actual data for all this logic is not done in microcontroller. While it would be possible to do for any of the protocols, this microcontroller simply doesn't have enough programming space to do them all. Therefore, all necessary calculations are done in software and then transferred once a minute to the device.
And that's about it. The software on a computer calculates bit-stream data for the next minute and sends it to the microcontroller. The microcontroller loads that data and keeps accounting for the current minute. Once minute is over, all repeats again. Yes, there are a few details more but you can discover those yourself.
Of course, the source code is available on GitHub.