RX/TX-Sequencer with 28V relay driver

This is built with an ATtiny214, doing sequencing in code running on the CPU and step-up voltage regulation with the core independent peripherals, like demonstrated in a previous post here.
The power drawn by the relay is monitored and the TX and PA outputs does not become activated if the relay isn’t functioning.
Schematics and layout in KiCAD format and source code is available here: https://github.com/danupp/boost_sequencer
An article describing the project was published in Dubus 3/2021. Unfortunately the wrong version of the block diagrams was printed – the correct ones below better describes the functionality:

The sequence of the signals is as follows:

Here is a movie showing the first prototype (an earlier version than the one described) with very slow sequencing:

Boost regulator with ATtiny

When Microchip bought Atmel I was afraid that they would discontinue further development of the AVR MCUs and instead only promote their PIC-devices. But it seems this worry was unjustified. New series of ATtinies and ATmegas have been released and these new devices are packed with very useful periferals. At the same time the price of the newer devices are substantially lower than the older ones.

Of particular interest to me is the Event System and the Configurable Custom Logic (CCL) block that makes it possible to route signals between peripherals and pins without having to go through any CPU processing.
This opens up for applications like software configurable switch-mode regulators for voltage translation (step-up/boost, step-down/buck, flyback etc.).

To evaluate this, a boost regulator to deliver +12V from a +5V supply was designed, built and tested.

The schematic is simple:

What is realised in the AVR can be seen from this block diagram:

The blocks drawn are all configured, but then afterwards fully independent, from the CPU. Two timers are utilized. The first, to the upper left, is configured to run continuously and overflows after 30 µs, i.e. at a fixed frequency at around 33 kHz, when set up as in the code below. The overflow signal is logically AND:ed with the state of the analog comparator. This AND gate is implemented by one of the two Look-Up-Tables in the CCL block. The comparator takes its inputs from a pin and the internal DAC. If the voltage detected at the output of the regulator is lower than the DAC set point, the comparator output is high. The signal from the AND gate then starts the second timer in one-shot mode. It is configured such that its comparison output, which is fed to a pin, is high from start to around 15 µs later. If the comparator would be continuously in its high state this all together creates a signal that repeatedly is high 15 µs and low 15 µs (total period time 30 µs).
This signal is intended to drive a transistor to first “charge” an inductor and then give the inductor time to release its energy at a higher voltage.
The voltage at the output is scaled down and compared with the one from the DAC. This ensures regulation to a fixed output voltage. Thanks to the DAC, the regulator output voltage can be tuned digitally without having to change any resistor values.

The test prototype was built in “dead bug style” above a proto board:

The waveform at the switch node (drain of the transistor) looks like this:

At first we see the 5V supply, then, for 15 µs the transistor pulls low and the current rises in the inductor (a current probe would be nice to have). With component values as in the schematic, the current reached will be 5V/33µH*15µs = 2.3 A.
When the driving waveform goes low, the transistor turns off and the inductor releases its energy. This happens at a voltage (assuming steady-state) that is the difference between the output voltage (plus diode drop, some 0.5V) and the supply voltage. The time it takes for this is 15 µs * 5V/(12.5V-5V) = 10 µs. With 30µs period time we thus have some margin left. It is important that there is enough time for the inductor to discharge, otherwise the current will raise higher and higher at situation of high loading and eventually something will be damaged.
When the energy is released some ringing is observed around the 5V level due to stray capacitance in the circuit (e.g. drain-source capacitance) which resonates with the inductance. This ringing is damped by the RC-snubber circuit across the diode. Without the snubber it looks like this:

The maximum current that can be delivered at 12V can be calculated from the fact that the mean current at 5V is 1.15 A during 15µs and zero at the remaining 15 µs, which gives 0.575A on average if the regulator is working continuously. This is 5V*0.575A = 2.9W. Assuming 85% efficiency leaves around 2.5W at 12V, or a little more than 200 mA. If we want more than this we could e.g. increase the switch current by decreasing the inductance value.

