Работа RP2040 с внешним кварцевым генератором

Сейчас на платах типа RP2040 уже стоят кварцевые генераторы. Но эти кварцы обычно низкого качества и при их нагреве частота их значительно изменяется.
По ссылке ниже приведен пример работы с внешним кварцевым генератором.

Поэтому есть несколько вопросов:
1 - Просьба скинуть ссылку на литературу по данной теме.
2 - Это действительно так хорошо, как описано в статье?
3 - Кто нибудь занимался этим вопросом?

Скажите, насколько меняется частота при нагреве на вашей RP2040 с штатным кварцем?

У меня одна плата пришла с повреждённым кварцем, пересадил с какого-то донора (по размерам был в два раза больше, но всё без проблем) , так что плохой кварц видимо проще заменить

Плата у меня RP2040 Zero. Кварц там с 4 ногами размером 3Х5 мм. Если и получится заменить то не факт, что станет лучше.
По поводу термостабильности. Кварц находится рядом с чипом и поэтому при нагреве чипа частота меняется. Чип греется примерно до 50 Град. Изменение частоты при нагреве около 2 ppm.

Не понял, каким тут боком программирование. Перенёс в “аппаратные”.

что ты там на него нагрузил? у меня при разгоне внутренний датчик температуры показывал чуток больше 40 )))

Нагрузка это цифровые пины от ЖК дисплея. Напряжение питания ядра 1.3 в и частота 240 мГц. Ток потребления 60 мА. Ну и в комнате около +25 град.
Сейчас вопрос стоит не в том, чем я его нагрузил, а в том что я хочу код из этой статьи перенести на Arduino и мне нужен человек, который сможет указать мне на ошибки в моем программном коде, поскольку я понятия не имею как этот код работает.
Поэтому я и хочу почитать чего нибудь по этой теме.

По той ссылке описывается частотомер, основанный на данных с GPS.
Вы хотите тоже частотомер собрать? Или что?

Для начала я хочу получить переменную (64 бит), которая считает импульсы внешнего кварцевого генератора (в статье это 10 мГц).

Я сильно не вчитывался, но не припомню никакой переменной в 64 бит (может быть она там и есть, не буду спорить).
Так зачем вам эта переменная? Вы всё-таки хотите частотомер собрать?

Диапазона 32 бит достаточно для подсчета Гигагерцев (значения uint32_t от 0 до 4 294 967 295).

Пока только счетчик. А число 4 294 967 295 не такое большое. Если поставить кварц на 50 мГц то до переполнения он проработает всего 90 секунд.

Ну ты хотя бы с основами ознакомился бы, прежде чем такую ерунду писать. Откуда у тебя переполнение возьмется и для чего???

Вот с этим хотя бы ознакомься: https://dzen.ru/a/Ya-V1mMNY2pRwjTt

В кратце можно описать работу простого частотомера на мк так:
Берём эталонное время и с помощью него отсчитываем некоторый период (вот тут для повышения точности отсчета и использовался gps в статье), а пока период длится - считаем сколько импульсов пришло.

Если в качестве периода выбрать интервал в 1 секунду, то насчитанное количество входящих импульсов за этот период в 1 секунду и будет частота в герцах.

И на каждом новом периоде счет обнуляется. Так что переполнения там быть в принципе не может.

Надо лучше читать текст. Нужен счетчик импульсов. И как я понимаю по твоим словам он не может переполниться. Хорошо пусть будет счетчик импульсов на 32 бита без переполнения с тактовой частотой 10 мГц, который никогда не переполняется.
А ссылка на Дзен твоя ну просто умилила. Особенно понравилось описание резонансного частотомера. Наверное до Великой отечественной они уже были.
А дальше описывается частотомер по схемотехнике начала 80-х годов. Такую схему даже я собирал на 176 серии.

Ну, да ладно, я отвлекся. Выкладываю код для дальнейшей работы, а дальше потихоньку буду исправлять ошибки. Сначала попробую добавить Loop() и Setup() и убрать ошибки библиотеки и вывода в Serial.

#include "main.h"

#define PPS_PIN 2
#define SIGNAL_PIN 15

volatile int ppsFlag = 0;
volatile absolute_time_t ppsTime;
volatile uint16_t signalCount;
volatile uint32_t signalWrapCount = 0;

uint signalSlice;

