STM32 GPSDO

What are you developing?
AndrewBCN
Posts: 105
Joined: Sun Apr 25, 2021 3:50 pm
Answers: 1
Location: Strasbourg, France

64-bit counter, and 16-bit PWM to replace 12-bit DAC

Post by AndrewBCN »

I have just pushed to GitHub version v0.03e of the STM32 GPSDO firmware.
This version implements:
1. A 64-bit counter (in software, using interrupts) and 64-bit ring buffers. Thanks to the Hardware Timer library, using interrupts adds a single line of C to the setup code, and the callback function contains a single line of actual code, to increment the overflow counter!
2. I am testing the generation of an analog output with 16-bit resolution by taking advantage of the 16-bit resolution of the PWM output pins. Note that to really get 16-bit PWM resolution, a small typo has to be fixed in the current 2.0.0 release of STM32 core, see GitHub issue #1378.

On the hardware side, I put together/wired two more GPSDOs on breadboards, since I had the parts available to do so. This allows me to test the firmware on one GPSDO while having two more as backups. :mrgreen:

Here is the Timer 2 setup code:

Code: Select all

  // Setup and start Timer 2 which measures OCXO frequency
  
  // setup pin used as ETR (10MHz external clock from OCXO)
  pinMode(PA15, INPUT_PULLUP);    // setup PA15 as input pin
  pinModeAF(PA15, GPIO_AF1_TIM2); // setup PA15 as TIM2 channel 1 / ETR
  
  // setup Timer 2 in input capture mode, active input channel 3
  // to latch counter value on rising edge

  // Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
  HardwareTimer *FreqMeasTim = new HardwareTimer(TIM2);
  
  // Configure rising edge detection to measure frequency
  FreqMeasTim->setMode(3, TIMER_INPUT_CAPTURE_RISING, PB10);

  // Configure 32-bit auto-reload register (ARR) with maximum possible value
  TIM2->ARR = 0xffffffff; // count to 2^32, then wraparound (approximately every 429 seconds)

  #ifdef COUNT_64_TEST
  // Configure the ISR for the timer overflow interrupt
  FreqMeasTim->attachInterrupt(Timer2_Overflow_ISR);
  #endif // COUNT_64_TEST

  // select external clock source mode 2 by writing ECE=1 in the TIM2_SMCR register
  TIM2->SMCR |= TIM_SMCR_ECE; // 0x4000
  
  // start the timer
  FreqMeasTim->resume();
  
And here is the single line of code callback routine:

Code: Select all

#ifdef COUNT_64_TEST
// Interrupt Service Routine for TIM2 counter overflow / wraparound
void Timer2_Overflow_ISR(void)
{
  tim2overflowcounter++;
  // do we have to manually clear the UIF bit in TIM2 status register? It seems so...
}
#endif // COUNT_64_TEST
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: STM32 GPSDO

Post by dannyf »

i haven't had a chance to see your code. but some suggestions:
1. need to be careful with atomicity of the overflow counter + timer counter reading - not difficult but it may introduce jitter.
2. some chips have hardware 64 bit counters. LM4F120 / TM4C chips for example.
3. depending on the oscillator used, and desired range, 16-bit pwm may not be that useful.
4. if you have to, one way to increase pwm resolution is through dithering -> need to use long time constant, however.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: STM32 GPSDO

Post by dannyf »

I was coding a simple arduino clone for STM32F0 chips: https://github.com/dannyf00/Another-Ard ... ee/STM32F0

the pwm implementation there is 16-bit (actually variable) - in case it helps.
AndrewBCN
Posts: 105
Joined: Sun Apr 25, 2021 3:50 pm
Answers: 1
Location: Strasbourg, France

Re: STM32 GPSDO

Post by AndrewBCN »

Hi dannyf,
Thank you very much for your comments.

1. Regarding the 16-bit resolution PWM in the STM32 family, this is very well implemented in the Wiring library. The PWM defaults to 8-bit resolution - I assume this is for Arduino compatibility reasons - but it's very easy to change, and it follows the Arduino API to do so:
https://www.arduino.cc/reference/en/lan ... esolution/
With a single line of code, one can change from the default 8-bit PWM resolution to 16-bit PWM resolution, which (as far as I know) is supported by all STM32 MCUs:

Code: Select all

analogWriteResolution(16);
So i went ahead and implemented a 16-bit DAC using a PWM pin and a simple 2 x RC filter with 2 x (20kOhm + 10µF) and I am using that to control the OCXO frequency, and so far (12 hours of testing) it seems to be working no worse than the 12-bit I2C DAC I was using until now.

2. The 64-bit counter code is still work in progress, it needs more testing at this stage.

You'll find these changes in the latest release of the code, GPSDO - v0.03g.
https://github.com/AndrewBCN/STM32-GPSDO/releases
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: STM32 GPSDO

Post by dannyf »

