Welcome to Nixiana.com!
The Nixie Tube Propeller ClockNTPC has spun times since midnight!
PCMF Firmware

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.

© Peter Csaszar - All rights reserved