Introduction

Our homebrew repeaters consist of two commercial radios, a duplexer, a power supply and a homebrew controller. The controller itself consists of two PCBs which plug into a backplane. One PCB is the logic board. It decides when to key up the transmitter, when to issue automatic repeater IDs and many more things that are within the realm of the controller logic. The second PCB is the audio board. Among other things, it is responsible for filtering the audio from the receiver before it is sent back to the transmitter, summing up this audio with the CW tone, controlling an mp3 module for the automatic voice ID and synthesizing the PL/CTCSS tone which is also added to the audio sent to the transmitter.

In order to perform these tasks, an auxiliary PIC16F722A [datasheet] microcontroller is used. This chip is strapped to the I2C bus, available at the backplane, to receive commands from the logic PCB. In particular, the following features are implemented:

1. Synthesizing a PL tone by means of a DDS.
2. Setting output pins HIGH momentarily. The pins will fall back to LOW after a fixed hold time.
3. Setting output pins to a HIGH or LOW state.

The first feature is arguably the most interesting aspect of this solution. The second feature is used to control the ELV MSM 2 mp3 module and the third feature is used to mute audio paths by grounding them through a MOSFET. It is possible to use the software published on this site for the DDS only, ignoring or disabling the remaining features.

We are powering the microcontroller from a 5 V rail, although operation on a 3.3 V level is equally possible. Naturally this will result in a lower DDS output level. The 7-bit I2C address is set to `0x39`, but can be easily changed in the code. While the internal 16 MHz oscillator can be used, we added an external 16 MHz crystal oscillator to mitigate drift originating from temperature changes which are quite significant at some repeater sites. If you want to use the internal oscillator instead, you have to change `#pragma config FOSC` in the `config_bits.h` file.

DDS Implementation

The working principle of a DDS is explained in quite some detail in this video. Instead of an R2R ladder, a PWM output is used in the solution described here. The following figure illustrates this concept.

The top plot shows the sinewave that is to be synthesized. The little red dots are the sampling points; they correspond to the period of the square wave in the second plot. The duty cycle of the square wave is altered such that the average over a period equals the sampled value, i.e. the red dot in the top plot. The averaging is performed by a reconstruction filter which is discussed below.

DDS Specifications

The following table summarizes the characteristics of our DDS:

 DDS clock 15625 Hz PWM resolution 8 bits LUT size 256 bytes LUT width 8 bits Frequency resolution approx. 238.4 mHz Worst absolute CTCSS frequency error 0.118 Hz (at 114.8 Hz) Worst relative CTCSS frequency error 0.142 % (at 71.9 Hz)

Last but not least, modifying the source code to support additional frequencies is trivial: The phase increment can be computed by dividing the desired output frequency by the DDS frequency resolution. The frequency resolution in Hz is ; the phase increment is therefore ${\phi }_{\text{increment}}=\frac{{f}_{\text{desired}}}{{f}_{\Delta }}$.

Reconstruction Filter

A passive RC filter is used in our design to integrate the square wave into a sine wave. Some more considerations with respect to the filtering of DDS and PWM signals can be found in this video.

Our filter is shown below, although it must be said that there might be better approaches to this than what is depicted. Then again, this solution fully satisfies our needs.

As this is not a split-supply design, `V_half` is the half-rail voltage generated by another OpAmp. `R220` can be used to further increase the gain of the inverting amplifier that follows the filter. We designed this in just in case, but populated the footprint with a 0 Ohm jumper. Using an inverting amplifier here allows for the option of negative gain. The purpose of `R230` is just to improve the cross-over distortion behaviour of the good old `LM358` and can be omitted if a better OpAmp is used.

In addition to averaging out the pulses, it is this filter's job to reject unwanted nyquist images. Those images will appear close to the sampling frequency of 15625 Hz, hence the requirements on the filter in this regard are fairly low. If necessary, the passive filter could be optimized such that the later stages impose less loading on the early stages. Probably the number of stages can be reduced anyway. An active filter could be used instead (e.g. a Sallen-Key filter), but it is generally a bad idea to introduce gain there. A second OpAmp would be needed as a consequence.

I2C Register Map

The following table shows all the commands that are understood by the `aux_pic`. The microcontroller acts as an I2C slave and expects one command byte after the address. Hence, the I2C transaction executed by the I2C master is:

