-
Fractional vs. Integer mode
09/24/2017 at 09:33 • 0 commentsThe obvious difference between fractional and integer mode synthesizer, is that int-mode synthesizers output frequency that is integer multiple of reference frequency, or in this case, f_PDF. Frac mode synthesizers, on the other hand can provide fraction of the step of that frequency, making it possible to generate virtually any frequency at the output.
But, improved frequency resolution is not the only advantage of frac-mode synthesizers, there is something called phase noise decrease. Phase noise increases as 20log(N), meaning that the higher output frequency will look more messy in the spectrum, to say it technically correct. If, for example reference frequency is 100 kHz, to synthesize 3 GHz at the output, we'd need N of 30000. Output divided by 4 (to 750 MHz) would look like this:
Now, if we employ frac-mode synthesizer, we wouldn't need that low reference frequency nor that large N. Instead, we could keep reference frequency at 19.2 MHz and tune the fraction. For example, in our 3 GHz example we'd need N equals 156 and additional fraction of 0.25. This fraction is defined as F/M. Given that denominator M is fixed at 100, F would be 25 and the output (again scaled to 750 MHz) would look like this:
This is improvement of almost 200 times lower N. Spectrum look much smoother and more pleasant to look at.
-
Tweaking software
09/18/2017 at 10:35 • 0 commentsAfter verifying that MAX2871 works properly in almost every aspect I decided to write the code that will enable versatile configuration of the chip (I find it very tiring to flash the chip every time I want to update one parameter). I wrote firmware that expects user input from serial terminal to execute various functions. These are primarly PLL configurations that synthesize different output frequencies, but there are also functions to read out registers, select filters, etc.
I also wrote a Python script that provides command line interface for easier communication with the Transmitter.
Source code as well as wiki of the code is available on my Github repo.
-
Testing the synthesizer II: frequency sweep in INT mode
09/05/2017 at 22:27 • 0 commentsThis time, I was testing how frequency varies with user input. I wrote a program that allows user to set and change values of N and DIVA from the serial terminal. I wanted to see how can I control frequency the easiest way. But before, let's think about how is frequency synthesized in the first place.MAX2871 contains 64 VCOs organized in 4 core blocks with 16 sub bands each. I have no idea how advanced/complex in IC production is, but it surerly sounds impressive. These VCOs are able to cover any frequency between 3 and 6 GHz. The whole span is covered in steps of f_PFD, meaning that f_PFD presents max resolution of the PLL (when it is working in integer mode).
In integer mode, VCO frequency is determined as But, there is certain limit. N can not be arbitrarily chosen and the datasheet boundaries 16 - 65535 is not the only one there. Second boundary is limit of the VCOs: you can not push N f_PFD outside 3 - 6 GHz range. VCO will simply saturate and keep at limits. Another boundary comes from the loop flter. If difference f_VCO/N - f_PFD is higher than cut off frequency of the loop filter, control signal won't be applied and VCO will oscillate with lower limit value.
To demonstrate it, I've put f_PFD to 19.2 MHz (f_REF = 19.2MHz, R = 1, RDIV2 = 0, DBR = 0). I've selected output divider value DIVA to be 32 and 16 ( so I can measure it easier with my SA). Output divider determines final frequency that will be emitted out of the chip:This DIVA factor is actually the one that is responsble for such broad range: if DIVA is 128, we'll have output frequency as low as 23.5 MHz. DIVA can have 8 values, meaning that MAX2871 will synthesize frequency in 8 sub-bands.
As you can see on my schematic, loop filter is simple lowpass RC network with cutoff at 776 kHz. From above disussion, we see that it is necessary that
| f_VCO/N - f_PFD | < f_cutoff.
from there, it follows that limits for N are given by:N_min = f_VCO,min / (f_PFD - f_cutoff) and N_max = f_VCO,max / (f_PFD - f_cutoff).
For given, settings, expected limits are 162 and 325. When N is within that limit, we can expect linear dependency of the output frequency. Outside this limits, VCO will saturate, or, stay at minimum frequency. This is exactly what you can see on the pics of measured data below.
I just realized that I could sum up all the text above simply by saying "f_PFD" is the minimum step size within the given range of VCO frequencies. This what you can read in most of PLL introduction texts. However, I would miss one important thing: not only does f_PFD defines the step size, it also defines, together with loop filter, which steps can be taken. Although N can be as high as 65535, it will have no effect because the VCO will saturate! -
Testing the synthesizer
09/04/2017 at 21:13 • 0 commentsNow that everything's finally working, I could go on and get some real job done here. For instance, to check out what's worth this little MAX2871 chip. What's the spectral content of the synthesized signal? Is it stable? How much does it vary with temperature? What's its quality factor? I used R&S FSIQ3 3.5GHz spectral analyzer to measure all these properties.
I use my small Arduino program to control the synthesizer ("Basic" example in my Github repo). I can enable/disable RF ports and control the RF output power. Once the transmitter is powered on, I use large frequency span (100 MHz) of the SA to locate the signal. I chose 2 MHz resolution bandwidth to start with. Here we can see nice 11.25 dBm peak at 1.35 GHz (DIVA = 1):
When I reduce the span to 50 MHz, or zoom-in, to be more technical, I observe some funny things (again technical) on the sides of the peak:
It looks like two small tumors growing on our signal. Further zooming in, we'll see the tumors are becoming real devil's horns:
With my frequency span lowered to 12.5MHz, I could see even more peaks unveiling in the spectrum:
Finally, with span of 6.25MHz, the crystal clear peak turns to be a total spectral jungle filled with side lobes, and multiple order harmonics of some basic frequency transposed to 1.35 gig:
I was expecting that Δf will be around 19.2MHz, what corresponds to the reference frequency of my crystal oscillator. It turned out to be false - the peaks are showing up at irregular distance, hundreds of kHz apart. I can only assume it's some PLL black magic of the MAX chip behind this, like intermodulation products in the chip. Maybe I should start sounding like a professional and address this phenomenon by its name: phase noise. Datasheet specifies it to be -136 dBc/Hz at 1 MHz offset. Since I have division factor N = 135 in the PLL feedback, I can expect additional 20log(135) = 42 dB to the phase noise. It still seams to be much less than what I'm measuring. Could it be chip itself or one more downside of my board (loop filter)?
During these measurements, the frequency drift was barely observable and mostly due to the heating of the chip. I deduct it, as the center frequency only changes when I cool the chip with the tip my finger.
Overall, the synthesizer chip performs well in the frame of what home made, 1st attempt, hobbyist board can hope for. Question that bothers me still is: why is the phase noise so big? why is the spectral content so weird and full of garbage? Why not clean and pointy spike, like I would expect for? Is it caused by the chip itself or by loop filter my board? Is it fabrication trade off for such a wide bandwidth? Questions, questions...I'll start by asking people who made the chip, at Maxim Integrated.
Note on the output power
MAX2871 is said to deliver max 5dBm of power at its outputs. I measure some 11 dBm at the end of my transmitter. Power amplifier TRF37A75 has gain of 12 dB, but programmable attenuator, filters and switches should be 0 dB. In real world that's not the case, so if we're generous, we can say each of them damps 0.5 dB. Together, expected attenuation is (filter, 2x switch, attenuator) some 2 dB. I would add 1 dB for bad soldering on each component (including PA). In total, 5 - 2 + 12 - 5 = 10 dBm, what kinda makes sense with my measurements.
I just want to add that RF power control of the chip worked fine - it really does reduce the chip power in steps of 3 dB.
And lastly, I did some measurements at lower frequencies (85 MHz). Total output power in that range was about 6.6 dBm. Hmm, that's weird, all of the components claim to be broadband compatible and I don't expect much loss at such low frequency. Again matter of chip?
Note on the main VCO frequency
I also wanted to see the max frequency the synthesizer can give (it claims to be 3GHz), so I set DIVA to 0 and powered it on. I measured clean 2.7GHz. Hmm.. where's my 300MHz gone? Bad loop filter? Lying chip?
Note on harmonics
Yes, there are strong harmonics visible on the scope. I just forgot to screenshot them. Luckily, I added filters that will eliminate them from the transmitter output, if necessary.
-
HW rework completed
09/01/2017 at 17:33 • 3 commentsI ve finally reworked power amp and tested my board directly over coax cable at the output. It was nice to see that little guy finally works HW bug free.
Here you can see my setup verified with R&S spectrum analyzer.
-
Testing with a Spectrum Analyzer
08/14/2017 at 14:36 • 1 commentIt's kinda shame to be RF engineer and not own a decent spectrum analyzer... Well, it's on my Christmas wishlist since I was 10, but noone bothered to buy me one so far. RTL-SDR was interesting tryout, but definitely not satisfying enough. Luckily, my local hackerspace has a nice little Agilent E4411B, 9kHz-1.5GHz spectrum analyzer, so I decided to give it a try.
I wanted to see how the signal looks like after every block of the cascade. That's why I used a standard oscilloscope probe instead of SMA cable. Of course, it's a very unprofessional approach in RF engineering, but I needed quick-and-dirty way to investigate my board. Hopefully, if I reduce the frequency low enough, the RF black magic will spare me some meaningful insight.
And here's the spectrum of my signal, directly at the MAX2871 output. It doesn't look bad at all! It's very narrow banded and stable over the time. It's only weakness is that it's..er..weak. It peaks to -25 dBm, while the datasheet specifies it should be minimum -5 dBm. What could make it this small? Bad soldering? Some capacitive leakage that I'm not aware of?
The following table shows the signal power after each element in the circuit:
MAX2871 -25 dBm 1st switch -29 dBm 2nd switch -32 dBm PE43711 (set to 0 dB att) -32 dBm Power amp -43 dBm Coupler and connector -43 dBm And we have a winner! The biggest damper is, ironically, power amplifier. I assume it's only one more bad soldering job, as many others on board.
So, the next task is, obviously, to rework the power amp. And for the future testing, stay out of RTL-SDR based spectrum analyzers.
-
Basic functionality example
08/11/2017 at 11:01 • 2 commentsVerifying that all components are working properly enables me to run the first functional test of the MW transmitter - frequency synthesis. In the repo, there is "Basic" example that configures MAX2871, attenuation, filter and ADC for RSSI detectors (even though the latter ones do not work, presumably).
The configuration is set as follows:
- reference frequency (XTAL input): 19.2 MHz
- phase detector: R = 1, DBR = 0, RDIV2 = 0, meaning fPFD = 19.2MHz
- integer mode with N = 120, resulting fVCO = 19.2*120 = 2304 MHz
- DIVA = 0b101, meaning that fRFOUT = fVCO/32 = 72 MHz.
- RF power 5dBm.
I intentionally set the DIVA so high so that I can measure the output frequency with my RTL-SDR dongle. With 5 dBm there should be no problem detecting some signal.
The "Basic" example makes user choose register content readout and turning on/off the RFOUT.
However, things didn't go as planed. Good things first: there was some output, and I could control it (turn it on/off) with my program. Bad things: The signal was very low, way below the expected 5dBm level. And it was also very wideband and filled with spurious content. Here's an example screenshot from RTL-SDR that runs "spectrum analyzer" program.
I cannot say that the basic functionality test has been passed, so the further investigations and debugging must take place. I'm just not sure where to start looking from. Is the MAX chip configured poorly (it shouldn't be, I used configuration from datasheet example) or do I get some terrible damping from my circuit?
-
Testing other components
08/09/2017 at 22:59 • 0 commentsAfter verifying the main chip, MAX2871, it would be nice to see if other components work as well.
First, I tested 4PST switch SKY13322-375LF. The switch is controlled with 4 parallel inputs that switch RF input to one of four outputs. I wrote a simple code that allows user to chose (and change) desired output and measured the connection between input and corresponding output. I was hoping to hear that lovely "beep" when I send certain character on the serial terminal. Guess what, it didn't work. There was short circuit to the GND from all outputs. Bad soldering job with my reflow oven. After the rework, everything worked fine.
Second thing was the attenuation chip PE43711. It is controlled over SPI and attenuation can be set in steps of 0.25 dB with more or less precision over the frequency range. The attenuation is calculated as an 8-bit word/4. Since the chip doesn't provide any data output, I only could test it with a multimeter. I measured input and output resistance as well as resistance from input to output with various settings. My observations are in the table below:
Attenuation, dB R in R out R 1-2 0 42.7k 42.7k 11.6 1 381 369 16.7 2 195 175 21 2.25 175 155 22 3 138 119 25 6 81 62 38 10 63 45 52 20 52 36 73 So, the resistance changes when different setting is chosen. I guess this suggests that SPI and chip work.
Power amplifier doesn't provide any meaningful testing options - I can only hope it works when it is powered on.
RSSI detectors are weird. I can measure detected signal using the ADC on Teensy. However, measured voltage is somewhere around 0.3 - 0.5V, even though there is signal at the input. Interestingly, the measured value drops to 0 when I put the voltmeter on the output pins. I have no explanation at the moment. My only guess is that troublesome soldering of BGA chips resulted in bad joints and ADC input measures any picked up voltage. Voltmeter probably than acts as a pull-down resistor and sets the measured value to 0.
Overall, since synthesizer, switches, filters, attenuation and power amp work, I can hope to see generated signal with a spectrum analyzer. When I get one.
-
Finally got SPI working with MAX2871
07/22/2017 at 21:14 • 3 commentsIt really took a long time to get the damn MAX chip working. Although I made it set MUX line high or low, I couldn't make it talk to me via SPI. On the scope, this line looked like nonsense:
In the process of investigating, I managed to burn 3 Teensy boards and 2 MAX chips. Don't ask how. I consulted tech support of Maxim Integrated as well. They were really friendly and prompt reacting (thanks Mohammed). But, before I got the answer from them, I decided to try one thing.
MUX bits are distributed in two registers. MUX[3] is in reg 5 while MUX[2:0] in reg 2. My initial code was writing reg2 first and reg5 later. For no clever reason, I decided to switch the order of writing. Guess what? Problem solved.
Aaaargh!
I'm using SPI read to get some data out of MAX2871. These are for chip temperature, VCO tuning voltage, and autoselected VCO number. During the development I found out that ADC that reads VCO voltage and temperature must be set and reset after each readout. Otherwise, it won't read properly. -
Testing SPI with MAX2871
06/11/2017 at 21:30 • 3 commentsSynthesizer MAX2871 can be configured using SPI on one of its six 32-bit registers. To test if everything works fine, I wrote a prompt Arduino program that runs the SPI bus and configures these registers in very simple way: setting pin MUX of the MAX2871 high or low. This is done by setting adequate bits of registers 0x5 and 0x2. this pin can also be configures as SPI data out (MISO) pin that reads out the contend of the only read register, 0x6. The read data is chip ID, ADC code and VCO status.
The program initializes the chip (programing all registers twice) and toggles MUX pin high and low every two seconds. After user input on serial port, the program displays read data from the above mentioned register. However, while the pin toggles fine, what led me to believe that SPI works as it should, read out makes troubles. It reads code that makes no sense. Still to be checked.
/* MAX2871 SPI Interface This example controls an MAX2871 synthesizer. MAX2871 serial interface contains six write-only and one read-only 32-bit registers. The circuit: * CLK is pin 13 * DATA OUT is pin 11 * LE is pin 5 * DATA IN is pin 12 * CE is pin 6 * RF EN is pin 7 Pero, June 2017 */ // include the SPI library: #include <SPI.h> #define slaveSelectPin 5 //LE or slave select SPISettings MAX2871_SPISettings(1000000, MSBFIRST, SPI_MODE0); char incomingChar = 0; //Serial input void setup() { delay(3000); Serial.begin(9600); Serial.println("MAX2871 SPI Interface"); Serial.println("v0.0"); Serial.println("Pero, June 2017"); Serial.println(" "); Serial.println(" "); // set the slaveSelectPin as an output: pinMode (slaveSelectPin, OUTPUT); pinMode (slaveSelectPin, HIGH); // initialize SPI: SPI.begin(); Serial.println("SPI Initialized"); MAX2871_Init (); Serial.println("MAX2871 Initialized") } void loop() { if(Serial.available() > 0) incomingChar = Serial.read(); switch (incomingChar){ case 'r': while(1){ MAX2871_Read(); delay(1000); } break; default: MAX2871_Set_MUX(1); delay(2000); MAX2871_Set_MUX(2); delay(2000); } } void MAX2871_Init (){ //Upon power-up, the registers should be programmed //twice with at least a 20ms pause between writes. The first //write ensures that the device is enabled, and the second //write starts the VCO selection process. Recommended to //turn-off the outputs during this sequence and then turn-on //the outputs using RFA_EN, RFB_EN. //Register programming order should be address 0x05, //0x04, 0x03, 0x02, 0x01, and 0x00. // Writing 2 times for (int j = 0; j < 2; j++) for (int i = 5; i >= 0; i--) // 6 write registers MAX2871_SPI_tx (uint32_t(i)); //Everything to 0. } void MAX2871_Read(){ /* Register 0x06 can be read back through the MUX pin. The user must set MUX (register 5, bit 18 and register 2, bits 28:26) = 1100. To begin the read sequence, set LE to logic-low, send 32 periods of CLK, and set LE to logic-high. While the CLK is running, the DATA pin can be held at logic-high or logic-low for 29 clocks, but the last 3 bits must be 110 to indicate register 6, then set LE back to logic-high after the 32nd clock. Finally, send 1 period of the clock. The MSB of register 0x06 appears after the rising edge of the next clock and continues to shift out for the next 29 clock cycles (Figure 2). After the LSB of register 0x06 has been read, the user can reset MUX register = 0000. */ uint32_t spi_data = 0; uint8_t spi_packet[5] = {0}; signed char i; Serial.println("Reading data from MAX2871."); MAX2871_Set_MUX(3); // Set read function on MUX pin MAX2871_SPI_tx(spi_data | 6); // "... last 3 bits must be 110 to indicate register 6." SPI.beginTransaction(MAX2871_SPISettings); // start new clock sequence. No need to change SS to low. for ( i = 0; i < 5; i++) { spi_packet[i] = SPI.transfer(0x00); // read from SPI shift register 4 times } SPI.endTransaction(); Serial.println("Data read:"); for ( i = 0; i < 5; i++){ Serial.println(spi_packet[i], HEX); spi_data |= spi_packet[i]; spi_data = spi_data << 8; } Serial.println(spi_data, HEX); Serial.println("Reading done!"); //MAX2871_Set_MUX(0); // Reset MUX pin } void MAX2871_Set_MUX(byte MUXSet){ /* Bits MUX[3:0] set the MUX pin. * 0001: VDD * 0010: GND * 1100: SPI read operation * * MUX[3] is bit 18 in reg5, and MUX[2:0] are bits 28:26 in reg2. */ uint32_t spi_data = 0; // 32-bits for SPI consist of 29 data bits and 3 address bits. switch( MUXSet ){ case 0: //Set MUX to High Z Serial.println("setting MUX to High Z ..."); // Set all to zero MAX2871_SPI_tx( spi_data | 0x02); MAX2871_SPI_tx( spi_data | 0x05); break; case 1: // Set MUX to VDD Serial.println("setting MUX to VDD ..."); spi_data |= (1 << 26); MAX2871_SPI_tx(spi_data | 0x02); break; case 2: // Set MUX to GND Serial.println("setting MUX to GND ..."); spi_data |= (1<<27); MAX2871_SPI_tx(spi_data | 0x02); break; default: //Set MUX to "read" Serial.println("setting MUX to SPI read ..."); spi_data |= (1<<28); MAX2871_SPI_tx(spi_data | 0x02); delay(20); spi_data = 0; spi_data |= (1<<18); MAX2871_SPI_tx(spi_data | 0x05); } } void MAX2871_SPI_tx(uint32_t spi_data){ /* This function takes 32-bit data and parses it on four 8-bit packets that Teensy transmitts. The 29 most-significant bits (MSBs) are data, and the three least-significant bits (LSBs) are the register address. */ signed char i = 0; uint8_t spi_package[4] = {0}; // store 32-bit integer into 4 bytes for ( i = 0; i < 4; i++) { spi_package[i] = (spi_data & (0xFF << 8*i)) >> ( 8*i ) ; //Serial.println(spi_package[i], HEX); } Serial.println("Sending data: "); Serial.println(spi_data, HEX); digitalWrite(slaveSelectPin,LOW); SPI.beginTransaction(MAX2871_SPISettings); for ( i = 3; i >= 0; i--) SPI.transfer(spi_package[i]); digitalWrite(slaveSelectPin,HIGH); SPI.endTransaction(); Serial.println("SPI transmission done!"); delay(20); }