void pps_callback(uint gpio, uint32_t events) {
    signalCount = pwm_get_counter(signalSlice);
    ppsTime = get_absolute_time();
    ppsFlag = 1;
}

void on_pwm_wrap() {
    pwm_clear_irq(signalSlice);
    signalWrapCount++;
}

uint setup_pwm_counter(uint gpio) {
    // can only use input on channel B
    assert(pwm_gpio_to_channel(gpio) == PWM_CHAN_B);
    uint slice_num = pwm_gpio_to_slice_num(gpio);
    pwm_config cfg = pwm_get_default_config();
    pwm_config_set_clkdiv_mode(&cfg, PWM_DIV_B_RISING);
    pwm_config_set_clkdiv(&cfg, 1.f);
    pwm_init(slice_num, &cfg, false);
    gpio_set_function(gpio, GPIO_FUNC_PWM);

    // register interrupt handler
    pwm_clear_irq(slice_num);
    pwm_set_irq_enabled(slice_num, true);
    irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
    irq_set_enabled(PWM_IRQ_WRAP, true);

    pwm_set_enabled(slice_num, true);
    return slice_num;
}

int main() {
    stdio_init_all();

    printf("Starting...\n");

    // setup signal counter
    signalSlice = setup_pwm_counter(SIGNAL_PIN);

    // setup PPS
    gpio_init(PPS_PIN);
    gpio_set_dir(PPS_PIN, GPIO_IN);
    gpio_pull_down(PPS_PIN);
    gpio_set_irq_enabled_with_callback(PPS_PIN, 0x08, 1, pps_callback);

    uint32_t prevTotal = 0;
    uint32_t seconds = 0;

    while(1) {
        if(ppsFlag == 1) {
            uint32_t millis = to_ms_since_boot(ppsTime);
            uint32_t total = (signalWrapCount * 65535) + signalWrapCount + signalCount;
            uint32_t delta = total - prevTotal;
            printf("%lu %lu %d %d %lu %lu\n", (unsigned long)seconds, (unsigned long)millis, signalWrapCount, signalCount, (unsigned long)total, (unsigned long)delta);
            prevTotal = total;
            seconds++;
            ppsFlag = 0;
        }
        tight_loop_contents();
    }

}

Да, а если всё-таки прочитать статью до конца, то и на мк там тоже описываются (статья как раз для понимания как работают частотомеры в принципе).
В прочем, умных учить - только портить. Удачи.

Пытался найти хоть слово про МК но не нашел. Может я с вами по разному буковки умеем читать? Хотя может быть это имелось в виду: “даже если частотомер выполнен на контроллере – основная структура выполнена программно.” Но это не в конце статьи, а в середине. Не надо людей в заблуждение вводить.

Зато в конце есть обнадеживающая информация:
"Работу индикаторной части мы рассмотрим отдельно в следующем материале. "
А поскольку это самая сложная часть частотомера, то автор не раскрыл принцип работу электронного-счетного частотомера полностью.

Поскольку данная статья для начинающих, то очень плохо, что автор не расписал достоинства и недостатки данного типа частотомеров.

И по поводу картинок. Данные картинки называются временные диаграммы. И они предназначены для более простого поиска неисправностей в цифровой технике. Когда микросхем мало (до 30 штук) они вроде бы не нужны. Но возьмем к примеру Систему ЧПУ 2Р22. Там целый альбом схем. На каждой странице микросхем по 50 и еще на другие листы уходят. И вот на занятиях нам приходилось рассказывать преподавателю, при выходе из строя какой микросхемы где нужно чего искать. А еще проектировщики, чтобы Американские шпионы не украли схему ЧПУ наделали в ней специально ошибок.

И не только буковки.

Из статьи:

Всё что я выше говорил - наглядно представлено.

Вместо того чтобы «упражняться в красноречии», лучше бы материал почитал (и знания подтянул). Но это если ты хочешь свой вопрос решить.

Более не отвлекаю. Можешь на меня время не тратить и не отвечать.