1. Send START condition.
2. Send 7-bit address as most significant bits. The least significant bit is 0 to indictate a write operation.
3. Send command byte.
4. Send STOP condition.
Command ByteDescription
`0b0xxxxxxx`Set PL tone. See the table below as well as `pl_tone_t_enum.h` for a list of supported PL tones and their corresponding command byte. The PWM waveform is available on pin `RC2`.
`0b10000001`Momentarily set pin `RA0` HIGH.
`0b10000010`Momentarily set pin `RA1` HIGH.
`0b10000011`Momentarily set pin `RA2` HIGH.
`0b10000100`Momentarily set pin `RA3` HIGH.
`0b10000101`Momentarily set pin `RA4` HIGH.
`0b10000110`Momentarily set pin `RB1` HIGH.
`0b10000111`Momentarily set pin `RB2` HIGH.
`0b10001000`Momentarily set pin `RB3` HIGH.
`0b10001001`Momentarily set pin `RB4` HIGH.
`0b10001010`Momentarily set pin `RB5` HIGH.
`0b11000000`Set pin `RC6` LOW.
`0b11000001`Set pin `RC6` HIGH.
`0b11000010`Set pin `RC5` LOW.
`0b11000011`Set pin `RC5` HIGH.

Output Frequencies, Errors and I2C Commands

The following table shows the pre-programmed output frequencies along with their I2C command bytes, the macros defined in `pl_tone_t_enum.h` as well as the actual output frequencies along with errors. These are calculated figures of the DDS alone. Errors from the reference clock are not accounted for.

Macro Command Byte Desired Frequency Actual Frequency Absolute Error Relative Error
PL_NONE0b00000000offoff--
PL_06700b0000000167.0 Hz66.996 Hz0.004 Hz0.007%
PL_07190b0000001071.9 Hz72.002 Hz-0.102 Hz-0.142%
PL_07440b0000001174.4 Hz74.387 Hz0.013 Hz0.018%
PL_07700b0000010077.0 Hz77.009 Hz-0.009 Hz-0.012%
PL_07970b0000010179.7 Hz79.632 Hz0.068 Hz0.086%
PL_08250b0000011082.5 Hz82.493 Hz0.007 Hz0.009%
PL_08540b0000011185.4 Hz85.354 Hz0.046 Hz0.054%
PL_08850b0000100088.5 Hz88.453 Hz0.047 Hz0.053%
PL_09150b0000100191.5 Hz91.553 Hz-0.053 Hz-0.058%
PL_09480b0000101094.8 Hz94.891 Hz-0.091 Hz-0.096%
PL_09740b0000101197.4 Hz97.513 Hz-0.113 Hz-0.116%
PL_10000b00001100100.0 Hz99.897 Hz0.103 Hz0.103%
PL_10350b00001101103.5 Hz103.474 Hz0.026 Hz0.025%
PL_10720b00001110107.2 Hz107.288 Hz-0.088 Hz-0.082%
PL_11090b00001111110.9 Hz110.865 Hz0.035 Hz0.032%
PL_11480b00010000114.8 Hz114.918 Hz-0.118 Hz-0.103%
PL_11880b00010001118.8 Hz118.732 Hz0.068 Hz0.057%
PL_12300b00010010123.0 Hz123.024 Hz-0.024 Hz-0.020%
PL_12730b00010011127.3 Hz127.316 Hz-0.016 Hz-0.012%
PL_13180b00010100131.8 Hz131.845 Hz-0.045 Hz-0.035%
PL_13650b00010101136.5 Hz136.614 Hz-0.114 Hz-0.083%
PL_14130b00010110141.3 Hz141.382 Hz-0.082 Hz-0.058%
PL_14620b00010111146.2 Hz146.151 Hz0.049 Hz0.034%
PL_15140b00011000151.4 Hz151.396 Hz0.004 Hz0.003%
PL_15670b00011001156.7 Hz156.641 Hz0.059 Hz0.038%
PL_16220b00011010162.2 Hz162.125 Hz0.075 Hz0.046%
PL_16790b00011011167.9 Hz167.847 Hz0.053 Hz0.032%
PL_17380b00011100173.8 Hz173.807 Hz-0.007 Hz-0.004%
PL_17990b00011101179.9 Hz180.006 Hz-0.106 Hz-0.059%
PL_18620b00011110186.2 Hz186.205 Hz-0.005 Hz-0.003%
PL_19280b00011111192.8 Hz192.881 Hz-0.081 Hz-0.042%
PL_20350b00100000203.5 Hz203.609 Hz-0.109 Hz-0.054%
PL_20650b00100001206.5 Hz206.470 Hz0.030 Hz0.014%
PL_21070b00100010210.7 Hz210.762 Hz-0.062 Hz-0.029%
PL_21810b00100011218.1 Hz218.153 Hz-0.053 Hz-0.024%
PL_22570b00100100225.7 Hz225.782 Hz-0.082 Hz-0.037%
PL_23360b00100101233.6 Hz233.650 Hz-0.050 Hz-0.021%
PL_24180b00100110241.8 Hz241.756 Hz0.044 Hz0.018%
PL_25030b00100111250.3 Hz250.340 Hz-0.040 Hz-0.016%