some random thoughts for your consideration:
1. you may want to typedef the type for the counter and use that consistent. if you want to change it to 32-bit or 64 bit, it can be done easily;
2. need to make sure that your overflow counter and the captured value don't get out of wack. a double read will ensure that.
3. checking changes in the captured value isn't a reliable way: theoretically you can have two valid but identical captured values. use the status register -> or capture ISR.
4. no need to clear the capture register.
5. may want to separate the conversion of frequency count to control variable and driving the DAC with that control variable into two functions so they can easily changed out in the future.
6. I would just record the counts in the ring buffer (using the typedef in #1): much easier to calculate frequency and eliminates the accumulation of rounding errors.

hope that helps.
AndrewBCN
Posts: 105
Joined: Sun Apr 25, 2021 3:50 pm
Answers: 1
Location: Strasbourg, France

Re: STM32 GPSDO

Post by AndrewBCN »

dannyf wrote: Tue May 25, 2021 9:08 pm ...
6. I would just record the counts in the ring buffer (using the typedef in #1): much easier to calculate frequency and eliminates the accumulation of rounding errors.

hope that helps.
Hi dannyf,
Regarding 6.
I do record the counts in the ring buffers, because as you noted, it makes it much easier to calculate the average frequency:

Code: Select all

if (buffer_full)
  average_frequency = (latest_count - oldest_count) / size_of_ring_buffer 
endif
Regarding 1. to 5. those are all excellent suggestions, I am going to check them one by one. Thank you very much!
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: STM32 GPSDO

Post by dannyf »

good to know.

for that calculation, you may want to see if you want to incorporate rounding; and if you want to display decimal points. Both can be done fairly easily.
AndrewBCN
Posts: 105
Joined: Sun Apr 25, 2021 3:50 pm
Answers: 1
Location: Strasbourg, France

Version v0.03h - measurement resolution

Post by AndrewBCN »

dannyf wrote: Wed May 26, 2021 2:48 pm good to know.

for that calculation, you may want to see if you want to incorporate rounding; and if you want to display decimal points. Both can be done fairly easily.
Hi dannyf,
Great minds think alike! :!:

For the 10s buffer I show 1 decimal, for the 100s buffer I show 2 decimals, and for the 1000s buffer I show 3 decimals! For the instantaneous frequency measurement (1s) I show, of course, zero decimals.

Here is the output printed on the serial monitor once per second for version v0.03h which I just uploaded to GitHub:

Code: Select all

Wait for GPS fix max. 5 seconds...
Fix time 884mS
Uptime: 000d 00:52:11
New GPS Fix: 
Lat: 48.~~~~~~ Lon: 7.~~~~~~ Alt: 132.9m Sats: 8 HDOP: 0.90
UTC Time: 06:05:26 Date: 27/5/2021

Vctl: 1.72  DAC: 2177
VctlPWM: 1.78  PWM: 35345
Vcc: 5.05
Vdd: 3.30

Using 64-bit counter implemented in software
Most Significant 32-bit Count (OverflowCounter): 7
Least Significant 32-bit Count (TIM2->CCR3): 1225527159
64-bit Count: 31290298231 Frequency: 10000000 Hz
10s Frequency Avg: 10000000.0 Hz
100s Frequency Avg: 10000000.00 Hz
1000s Frequency Avg: 10000000.000 Hz
BMP280 Temperature = 21.2 *C
Pressure = 1018.8 hPa
Approx altitude = 67.2 m
AHT10 Temperature: 18.16 *C
Humidity: 77.63% rH
And I didn't "adjust" the numbers above, it just so happens that this particular GPSDO at that particular 1000s time period was adjusted to 10MHz +/- 0.1ppb in relation to a rather good GPS fix (8 sats tracked, HDOP < 1)! :mrgreen:

I am still testing the 16-bit PWM output, it seems to be working well but I am not yet confident regarding temperature drift, repeatability, etc.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: STM32 GPSDO

Post by dannyf »

the use of double is convenient but costly -> not much better than 5 or 6 digits.

what I often do is to scale up to an integer. so 1.234 for example would be represented as 1234. in your case it is actually much easier as your numbers are naturally scaled up -> they are calculated from count differentials.

let's say that your 1000s count differential is cnt_def. you just need to convert it to a display as (cnt_def / 1000) + '.' + (cnt_def % 1000); this is much easier + faster on an avr.
AndrewBCN
Posts: 105
Joined: Sun Apr 25, 2021 3:50 pm
Answers: 1
Location: Strasbourg, France

Re: STM32 GPSDO

Post by AndrewBCN »

dannyf wrote: Thu May 27, 2021 2:25 pm the use of double is convenient but costly -> not much better than 5 or 6 digits.

what I often do is to scale up to an integer. so 1.234 for example would be represented as 1234. in your case it is actually much easier as your numbers are naturally scaled up -> they are calculated from count differentials.

let's say that your 1000s count differential is cnt_def. you just need to convert it to a display as (cnt_def / 1000) + '.' + (cnt_def % 1000); this is much easier + faster on an avr.
Hi dannyf,
Thank you, that's an elegant way to display these values without converting them to double (which is what I have been doing) and you are quite right that it would save quite a few CPU cycles... but I am counting on the little 100MHz STM32F411 MCU to do all the hard work for me! And displaying variables is a once-per-second task in my project, the MCU spends most of its time waiting for complete NEMA sentences from the GPS module i.e. essentially doing nothing.
Post Reply

Return to “Projects”