A feature that is nice to have in all switch regulators is overload protection. If we, with this design, can know that the analog comparator hasn’t seen a level above its threshold for a certain time, this can be interpreted as overload and the CPU core can turn off switching. This is implemented by configuring The Periodic Interrupt Timer (PIT) to trig a routine that checks whether the comparator in its turn has trigged an interrupt since the last PIT interrupt. The details are in the code.

This all together demonstrates how money and PCB space can be saved by using blocks in a modern MCU for voltage conversion instead of using separate chips.

The full source code is posted here below. After compilation with avr-gcc it can be programmed to the chip through the one-pin UPDI-interface with a standard UART-cable and pyUPDI.

// Targets an ATtiny214

#define F_CPU 3333333
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define OVERLOAD_LED PIN4_bm // PA4

volatile uint8_t overload_flag;

ISR(RTC_PIT_vect) { // 16 Hz

  RTC.PITINTFLAGS = 0x01; // clear flag
  if (overload_flag) {
    TCA0.SINGLE.CTRLA = 0b00000000; // DISABLE switching.
  else {
    overload_flag = 0x01;
    AC0.INTCTRL = 0x01; // enable interrupt

ISR(AC0_AC_vect) {
  AC0.STATUS = 0x01; // clear flag
  AC0.INTCTRL = 0x00; // disable interrupt
  overload_flag = 0x00;

void main () {
  PORTA.DIR = OVERLOAD_LED | PIN2_bm | PIN5_bm; // outputs
  PORTA.PIN7CTRL = 0x04; // digital input disable for AINP0/PA7
  PORTA.OUT = 0x00;

  PORTMUX.CTRLA = 0x01; // Enable EVOUT0

  //TCA0.SINGLE.PER = 65; // 19.5us period, for 12V->26V and 120uH
  TCA0.SINGLE.PER = 100; // 30us period, for 5V->12V and 120uH
  TCA0.SINGLE.CTRLA = 0b00000001; // DIV1 -> 0.3us/tick, ENABLE
  TCB0.CTRLB = 0x16; // Single shot, output enable
  TCB0.EVCTRL = 0x01; // Enable input capture, start counter at pos edge
  //TCB0.CCMP = 27; // 8us for 12V in and 0.8A current in 120uH
  TCB0.CCMP = 50; // 15us for 5V in and 2.3A current in 33uH
  TCB0.CTRLA = 0x01; // ENABLE

  AC0.MUXCTRLA = 0b10000011; // Inverted output, DAC to neg in
  VREF.CTRLA = 0x03; // 4.3V
  //DAC0.DATA = 140; // 2.36V *(100+10)/10 = 26V
  DAC0.DATA = 65; // 2.36V *(100+10)/10 = 12V
  DAC0.CTRLA = 0x01; // ENABLE

  AC0.CTRLA = 0b00100001; // Interrupt at neg edge, ENABLE
  EVSYS.SYNCCH0 = 0x02; // TCA0_OVF
  EVSYS.ASYNCCH0 = 0x01; // CCL_LUT0
  EVSYS.ASYNCUSER8 = 0x01; // SYNCCH0 -> EVOUT0/PA2 to see max switch freq 
  CCL.LUT0CTRLB = 0x63; // AC0 + EVENT0
  CCL.TRUTH0 = 0b10001000; // AND
  CCL.CTRLA = 0x01; // ENABLE

  _delay_ms(200); // wait 200ms after start before enabling overload detection
  overload_flag = 0x00;
  RTC.CLKSEL = 0x00; // Use 32 kHz from OSC32K
  RTC.PITINTCTRL = 0x01; // enable interrupt
  RTC.PITCTRLA = 0b01011001; // Enable, interrupt after 2048 cycles => 16Hz

  while(1) {

There is a thread at AVRFreaks for discussion.

When to use dB and when to use dBm

Occasionally one can see texts and reports about RF circuits where people mix up the terms dB and dBm.
The typical erroneous statement is something like “The power level is 7 dBm lower”. In most, probably all, cases it should instead say “The power level is 7 dB lower.”

Let’s explain by example:

Say we have a power level of 20 dBm. This means 20 dB above 1 mW, i.e. 100 mW.
And 7 dB is a factor of 5, so 7 dBm is 5 mW.
Now 20 dBm – 7 dB = 13 dBm = 20 mW. Subtracting a value in dB means a division. I.e. 100 mW / 5 = 20 mW.
But subtracting a value in dBm means taking the difference between two absolute levels: 20 dBm – 7 dBm = 100 mW – 5 mW = 195 mW = 19.8 dBm.
Thus it very rarely makes sense to say “xx dBm lower / higher” or “the power was reduced / increased by xx dBm”. Use dB in these cases instead!

PLL / Frequency synthesis with Programmeable Logic

A common FPGA chip, like the Cyclone-series from Intel (formerly Altera), include PLL-blocks that can be used to generate a high frequency clock, referenced to a crystal oscillator at lower frequency. The high frequency oscillator of the PLL block is probably a ring oscillator, consisting of a lot of inverters in cascade, connected in to out, in order to get an oscillation at several hundred MHz that is the subject to phase-lock and subsequent frequency division. (See the data sheet for the chip TLC2933 which is likely very similar to the PLL implemented in the Intel FPGAs. And here is a nice ring-oscillator with discrete gates. )
For most applications this is great, but in some cases the frequency resolution is not good enough. And, which is of more importance, for some applications the jitter / phase noise of the internal PLL is not as good as what one would want. Those applications could be RF / analog / mixed signal related, like when clocking a high speed ADC.

However, it is possible to use an external voltage controlled oscillator (VCO) and some other analog parts while implementing the digital blocks of a PLL as logic in the FPGA:

The frequency dividers needed for main and reference path are readily synthesized as counters that are reset at some arbitrary division number. And a phase-frequency detector (PFD) with two flip-flops and an AND-gate for reset is also simple to implement:

library ieee;
use ieee.std_logic_1164.ALL;
use ieee.std_logic_unsigned.ALL;
use ieee.numeric_std.ALL;

entity pfd is
	port (fvco : in std_logic;
			fref : in std_logic;
			up : inout std_logic;
			down : inout std_logic;
			reset_out : out std_logic;
			reset_in : in std_logic
end pfd;

architecture pfd_arch of pfd is
signal q_up, q_down : std_logic;


	reset_out <= q_up and q_down;
	up <= '0' when q_up = '1' else 'Z';  -- neg polarity
	down <= '1' when q_down = '1' else 'Z';  -- neg polarity
	--up <= '1' when q_up = '1' else 'Z';  -- pos polarity
	--down <= '0' when q_down = '1' else 'Z';  -- pos polarity
	ff1 : process(fvco,reset_in)   
		if reset_in = '1' then
			q_down <= '0';
		elsif fvco'event and fvco = '1' then
			q_down <= '1';
		end if;	
	end process;

	ff2 : process(fref,reset_in)   
		if reset_in = '1' then
			q_up <= '0';
		elsif fref'event and fref = '1' then
			q_up <= '1';
		end if;	
	end process;
end pfd_arch;

The purpose of the delay in the reset path is to eliminate the “dead-zone” problem. This arises in the lock-state where the very small phase error will result in pulses of so short duration that they don’t even get out of the chip. This then mutes the PFD at small phase errors, i.e. it creates a dead zone. With a delay in the reset path there is a minimum time where both the up and the down impulse is present so that any difference of the duration of these also can propagate through. An asynchronous delay is not easily synthesized in an FPGA but the VHDL code above solves this problem by providing a reset path out from and then into the module. These signals are routed to physical pins of the device and hence guarantee a small but still substantial delay.

Note in the code above that the up and down outputs are configured as “inout” and switched between input/tri-state (‘Z’) and high/low level. This effectively forms a balanced charge pump when they see an active loop filter:

At lock state, the voltage at the negative terminal of the opamp is fixed to half the 3.3V rail voltage. The two resistors at the FPGA pins shall be of equal size, say 1kOhm. A high or low level driven on any of these pins then pump a charge of 1.65V/1kOhm = 1.65 mA into the opamp integrator.

This picture below, from a spectrum analyzer, shows two superimposed traces of a signal at 65 MHz. The one with the higher noise floor is the FPGA internal PLL-block. Note that it has quite a lot of noise at high offsets from the carrier. The other trace is from the design described in this post/article. Here the wide offset noise lies below the spectrum analyzer noise floor. The two spikes are reference spurrs at 500 kHz offset, that should be possible to get rid of with a little more effort put into the loop filter, on a carefully designed PCB layout (see the ugly proto below).

The close in noise is another matter, which is hard to analyze with just a spectrum analyzer, and is highly dependant on the Q in the VCO-resonator (and hence the tunable frequency range), as well as the used active device, cleanliness of the voltage supply etc.

Here is a semi-complete schematic and a pic of the prototype, using a cheap Cyclone IV experiment board. (Phase noise of the internal PLL has also been measured to similar levels on Cyclone II and Cyclone 10LP.)
Note that the VCO frequency is driven into the FPGA through a 74AUC1GU04 inverter buffer. This is critical since the FPGA needs a proper square wave for its edge triggered logic.

IC-7000 short term frequency stabilization

The IC-7000 is a nice HF+6m+2m+70cm-transceiver. But it has one stupid design flaw. The crystal oscillator which supplies the reference frequency to all synthesizers does not have any thermal insulation from its surroundings. Since it is a TCXO one can guess that Icom thought that its intrinsic temperature compensation is good enough. But they didn’t think about operating modes like FT8 where a drift of tens of Hz in a few seconds is a serious problem.
The main problem is that the fan is very close to the crystal oscillator and every time a transmission is started the fan starts blowing air into the transceiver. (At least when operating at 144 MHz, not always at HF it seems.) This creates a jump in temperature at the beginning of a transmission cycle, as well as in the beginning of the following reception period when the fan stops. For a tested transceiver, this temperature jump caused a short term frequency drift of some 25 Hz at 144 MHz over a few seconds, after which the temperature compensation in the crystal oscillator had pulled the frequency back again. This made it difficult to work FT8-QSO:s.
Fortunately it is easy to insulate the oscillator from the fan air by placing a piece of foam onto it. The crystal oscillator is mounted on the backside of the DDS board. This is accessible by removing the top lid and unscrewing the DDS-board screws. (One screw is shared with the fan, beneath the fan gasket.)
With a small piece of “double sided tape”, foam with adhesive, onto the crystal oscillator, the drift virtually disappeared.

Tuja SDR transceiver

A site is now launched for the Tuja SDR transceiver.

Tuja is an FPGA based software defined radio (SDR) transceiver characterised by a high dynamic range in receive mode and clean spectrum emissions in transmit. It is very different from other low or medium priced SDRs that often suffer from poor dynamic range and spurious emissions. Tuja has been optimized for narrow band communications and works great for weak signal modes like SSB, CW and low speed digital modes such as WSPR, FT-8 and PSK31. The high dynamic range also makes Tuja an excellent choice for VHF/UHF/SHF operation (in conjunction with a transverter). High speed digital modes such as 4G and WiFi are not supported.

Tuja integrates with the popular Raspberry Pi platform (like a “hat” but Eurocard format) for control and signal processing. The use of a Raspberry Pi enables flexible programming, easy spectrum presentation and remote operation. The Tuja board has an audio codec with microphone input and speaker/headphone output for stand-alone operation. It is possible to connect an LCD-display and a physical buttons/knobs for operation without any computer monitor or network connection.

Tuja is designed with modern components and well-proven radio architecture in mind. In receive mode it uses the superheterodyne architecture with ultra linear analog mixers and a steep intermediate frequency filtering. A bandwidth of 100 kHz is digitized to 24 bits where further filtering in an FPGA reduces it to 50-60 kHz usable bandwidth. Baseband data is sent to the Raspberry Pi as 24 bit I/Q samples over the I2S bus.

In transmit, the Raspberry Pi generates I/Q baseband data that is sent to the Tuja board for modulation of the carrier by analog mixers. Carrier frequency in transmit, as well as local oscillator signals in receive, are generated by the FPGA connected to a high-speed DAC, running on a very clean clock source.

Please visit the newly launched site for the Tuja SDR transceiver.