High frequency square wave output

Status
Not open for further replies.

DD4WH

Well-known member
I was wondering whether it would be possible to produce a high frequency square wave output (say in the range of 10kHz to 10MHz) with the Teensy 3.6. which is adjustable in frequency.

GOAL:
* a minimum-hardware software defined radio [SDR] with:
* Teensy as local oscillator (square wave: maybe from 10kHz to 10 MHz (or higher, if feasible ;-) )
* one IC mixer (direct conversion) mixing the RF signal with the Teensy produced LO signal down to audio baseband
* Teensy built-in ADC sampling the audio baseband signal at a sample rate of 8 or 16kHz
* Teensy DSP: producing IQ signals from the ADC mono input
* DSP demodulation running in the Teensy
* Teensy DAC output

The only external hardware would be one mixer IC (and some Rs and Cs for antialiasing and bypassing) and a buffer amp after the DAC for headphones.

The internal ADC and DAC would be set at a speed of 16ksps or 8ksps.

Now, my question would be:

Under these circumstances, would there be a possibility for the Teensy to produce a square wave with a duty cycle of 50%
adjustable in frequency from about 10Khz to 10MHz? That would eliminate the need for an external local oscillator for the mixer input. I would not need a sine, a square would be sufficient.

Maybe with the PWM functionality or the FrequencyTimer2 library?

Would be nice if somebody could point me in the right direction or provide a link (or tell me that this is a naive question and impossible to do with the Teensy :))

All the best,

Frank DD4WH
 
Last edited:
Should be doable with one of the FTMs, directly writing to the MOD and Channel compare value register.
What you need to know is the internal system clock frequency F_BUS which is for example 60MHz in a Teensy 3.6 running at 180MHz. Since you need a PWM output with a 50/50 duty cycle, the MOD value has to be a even number between 2 and 65534. For a given desired output frequency f0, divide F_BUS by f0 and take the closest even integer and take it as FTM MOD value and set the channel compare register to MOD/2 which is MOD >>1.
Example: f0=15kHz; mod=60MHz/15kHz=4000, channel compare value (Register depends on your selected pin) =2000 and you are done.
 
Theremingenieur,

thanks a lot for your quick response with an example!

I have another two questions:

* is there example code to modify the FTMs? Do I need to define an ISR for that?
* have I understood it right, that not all frequencies can be programmed, but only those where F_BUS / f0 is an even integer? That would mean, 10MHz is possible with FTM MOD == 6, but not 12MHz, because MOD would have to be 2.5
that would mean, one could not tune the radio to all Rx frequencies, but only to a few selected ones? AM radio could be feasible then (500 - 1800kHz receive frequency, but not shortwave 3-30MHz). Maybe I should think about an external programmable oscillator then, eg. the Si5351
 
No need for an ISR, if you can’t figure it out by looking at the analogWrite() and analogWriteFrequency() code in the core files, I could write the needed code for you.
You can use 32767 different fixed frequencies between ca. 900Hz and 30MHz with this simple zero cost approach. I say zero cost because the FTM will run autonomously without eating one single CPU cycle. Due to that integer division nature, continuous tuning is definitively not possible, but such low frequencies as you required (10kHz to 10MHz) look to me like IFs which would allow a fixed frequency approach, since almost no more broadcasting of public interest seems to happen in these frequency bands nowadays.

At least in France where I live and in the neighbored Germany and Switzerland, everything below analog FM radio (87.5MHz) seems deserted.
 
Thinking further in IF terms, but with still more coarse steps, one could think of coupling 2 channels of the same FTM to get 2 outputs with 90° phase shift, still without any processor load. But that would require a even channel compare register value and thus a mod value which had to be a multiple of 4 which gives “only” 16382 different frequencies in the range cited above.
 
Still just thinking out loud, another approach could be mis-using one of the I2S modules which have fractional dividers and thus would allow to generate square waves in a linear frequency raster. The price for that luxury would be that you’d get just a single frequency without the 90° phase shift option.
 
With Teensy 3.6, no need to fiddle with the timer registers. All you need is this:

Code:
void setup() {
  analogWriteFrequency(3, 10000000);
  analogWrite(3, 128);
}

void loop() {
}

This does depend on F_BUS being a multiple of 20 MHz. On 3.5 & 3.6 it defaults to 60 MHz. At other speeds or on Teensy 3.2 F_BUS is other frequencies like 48 MHz.

Tried it just now on a Teensy 3.6. Here's the waveform (using a short wire and ground lead clip - not the best probing for a high speed signal so you see a little ringning due to the leads, but quick and easy to connect)

file.png
 
Paul & Theremingenieur, thank you very much!

