Overview
After not so much deliberation, we decided that the firmware of the Phone Call Monitoring Facility should be written in C and built with the
MPLAB C18 compiler suite. This is one of the reasons we picked the more powerful PIC18F4520 Microchip MCU, which also gave Monica a
chance to get better at C programming.
The firmware code can be found at the PCMF GitHub repository.
Firmware Architecture
The essence of the PCMF firmware is the event-driven Main Loop, which analyzes the ring signal on the line, counts the incoming calls and
updates the display with the results. The events themselves are generated by various interrupts. The operation of these interrupts and the Main
Loop are explained in detail below.
Timer 1 Capture for Ring Signal Edge
The output of the optocoupler represents one half cycle of the ring signal in the MCU's power domain. To make the [rising] edge of these pulses
cause an interrupt, a simple option would have been to connect the signal to one of the MCU's INTn inputs. However, in
order to better understand the exact requirements, we decided to measure the period of ring pulses using the PIC's Capture/Compare/PWM module
in Capture Mode, clocked by the Timer 1 module. In simpler terms, Timer 1 is used as a stopwatch to measure the time between
consecutive pulses. The CCP module also generates an interrupt upon each capture, i.e., on the rising edge of each pulse, which is what we really
needed. The high-priority interrupt service routine that fires in this case resets Timer 1 for the next pulse width measurement and
informs the Main Loop of the event.
While monitoring the width of pulses is potentially a good way to distinguish valid ring signals from noise on the line, it ended up being
unnecessary. As we learned it from James Darwood's patent,
the primary (and probably only real) aggressor, the dialing pulses from an old rotary phone, cannot be set apart from the ring signal just on the
basis of pulse width; because of this, our algorithm also uses pulse-counting instead.
Timer 1 Overflow for Gap Measurement
The next challenge is identifying the ring pulses that belong to the same ring, so that the number of rings in a given incoming call attempt can be
counted. This part of our algorithm is based on the measurement of the elapsed time between ring pulses. In the United States, the ring
cadence is defined as 2 s of ringing, separated by 4 s of silence; however, the different distinctive ring patterns use periods
of silence shorter than 4 s within the same ring. Because of this, the firmware assumes that pulses separated by less than 1.3 s
(called Ring Gap Timeout) belong to the same ring, while if no pulses arrive for more than or equal to 4.5 s (called Call Gap
Timeout), the current call is considered over. All other gaps are interpreted as silence between rings within the same call.
The ring pulse gaps are measured using Timer 1's overflow interrupt. We are using Timer 1 in 16-bit mode, clocked from the 2 MHz instruction
clock (¼ of the internal 8 MHz oscillator frequency) through 1:8 prescaling. The overflow of this free-running timer occurs every
262 ms (referred to as an eon in our terminology), which is a sufficient resolution to properly identify the nature of a ring gap.
Considering that this is a low-frequency event with no urgent tasks to complete, this interrupt is masked and does not have a corresponding ISR; its
flag is acknowledged, then cleared from the Main Loop.
Although the timer could be stopped after the completion of a call and only restarted upon the next ring pulse, we decided to keep it ticking and
triggering interrupts, which makes the red "heartbeat" LED flash with the cadence of the timer overflow; this is a classic diagnostic feature to
visually confirm that the device is alive and well (especially when the display is off).
INT1 for Call Count Reset
The Call Count Reset button allows the user to clear the call counts. The button is connected to the INT1 interrupt pin of the MCU, which Monica
decided to hook up to the low-priority ISR. The Main Loop responds to the event by setting the call count variables to zero.
Full Device Operation
With the above introduction, we are now able to present the flowchart of the complete ring interpreter algorithm. It is implemented in the Main
Loop, with the events generated by the interrupts as described above.

Driving the Display
Vacuum fluorescent displays have a couple of peculiarities that have to be kept in mind; in addition, our device also posed its own custom
challenges. All of the related fun times are being savored below.
SPI Setup
In SPI Mode, the protocol we chose to communicate with the Noritake VFD, the display acts as a slave device, i.e., it takes the SCLK signal as an input from the master. Since it never sends any data back, the standard SPI
MISO signal is completely absent. For flow control, the display asserts the SBUSY signal when its internal
Rx-buffer is full.
Per the datasheet, the VFD expects Mode 11 data clocking from the master, so we configured the PIC's SPI module accordingly. The display's
maximum supported clock frequency is 2 MHz, but for stability we decided to go down to 125 kHz with the 1:64 prescaler
setting.
SPI Communication
This part seemed straightforward, yet it threw a few curve balls at Monica. The MPLAB C18 library offers the
putsSPI() function to send a zero-terminated string (sans the zero) out on the SPI bus. However, we could not use it as-is, and had to spin our
own putsVFD() implementation instead, for the following reasons:
- The VFD's internal buffer is mere 12 bytes, which is less than some of the messages the firmware needs to display; because of that, we
needed to add intra-string flow control to our API.
- The PIC's SPI module uses the common MSb-first convention to put the bits on the bus, but what do we know, the VFD is expecting them
LSb-first. Since neither of the two sides are configurable, the bit-reversal had to be done in firmware.
- In some of the VFD command sequences, the value 0 can occur as a parameter, which is a problem when dealing with C-style zero-terminated
strings. For this reason, we had to represent the zeros in the display strings with another value that is never used otherwise (0xff turned
out to be suitable in our case), with putsVFD() doing the "un-escape" before sending out the byte.
The Totally Unexpected
While the above issues were easy to recognize and fix, there was a real head-scratcher that left even the Noritake customer support dumbfounded.
For some reasons, right after reset the VFD believes that its buffer has two bits already clocked in. What this means is that if in this state
a string is sent to the module 8‑bit by 8‑bit, the display will be complete gibberish. It took us a while to root-cause the issue. Once we
did, we recognized that clocking in six dummy 0 bits will rectify the problem (until the next reset). We also found that this is not related to
the timing of the reset sequence, and that the number of "stray bits" is always two, without fail.
Save the Screen
In vacuum fluorescent displays, light is emitted by the phosphor-coating of the anode when bombarded by electrons. These electrons are coming from
the cathode, which is a network of heated filaments. Contrary to Nixie tubes, which are cold-cathode displays, the heated cathodes of the VFD
allow for lower voltages inside the device, but alas, they are basically under-powered light bulbs. In order to extend the display's life expectancy,
the application can do the following favors:
- Only keep the display on when there is anything meaningful to show - when both call counts are zero, the PCMF firmware invokes the VFD's
Screen Saver command that turns off the display and powers down the filaments.
- Like cathode ray tubes, VFDs are also prone to the infamous burn-in effect, where static images can leave permanent fading patterns on
the display surface. To avoid this, we used a control command that alternates the screen between normal and reverse with an exact 50% duty cycle
at all times; when potential messages have been detected, the flashing is faster, so that the device draws immediate attention.
Throughout the years, the display has displayed impeccable reliability, with zero printout mistakes and no apparent loss of brightness. Monica and
I were both very pleased with this peripheral, and cherished our wise choice for her career-launching project.
|