Теперь выкладываю первые результаты работы. Поскольку я понятия не имею как оно работает спрашивать по программе меня бесполезно.

  1. Входную частоту 1 Гц с высокостабильного генератора подаем на вывод 2.
  2. Частоту с кварцевого генератора подаем на вывод 15.
  3. Каждую секунду на экране выводятся новые параметры.
  4. Если на выводе 2 частоты нет, то на экран ничего не выводится.
  5. При выводе на экран значение Т1 - длительность импульса 1 Гц в микросекундах кварца на плате.
  6. Т2 - время в миллисекундах для внешнего кварцевого генератора. (как работает не знаю)
  7. F - частота внешнего кварцевого генератора. Частота должна быть как минимум в 3 раза меньше частоты работы процессора. Оптимально для частоты 133 мГц частота внешнего кварца не более 40 мГц. Если хотите получить более стабильные результаты частоту кварца не использовать выше 32 мГц.
  8. Точность изменения частоты составляет +/- 1 Гц и не зависит от частоты внешнего кварца. Она была одинаковой при кварце на 2 и на 32 мГц.

Ну в общем пока все. Просьба глянуть.

//---VER01
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/irq.h"
//-----------------------
#define PPS_PIN 2 //Сигнал частотой 1 Гц.
#define SIGNAL_PIN 15//Сигнал с внешнего кварцевого генератора.
//-----------------------
uint32_t seconds = 0;
uint64_t total;
uint64_t delta;
uint64_t prevTotal = 0;
volatile int ppsFlag = 0;
volatile absolute_time_t ppsTime;
volatile uint16_t signalCount;
volatile uint64_t signalWrapCount = 0;
unsigned long t3,t4,tim;
uint32_t millis_n;
uint signalSlice;
uint slice_num;
//-------------------------
void pps_callback(uint gpio, uint32_t events)
{
  signalCount = pwm_get_counter(signalSlice);
  ppsTime = get_absolute_time();
  ppsFlag = 1;
}
//-------------------------
void on_pwm_wrap()
{
  pwm_clear_irq(signalSlice);
  signalWrapCount=signalWrapCount+1;
}
//-------------------------
uint setup_pwm_counter(uint gpio)
{
  // can only use input on channel B
  assert(pwm_gpio_to_channel(gpio) == PWM_CHAN_B);
  slice_num = pwm_gpio_to_slice_num(gpio);
  pwm_config cfg = pwm_get_default_config();
  pwm_config_set_clkdiv_mode(&cfg, PWM_DIV_B_RISING);
  pwm_config_set_clkdiv(&cfg, 1.f);
  pwm_init(slice_num, &cfg, false);
  gpio_set_function(gpio, GPIO_FUNC_PWM);
  // register interrupt handler
  pwm_clear_irq(slice_num);
  pwm_set_irq_enabled(slice_num, true);
  irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
  irq_set_enabled(PWM_IRQ_WRAP, true);
  //-------------------------
  pwm_set_enabled(slice_num, true);
  return slice_num;
}
//-------------------------
void setup()
{
  //---set Serial------------
  Serial.begin(115200);
  while (!Serial);
  //-------------------------
  // setup signal counter
  signalSlice = setup_pwm_counter(SIGNAL_PIN);
  // setup PPS
  gpio_init(PPS_PIN);
  gpio_set_dir(PPS_PIN, GPIO_IN);
  gpio_pull_down(PPS_PIN);
  gpio_set_irq_enabled_with_callback(PPS_PIN, 0x08, 1, pps_callback);
}
//-------------------------
void loop()
{
  if(ppsFlag == 1)
  {
    t3=micros();
    tim=t3-t4;
    t4=t3;
    millis_n = (unsigned long)(to_ms_since_boot(ppsTime));
    total = (signalWrapCount * 65535)  + signalCount;
    delta = total - prevTotal;
    Serial.print(" S = ");Serial.print(seconds);
    Serial.print(" T1= ");Serial.print(tim);
    Serial.print(" T2= ");Serial.print(millis_n);
    Serial.print(" F = ");Serial.print(delta);
    Serial.print(" N = ");Serial.println(total);
 //   Serial.print("  ");Serial.print(signalWrapCount);
 //   Serial.print("  ");Serial.println(signalCount);
    prevTotal = total;
    seconds=seconds+1;
    ppsFlag = 0;
    t4=t3;
  }
  tight_loop_contents();
}

Программа банально считает число импульсов на сигнальном входе за 1сек.
Поскольку секунды отмеряются сигналом точного генератора, то все операции с миллис и микрос в коде абсолютно лишние.

Делал я часики на часовом кварце, с калибровкой по GPS 1сек и термокомпенсацией по формуле Сейко - погрешность меньше 30 сек в год. Причем один экз плыл туда-потом оттуда и получалось 1сек в год ))