the whole thing is (so far) just a quick idea and thoughts on whether such a very simple SDR would be possible (but its a nice simplistic goal: just the Teensy and one other active component ;-)).

Promising! I did not think it would be that simple to have the Teensy produce RF signals ;-)!

I tested 10MHz, 5Mhz, 6MHz and 1621.5kHz, unfortunately as you explained there are large gaps in between the possible frequency values.

Here you can see the 1621.5kHz signal and its even and odd harmonics on my direct-sampling-SDR (I used a 15cm wire on pin 3 as a transmit antenna):

FMT1621_5.JPG

So far, so good. So this could theoretically work as an SDR Receiver for selected frequencies (only even divides of 60MHz). Good for a simple WWV time signal receiver at 10MHz or the Hawaiian WWVH at 15MHz.

But it would be nice to be able to have general coverage: the frequency steps could be as wide as 24kHz or even 48kHz, depending on the sample rate for the audio base band. We could use a tunable software LO to mix it to baseband again inside the 48kHz window.

Could you elaborate a little more on the "misuse of one of the I2S modules"?

All the best,

Frank DD4WH
 
Yes, I just did. Not sure what this should alter. Could you explain?

I did not observe any difference, tested 6MHz and 1621.5kHz output.
Code:
void setup() {
  //analogWriteFrequency(3, 10000000);
  //analogWriteFrequency(3, 1621500);
  //analogWriteFrequency(3, 6000000);
  analogWriteFrequency(3, 6090000);
  analogWrite(3, 128);
}

void yield(void) {};

void loop() {
}
 
The idea is to use only the master clock and/or bit clock generation of the I2S module without really transmitting I2S data. The i2s clock generator allows to take either F_PLL (often identical to F_CPU) or F_BUS and to do a f * x / y operation within a relatively wide range. I’ve not the reference manual at hands for the moment (sitting on the sofa with my iPad) but imagine starting from 60 MHz f_bus and starting with a y=2500 divider which gives 24kHz, then you could choose x as a multiplier from 1 to 255, thus covering a range from 24kHz to 6.120MHz
 
Theremingenieur, would that be possible to do independently of the ADC and DAC sample rate?
 
Yes, the I2S clock generation is independent and can run independently without impact on other modules and without eating CPU cycles.
 
By the way, the long « Theremingenieur » is a surname given to me by other people. I’m perfectly fine with being called by my first name, Thierry.
 
simplistic teensy SDR DD4WH.jpgfirst idea on hardware (upper half) and software (lower half) setup
the idea to use the MCP2036 is taken from Martin Ossmann, Funkamateur 06/2018
 
everything below analog FM radio (87.5MHz) seems deserted.

Thierry, if you switch on your AM radio right now, you will hear a vast lot of medium wave stations from southern Europe: Spain, Italy, Romania, also JilFM from Algeria on 531kHz: they play music all night, mostly cool stuff. Your are right: french and german stations have all closed down, but there is still a lot to hear (at night), even US and south american stations can be heard around dawn here in Central Europe, but not every day . . .
 
The idea is to use only the master clock and/or bit clock generation of the I2S module without really transmitting I2S data.

OK, I will have a look into the I2S audio lib code in the next days, thanks very much!
 
Hi Paul,

MCLK generator doesn't give a square wave, except for a very limited range.

I am not sure how to interpret this. But I am totally unexperienced with the details of the I2S protocol.

All figures I have seen of clock SCK (Wikipedia and the Philips I2S bus specification), however, show square waves. My interpretation would be: if SCK is a square, theoretically also MCLK should be a square. So could you point me to a reference where I can read more about waveforms and I2S in the Teensy 3.6?
 
The master clock is not guaranteed to be perfectly symmetrical with 50% duty cycle, due to the fractional frequency division. That’s why I’d tend to make the master clock (as usual in I2S configurations) 2 or 4 times higher and then use the bit clock which is the master clock divided by 2 or 4 and which should, after this division, be with a perfect duty cycle.
 
Thierry,

OK, I understand:

* MCK = MCLK is the master clock which is the fastest clock, but does not provide exact 50% duty cycles, but is "freely" adjustable in frequency because you can use fractional dividers

* SCK is the bit clock, which is derived from an integer divide of the MCLK, so has a better duty cycle

So, is my interpretation correct, that the highest setting for MCLK would be 60MHz as in the FTM? That would mean an output of 30MHz max SCK with a divide-by-2 (ideal for RF reception) and 15MHz max SCK with a divide-by-4 (misses some of the upper bands, but not really important, because it remains to be seen whether the cheap MCP2036 sets a hardware upper frequency limit here: reported as about 10MHz)

Now, the only thing I have to do for a first test is to figure out how to set the I2S clocks, will have a look into the code in i2s.h in the audio lib and try to figure out whats going on there!

Thanks a lot for your help so far!

All the best,

Frank DD4WH
 
If I remember well, but you’d have to look up that in the reference manual, there is a limit of 25 or 30MHz for the master clock.
 
I had a look into the reference manual . . . (> 2200 pages, thats a lot ;-)) and did not find a word about max frequency.

Inspired by this post (https://forum.pjrc.com/threads/3875...he-sample-rate?p=182458&viewfull=1#post182458) I found in the technical datasheet page 18, which says:

f I2S_MCLK max is 12.5MHz
f I2S_BCLK max is 4 MHz

on page 66 however, it says about the MIN cycle time (and the figure 31 clearly shows that cycle time means one full cycle):

MCLK 40nsec which is 25MHz
BCLK 80nsec which is 12.5MHz

So, I would hope that BCLK would provide a nice 50% duty cycle square up to 12.5MHz and I could use MCLK for frequencies from 12.5 - 25MHz

Up to now, I did not figure out exactly how to set BCLK, but could it be that only this needs to be done once to set and enable MCLK frequency generation?:

Code:
// enable MCLK output
I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
while (I2S0_MCR & I2S_MCR_DUF);
I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

I would expect the signal at pin 11 (I2S0_MCLK), lets see . . . I will try
 
It works:

Code:
/*************************************************************
 * 
 *  Use I2S transmitter to produce an RF frequency
 *  as a local oscillator for a minimum hardware SDR
 *  
 *  compile with 180MHz F_CPU
 *  
 *  DD4WH 2018-09-11
 * 
 *  GNU GPLv3
 * 
 *************************************************************/

  #include "kinetis.h"

  // pin 11 MCLK
  // pin 9 BCLK

  #define MCLK_MULT   9
  #define MCLK_DIV    99
  #define MCLK_SRC    0
  #define BCLK_DIV    4   // has to be even !
  
// MCLK = MULT * 180MHz / DIV
// BCLK = MCLK / 4

// mult 16 div 255
// MCLK = 11294k
// BCLK = 2823.5k

// mult 10 and div 255 = 7059k
// mult 12 and div 255 = 8470k
// mult 9 and div 99 = MCLK 16363.6kHz, BCLK 12272.6kHz ???
// mult 10 and div 100 = MCLK 18MHz
// mult 15 and div 100 = MCLK 27MHz
// mult 100 and div 150 = BCLK 3MHz


  void setup() 
  {
    config_i2s();
    start_i2s();
  }

  void config_i2s(void)
  {
  // abbreviations taken from kinetis.h
  // System Clock Gating Control Register 6
      SIM_SCGC6 |= SIM_SCGC6_I2S;

      // this enables the I2S master clock
      I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;

      // wait until divider update is ready!?
      // I2S_MCR_DUF: Divider Update Flag
      while (I2S0_MCR & I2S_MCR_DUF);

      // F_CPU = 180MHz
      // MCLK = MULT * F_CPU / DIV
      // change mult and div to control MCLK frequency
      // SAI MCLK Divide Register
      I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

      // I2S0_TCR2: SAI Transmit Configuration 2 Register 
      // I2S_TCR2_MSEL: MCLK select, 0=bus clock, 1=I2S0_MCLK   
      // I2S_TCR2_SYNC: 0=async 1=sync with receiver
      // I2S_TCR2_BCP: Bit clock polarity
      // I2S_TCR2_BCD: Bit clock direction
      // I2S_TCR2_DIV --> Bit clock divide by (DIV+1)*2, 
      //     if 1, divide-by-4; if 0, divide-by-2
      I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
                | I2S_TCR2_BCD | I2S_TCR2_DIV(BCLK_DIV / 2 - 1);

      // Pin Mux Control
      CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
      CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
  }

  void start_i2s(void)
  {
    // I2S_TCSR_TE: Transmitter Enable
    // I2S_TCSR_BCE: Bit Clock Enable
      I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable
  }


  void loop() 
  {
    
  }

It would be nice of somebody could have a look at the code whether I am missing something.

I could observe MCLK signal at pin 11 as expected and BCLK at pin 9, again observed with a wire antenna at the pins and using a direct sampling SDR as a spectrum analyser.

It seems MCLK can be produced up to at least 36MHz.

I observe some strange frequencies in BCLK when I use higher frequencies, but I have to test some more.

* Is there some kind of algorithm that is able to calculate the multipliers and dividers with a given MCLK frequency?
* is there an upper limit for MCLK_MULT and MCLK_DIV?

All the best,

Frank DD4WH
 
Status
Not open for further replies.
Back
Top