Генератор с регулируемоей частотой на ардуино.

ua6em
ua6em аватар
Онлайн
Зарегистрирован: 17.08.2016

Кстати, применённая микросхема позволяет задействовать для стабилизации частоты технологию GPSDO и, в частности одно из предложенных радиолюбителями решений выполнено Кареном Тадевосяном RA3APW )))

 

На диапазоне 10м в режиме GPSDO среднеквадратическое отклонение несущей частоты составляет примерно 7 миллиГц, а пиковое значение примерно 90 миллиГц.

Дима, что ты там говорил о любительских конструкциях )))
 

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

По-моему, для данного генератора это уже перебор, тем более что надо тащить кабель от подоконника до стола с генератором для антенны GPS. Врядли внутри помещения Глонасс-ГПС будет работать.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Продолжил издевательства над Si5351. Повысил верхний предел частоты с 200Е7 до 220Е7 в строке 453, дохожу до  214 000 000 - норма, ещё плюс мегагерц и вместо 215 000 000 получаю 4000 Hz. Начинаю выяснять точную величину этого скачка - получилось 214 748 364 держит, а вместо 214 748 365 опять получаю 4000. Ладно бы "круглая" граница была, а то непонятно что.

Разобрался! Oказывается, уткнулся в предел int32, пришлось переменную freq сделать int64. Вроде работает, во всяком случае на 440 МГц вторую гармонику вижу, но теперь непонятно как работает регулировка частоты. В общем ну его нафиг, не очень-то и хотелось, откатился назад.

Если я правильно понял, всё это - следствие от добавления шага 0,1 Гц - переменная freq выросла в десять раз. Будем "скрещивать" версии 3.1 и 3.2 Десятые герца мне не нужны, а вот разделение пробелами или точками надо будет сделать.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

MAG-N, всё верно, именно из-за предела int32 я уменьшил верхнюю частоту, так как переход  с int32 на uint32 или на int64 тянул за собой огромные переделки из-за такого пустяка. PS:  Разница между 3.1 и 3.2 довольно существенна в плане улучшения кода. Особенно в частотометре.  Даже если выдрать только вывод на дисплей всё равно придёться повозиться, там же всё "заточено" под вывод удесятерённого значения. Так что проще смирится :)

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Нацарапал утилитку для калибровки Si5351. Работает на "железе" V3.2, частота 10 МГц, но можно подставить любую другую. Работа с энкодером "сплагиачена" из скетча генератора (может чего лишнего навыдёргивал). В общем, работает и ладно. Cкетч генератора надо дополнить строкой (после строки 45), и вместо 80000 ввести свою константу (Cal.factor).

si5351.set_correction(80000, SI5351_PLL_INPUT_XO);



#include "Adafruit_ST7735.h"
#include <SPI.h>
#include "si5351.h"
#include <Wire.h>

#define enc_on()  timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int); 
#define enc_off()  timer_detach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT); 

Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11 ,PB10);
Si5351 si5351;

volatile int encstep=1; //шаг изменения частоты по умолчанию (желаемый *10)
volatile int32_t cal_step=0;
volatile int32_t cal_factor = 0;
uint64_t target_freq = 1000000000ULL; // 10 MHz, in hundredths of hertz

void setup() {
SPI.setModule(2);// выбор SPI2
i2c_master_enable(I2C1, I2C_REMAP); //SDA PB9, SCL PB8
Wire.begin();
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(target_freq, SI5351_CLK0);
pinMode(PB1,INPUT_PULLUP); //key encoder
pinMode(PB6,INPUT_PULLUP); //encoder
pinMode(PB7,INPUT_PULLUP); //encoder

///////// Таймер4 -обработчик энкодера     
TIMER4_BASE->CR1=(1<<2)|(1<<9);//CKD10 URS
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );//cc1s, cc2s input mapped TI1/TI2
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);//Capture/Compare 1,2 output polarity
TIMER4_BASE->SMCR=(1<<1)|(1<<0);//Encoder mode3(SMS bit)стр.407
TIMER4_BASE->CNT=0;
TIMER4_BASE->ARR=1;//ограничение счёта ( =3 для двухимпульсного энкодера)
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1; 
enc_on();
TIMER4_BASE->CR1|=(1<<0);//запуск 

attachInterrupt(PB1, key_enc_int, RISING); //прерывание кнопки энкодера

tft.initR(INITR_BLACKTAB);
tft.setRotation(3); tft.fillScreen(ST7735_BLACK); tft.setTextWrap(0);
tft.setTextColor(ST7735_GREEN, ST7735_BLACK);  tft.setTextSize(1);
tft.setCursor(0, 10); tft.print("Si5351 Calibration utility");
tft.setCursor(35, 20); tft.print("(c) R1BAO 2018");
tft.setTextColor(ST7735_RED, ST7735_BLACK);  tft.setTextSize(2);
tft.setCursor(20, 35); tft.print("Cal.factor");
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.setCursor(15, 110); tft.print("Step=");
}

void loop() {
    
si5351.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
tft.setTextSize(3);
if (cal_step==0){tft.setCursor(35, 65); tft.print("        ");}
if (abs(cal_factor)>=1000000){tft.setCursor(20, 65); tft.print(cal_factor);}
if (abs(cal_factor)<1000)   {tft.setCursor(20, 65);  tft.print(cal_factor); tft.print("    ");}
  else if (abs(cal_factor)<10000)  {tft.setCursor(20, 65);  tft.print(cal_factor); tft.print("   ");}
  else if (abs(cal_factor)<100000) {tft.setCursor(20, 65);  tft.print(cal_factor); tft.print("  ");}
  else if (abs(cal_factor)<1000000){tft.setCursor(20, 65);  tft.print(cal_factor); tft.print(" ");}

tft.setTextSize(2); 
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.setCursor(75, 110);
switch (encstep) { 
  case 1:   tft.print("1 Hz  "); break;
  case 10:  tft.print("10 Hz "); break;
  case 100: tft.print("100 Hz"); break;
 }
}

//обработчик прерываний энкодера
void enc_int(){   
  if((TIMER4_BASE->CR1)&1<<4) {cal_step+=(encstep*-1);} 
  else {cal_step+=(encstep*1);}
  if (cal_step>=10000) {cal_step=10000;}
  if (cal_step<=-10000) {cal_step=-10000;}
  cal_factor = cal_step*100;
}

// обработчик кнопки энкодера
void key_enc_int(){
  encstep*=10;
  if (encstep >100) {encstep=1;}
}

 

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

ua6em пишет:

И какова процедура калибровки?

Чем измерять частоту - это у кого что есть. Я, за неимением частотомера, использовал трансивер и пограмму WSJT-X. На частоте 10 МГц работают две станции эталонных частот из Москвы и Новосибирска, поэтому и выбрал её для калибровки. Настраиваемся, находим эталонную и свою частоты. Далее всё очень просто: энкодер по часовой стрелке - частота увеличивается, а значение Cal.factor будет отрицательным. Против часовой - частота уменьшается, Cal.factor положительный. Нажатие на кнопку энкодера - шаг перестройки. При шаге 1Гц меняется разряд сотен, а десятки и единицы - это для шага 0.1 и 0.01 Гц - я их не делал, нафиг не надо. У меня Cal.factor получился 79600, т.е. сдвиг частоты вниз на 796 Гц. После точной настройки полученное значение Cal.factor вписываем в скетч и радуемся.

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

Спасибо большое dimax за генератор. Очень хороший получился аппарат.

Я сделал два генератора что бы через смеситель получить пакеты импульсов. 

Все отлично работает, но как говорится аппетит приходит во время еды. В пакетах нет сихронизации по фазе ну и конечно их качает. Малость не приятно, но не смертельно. Большое Спасибо.

  

miklin
Offline
Зарегистрирован: 08.06.2018

 Моя плата V3.2.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

А я не стал связываться с мультиплексором. Поставил дешифратор 74138 и к нему 4 реле, они пятивольтовые с током потребления около 10 мА. Если применить 74145, у которого ток выхода до 80 мА и допустимое напряжение на выходе 15 В - можно использовать 9-12 вольтовые реле с бОльшим током срабатывания.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Выложил полный архив с  Arduino IDE и всеми свежими библиотеками и скетчем версии 3.2 в сообщении   #50  Просто разархивировать и выбрать скетч в примерах. Кстати там чуть свежее версия скетча, но принципиальных отличий от ранее выложенной нет.

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

anri пишет:

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

Именно так и должно быть, в сетапе ведь отключается виртуальный СОМ порт. Повторная перепрошивка возможна нажатием на ресет в нужный момент, или просто отлюченную плату включить в USB порт в нужный момент.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Похвастаюсь :-) Мой вариант генератора.  Снимал на телефон - ну не умеет он с близкого расстояния снимать, так что извините за качество. Это называется оптическая аберрация.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Самое главное - работает, и очень хорошо работает. Clock Gen настроил по эталонной частоте, потом по этому генератору настроил частотомер (на PIC-e), затем пересчитал "множитель DDS для частоты F_CPU 72МГц"  - и погрешность установки частоты в этом режиме стала почти "нулевая" :-) В режиме PWM на частоте 36 МГц ошибка 1200 герц - но тут тяжело исправить, надо конденсаторы, которые возле кварца, подгонять - ну его нафиг, и так хорошо. Непонятно одно - почему частота скачет на несколько десятков герц туда-сюда, причём только в режиме PWM. Вот картинка сигнала 36000000. Цена деления - 50 Гц.

А это сигнал Si5351, трансивер на частоте 35.999 - биения 1 кГц

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

MAG-N, думаю, что виноват PLL синтезатор в МК.  В Si5351 тоже PLL, но он всё таки задумывался изначально как тактовый генератор, и у него параметры лучше.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Наверное, так как на частоте 9 МГц та же пила, но размах раз в пять-шесть меньше, как и отклонение. На глаз уже не очень заметно, а ухо чует.  Да и не очень то надо на таких частотах PWM гонять, ну мегагерц-два ещё может и понадобится, а дальше Х его З. Самая высокая частота PWM в моих "самоделках" - 31 кГц для управления вентилятором в усилителе на 144 МГц. На звуковых частотах и на малых оборотах эта зараза работает скорее динамиком, чем вентилятором. Зудит, как комар.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Ещё похвастаюсь: с помощью данного генератора поднастроил частотомер (на частоте 200 МГц)- и вот результат - на проводок длиной около 10 см сигнал радиостанции ловит с расстояния 3-4 метра. (435 МГц)

Ванька79
Offline
Зарегистрирован: 15.04.2019

Выкладываю свою печатку в LAY,вариант на голой атмега328р,весия 2.4,в железе проверена

Предусмотрена зарядка на LTC4054 аккумулятора от сотового и USB разъем для зарядки

https://yadi.sk/d/oF1lFfxWapnwxQ

Спасибо!

 

в ардуино 1.8.8 не мог победить расположение массивов,в 1.6.5 все норм но убрал 6 "0х00"

Также энкодер наоборот (исправляется программно)

condensator80
Offline
Зарегистрирован: 24.02.2018

собрал генератор, версия 2.4 с дисплеем 5110, и небольшими доработками

надписи пока не делал

внутри

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Версия 3.3
-изменён алгоритм обработчика энкодера и обработчика кнопки энкодера, за счёт этого больше не требуются дребезгоподавляющие конденсаторы.
-побочный бонус за счёт первого пункта -управление частотой стало более быстрым, уменьшилось время отклика при вращении энкодера.
-добавлена поддержка микроконтроллеров с интегрированным ЦАП (этой же серии F103), для них не нужно собирать резисторную матрицу, но О.У. на выходе крайне желателен (пример в Application note AN4566)  Проверил работу на  MK stm32f103ret6, не тестировал.

 

Скетч:

 /*Генератор с регулируемой частотой v3.3 (C)Dimax 
 не нужны антидребезговые конденсаторы, добавлена возможность вывода DDS сигнала через МК с встроенным ЦАП */
#define pwm2_polar 0 //полярность выхода PWM2
#define paper        0x000000 // цвет фона экрана
//#define DACINT //раскомментировать  для МК с встроенным ЦАП, выход сигнала DDS с пина PA4 
#define DDSMAX 1E7 //максимальная частота для генератора DDS (удесятерённая)
#define dds_mpl_72 835.05327478167234049174700635502 //множитель DDS для частоты F_CPU 72МГц
//для пересчёта множителя необходимо: частоту на экране прибора * текущий множитель и разделить на фактически измеренную частоту
#define dds_mpl_128 469.7191655978919104715512499704  //множитель DDS для  частоты F_CPU 128Mhz
 #include <Adafruit_GFX_AS.h>    // Core graphics library
   #include <Adafruit_ST7735.h> // Hardware-specific library
    #include <SPI.h> 
    #include "si5351.h"
     #include <Wire.h> 
      #include <libmaple/dac.h>
       Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10); 
        Si5351 si5351;
         boolean si5351_found=0;
      volatile int enc_tic=0, duty_in=50, mon_flag, divider, modebit=1;
      volatile int mode=1;// 0- GEN_si5351, 1-PWM, 2-Duty, 3..7 DDS, 8-Freqmeter 
      volatile int encstep=10; //шаг изменения частоты по умолчанию (желаемый *10)
     volatile int32_t freq=10000; //частота по умолчанию (желаемая *10)
   volatile float duty_out;// переменная счёта скважности
  float t_hi, t_low; //переменные счёта длины импульсов 
 uint8_t wave[512]; //массив для DDS синтеза
uint8_t sine_logo[] __FLASH__ ={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
  44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
   46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
    19,17,16,14,13,12,10,9,8,7,6,5,4,3,2,2,1,1,0,0,0,0,0,0,0,1,
     1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};

          void setup() {
          delay(100);// пауза для дисплея
         SPI.setModule(2);// выбор SPI2
        tft.initR(INITR_BLACKTAB);
       tft.setRotation(3);//дисплей горизонтально, контакты слева
      tft.fillScreen(paper);//залить цветом по умолчанию
    tft.setTextWrap(0);//не переносить строки
  Serial.end();// дефолтовый USBCDC не нужен
 nvic_irq_disable_all();//отключить все прерывания 
disableDebugPorts();//отключить режим дебага
systick_disable(); // отключить системный таймер
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0); //включить тактирование tim-2,3,4
 RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);////включить тактирование port-a-b-c,tim1
  AFIO_BASE->MAPR|=(1<<8)|(1<<6); //tim 1 && tim 2 Partial remap
   i2c_master_enable(I2C1, I2C_REMAP); //SDA PB9, SCL PB8
    Wire.begin();
     si5351_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);//если нашёлся si5351 поднимается флажок
// if (si5351_found) { si5351.set_freq(16E6*SI5351_FREQ_MULT, SI5351_CLK1);// фиксированная частота с выхода 1
// si5351.set_freq(20E6*SI5351_FREQ_MULT, SI5351_CLK2); }//фиксированная частота с выхода 2
  pinMode(PB0,PWM); //buzzer
   pinMode(PB1,INPUT_PULLUP);//key encoder
    pinMode(PB3,OUTPUT); //мультиплексор
     pinMode(PB4,OUTPUT); //мультиплексор
      pinMode(PB6,INPUT_PULLUP);//encoder
       pinMode(PB7,INPUT_PULLUP);//encoder
        mytone(1000,50);//сигнал после старта
       //прерывание кнопки энкодера
      attachInterrupt(PB1, key_enc_int, RISING);
     attachInterrupt(PB6, enc_int, CHANGE); attachInterrupt(PB7, enc_int, CHANGE);
if (mode==1){ timer_set(0); }
} //end setup



void loop() {
static int old_mode_loop=-1;
  if (mode!=old_mode_loop) {
   tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
     }//чистить полностью экран только при смене режимов
    comm();//коммутация выходов мультиплексором
     if (mode==8) {mon_out(); freq_meter();  }
   if (mode >2 && mode<8) { mon_out();  dds_set(); } // запуск DDS режимов
 if (mon_flag) {mon_flag=0; mon_out();} //в остальных ситуациях при наличии флага вывода на дисплей 
}
  

void freq_meter(){
/////////////////////счётчик импульсов
pinMode(PA15,INPUT_PULLDOWN); // вход частотометра
 uint32_t imp_long,imp_hi;//переменные измерения длины такта
   __asm volatile( "cpsid i" );
   /// Timer2 счёт младших 16 бит
    TIMER2_BASE->CR1=0;//стоп таймер
     TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0;  TIMER2_BASE->CNT=0; 
      TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0; 
       TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
        TIMER2_BASE->CCMR2=0;
         TIMER2_BASE->CR2=1<<5; //MMS:010 управление подчинённым в режиме "Update" 
          TIMER2_BASE->SMCR= (1<<14);// ECE & TS:000  режим 2 внешнего тактирования & разрешение работы от таймера1
         TIMER2_BASE->ARR=65535; //считать до максимума
        TIMER2_BASE->EGR=1; //перечитать регистры.
       TIMER2_BASE->CR1|=(1<<0);//start timer2
      /// Timer3 счёт старших 16 бит
     TIMER3_BASE->CR1=1<<0;//стоп таймер
    TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0; 
   TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0; 
  TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;  
 TIMER3_BASE->CCMR1=0; 
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);//SMS:111 && TS:001  такт брать от 2-го таймера  
 TIMER3_BASE->ARR=65535; //считать до 
  TIMER3_BASE->EGR=1; //перечитать регистры.
   TIMER3_BASE->CR1|=(1<<0);//start timer3
    /// настройка времени разрешения на таймере1 для таймера2
     TIMER1_BASE->CR1=(1<<3)|(1<<2);//один импульс, без прерываний
      TIMER1_BASE->CNT=0;
       TIMER1_BASE->CR2=(1<<4);  //MMS:001 сигнал разрешения работы другим таймерам
        TIMER1_BASE->CCER=0;// отключить выходы таймера на физ ноги
         TIMER1_BASE->PSC=F_CPU/36000 -1;// 1999; // 72000000/2000= 36000кГц тактовая таймера 
          TIMER1_BASE->ARR=35999;//считать до 36000 (1секунда) 
          TIMER1_BASE->EGR=1; //перечитать регистры.
         TIMER1_BASE->CR1|=(1<<0);
       __asm volatile( "cpsie i" );
      while (TIMER1_BASE->CR1&1) {asm volatile("nop"); if(mon_flag) {return;}  }
     freq=  TIMER3_BASE->CNT<<16  | TIMER2_BASE->CNT ; //частота не удесятерённая
    if (freq>1E5){freq*=10; t_low=0;t_hi=0; duty_out=0; mon_flag=1; return;} //выйти если freq больше 100кГц
   // Перенастройка таймера 2 в режии измерения длительности импульса и скважности для частот менее 100 кГц
 divider=1;                                  
while ((F_CPU/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
 __asm volatile( "cpsid i" );
  TIMER2_BASE->CR1=0;//стоп таймер
   TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0;  TIMER2_BASE->CNT=0; 
    TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0; 
     TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
      TIMER2_BASE->CCMR2=0;
       TIMER2_BASE->CR2=0;
        TIMER2_BASE->PSC= divider-1;
         TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);// TS:101 SMS:100  вход TI1FP1  , Режим сброса
          TIMER2_BASE->CCMR1=(1<<0)|(1<<9);//CC1 input,mapped on TI1, CC2 input,mapped on TI1
         TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);//cc1-Hi,cc2-lo 
        TIMER2_BASE->EGR=1; //перечитать регистры.
   /// настройка таймера1 для счёта  тайм-аута при измерения PWM 
     TIMER1_BASE->CR1=(1<<3);//один импульс, без прерываний
      TIMER1_BASE->CNT=0; TIMER1_BASE->CR2=0;  TIMER1_BASE->CCER=0;
         TIMER1_BASE->PSC=F_CPU/15625 -1; // тактовая таймера 15625 Герц 
          TIMER1_BASE->ARR=31250;//считать до 31250 (2 секунды) 
          TIMER1_BASE->EGR=1; //перечитать регистры.
          timer_attach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT, myint);
         TIMER1_BASE->CR1|=(1<<0);// старт счёта 2х секунд      
      __asm volatile( "cpsie i" );
       TIMER2_BASE->CR1=(1<<0);// старт захвата PWM
     while( (TIMER2_BASE->SR&0x65F)!=0x65F) {
      asm volatile("nop"); if(mon_flag) {timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT); return;} }
    TIMER2_BASE->CR1=0;// стоп таймер
   timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
   imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
  imp_hi=(uint32_t)  ((TIMER2_BASE->CCR2)*divider);
 if (freq <1000){ freq= F_CPU*10 /imp_long ;} //если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты (*10)
 else {freq*=10; } //иначе просто удесятерить результат для корректного вывода информации. 
duty_out=  (float) imp_hi / (imp_long / 100.0) ;
if (duty_out > 100 || duty_out < 0) {duty_out=0;} // на всякий случай ограничение
 t_low= (double)(imp_long-imp_hi) / (F_CPU/1E6) ;
  t_hi=  (double) imp_hi /(F_CPU/1E6);
   mon_flag=1;
   } //END freq meter

// прерывание тайм-аута при отсутствиии сигнала на входе при измерении PWM 
void myint(){ mon_flag=1; t_low=0; t_hi=0; duty_out=0;  freq=0; 
timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
} 

///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[15];
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print("  Clock Gen  ");  break;       
        case 1: tft.print("  PWM Mode   "); break;
          case 2: tft.print("  Duty Mode  "); break;
           case 3: tft.print("  Sinus DDS  "); break;
            case 4: tft.print(" Triangle DDS"); break;
           case 5: tft.print("  Pila1 DDS  "); break;
          case 6: tft.print("  Pila2 DDS  "); break;
         case 7: tft.print("  Meandr DDS "); break;
       case 8: tft.print(" Freq. meter "); break;
       }
 //*****************Вывод второй строчки*****************************
tft.setTextColor(ST7735_WHITE, paper); 
tft.setCursor(0, 19); tft.setTextSize(3);
 if (freq>=1E8) {tft.print("         ");   tft.setTextSize(2);tft.setCursor(0, 21);}    
     if (freq<10) {sprintf(mybuf,"   0,%ld   ", freq );}                //9 -> 0,9
else if (freq<100){sprintf(mybuf,"   %ld,%ld   ", freq/10, freq%10 );}   //99 -> 9,9                
else if (freq<1E3){sprintf(mybuf,"   %ld,%ld  ", freq/10, freq%10 );}    //999 -> 99,9           
else if (freq<1E4){sprintf(mybuf,"  %ld,%ld  ", freq/10, freq%10 );}    //9999 -> 999,9             
else if (freq<1E5){sprintf(mybuf,"  %ld %03ld  ", freq/10000, (freq/10)%1000 );} //99999 -> 9.999               
else if (freq<1E6){sprintf(mybuf,"  %ld %03ld ", freq/10000, (freq/10)%1000 );}  //999999 -> 99.999                       
else if (freq<1E7){sprintf(mybuf," %ld %03ld ", freq/10000, (freq/10)%1000 );} //999999 -> 999.999                       
else if (freq<1E8){sprintf(mybuf,"%ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //9999999 -> 9.999.999                       
else              {sprintf(mybuf,"%3ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //99999999 -> 99.999.999                       
tft.print(mybuf);    //вывод частоты
//Serial.println(mybuf);
  //********************Вывод третьей строчки*****************************
                 tft.setTextSize(3); //крупно
                tft.setCursor(50, 43); //  вперёд, вниз
               tft.setTextColor(ST7735_RED,paper);
               tft.print("Herz");
  //********************* "осциллограммы"******************************
     tft.fillRect(5,90, 100,38,paper);// зачистка пяточка (вправо, вниз, ширина вправо, длина вниз)
     tft.drawRect(0,67, 160,61,ST7735_MAGENTA);//вправо, вниз, ширина вправо, длина вниз
      if (mode==1 ||mode==2 || mode==8){
        tft.drawFastVLine(5, 90, 30, ST7735_CYAN); // восход фронта статическая вер линия
         tft.drawFastHLine(5, 91, (int)duty_out, ST7735_YELLOW);//длина единицы
          tft.drawFastHLine(5, 90, (int)duty_out, ST7735_YELLOW);//паралельная линия для выделения
           tft.drawFastVLine((int)duty_out+5, 91, 30, ST7735_YELLOW);// спад
            tft.drawFastVLine((int)duty_out+4, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
             tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);//спад конец такта статическая вер. линия
              tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
               tft.drawFastHLine((int)duty_out+5, 120, (100-(int)duty_out), ST7735_YELLOW);//линия единицы 2-го такта
                tft.drawFastHLine((int)duty_out+5, 119, (100-(int)duty_out), ST7735_YELLOW);//паралельная линия для выделения
                 }
                 if (mode==3){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+ sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==3)
                       else if (mode==4){// логотип треугольника                         
                      tft.drawLine(5,98,30,73,ST7735_YELLOW);
                     tft.drawLine(30,73,80,123,ST7735_YELLOW);
                    tft.drawLine(80,123,105,98,ST7735_YELLOW); 
                  } //END  mode==4
                         else if (mode==5){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==5)
                            else if (mode==6){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==6)
                                else if (mode==7 || mode==0){ //логотип меандра
                                  tft.drawFastVLine(5,73,25,ST7735_YELLOW);
                                  tft.drawFastHLine(5,73,50,ST7735_YELLOW);
                                  tft.drawFastVLine(55,73,50,ST7735_YELLOW);
                                  tft.drawFastHLine(55,123,50,ST7735_YELLOW);
                                  tft.drawFastVLine(105,98,25,ST7735_YELLOW);
                                }
                   //*********************** характеристики сигнала****************************************
                  tft.setCursor(5, 70); //  вперёд, вниз
                 tft.setTextColor(ST7735_WHITE, paper);
                tft.setTextSize(1);
                 if (mode==1 ||mode==2 || mode==8){
               tft.print("+Width="); if (t_hi<1E3) {tft.print(t_hi); tft.print(" uS  ");} else {tft.print(t_hi/1000); tft.print(" mS  ");}  
              tft.setCursor(5, 80); //  вперёд, вниз
            tft.print("-Width="); if (t_low<1E3) {tft.print(t_low); tft.print(" uS  ");} else {tft.print(t_low/1000); tft.print(" mS  ");}  
           tft.setCursor(114, 70); tft.print("Duty=");
          tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(" %   ");
          } //END if (mode < 2 || mode==8)
         if (mode!=8){ // выводить шаг кроме частотометра
         tft.setCursor(114, 95); tft.print("Step=");
       tft.setCursor(114, 105); 
       switch (encstep) { 
         case 1: tft.print(" 0,1"); break;
         case 10: tft.print("   1"); break;
        case 100: tft.print("  10"); break;
       case 1E3: tft.print(" 100");break;
       case 1E4: tft.print(" 1E3");break;
      case 1E5:  tft.print(" 1E4");break;
      case 1E6:  tft.print(" 1E5");break;
      case 1E7:  tft.print(" 1E6");break;
        }// END switch case        
        } // END if (mode!=8)
//if ( freq < 1)  {tft.fillRect(1,68, 158,59,paper); }
}//END mon_out




//обработчик прерываний энкодера
void enc_int(){   
static char EncPrev=0;      //предыдущее состояние энкодера
 static char EncPrevPrev=0;  //пред-предыдущее состояние энкодера
  char EncCur = 0;
   if(!(  GPIOB_BASE->IDR&64  )){EncCur  = 1;} //опрос фазы 1 энкодера
    if(!(  GPIOB_BASE->IDR&128 )){ EncCur |= 2;} //опрос фазы 2 энкодера
    if(EncCur != EncPrev)             //если состояние изменилось,
    {
    if(EncPrev == 3 &&        //если предыдущее состояние 3
       EncCur != EncPrevPrev )      //и текущее и пред-предыдущее не равны,
    {
      if(EncCur == 2)          //если текущее состояние 2,
        enc_mode(-1);            //шаг вверх
      else                          //иначе
        enc_mode(1);            //шаг вниз
    }
    EncPrevPrev = EncPrev;          //сохранение пред-предыдущего состояния
    EncPrev = EncCur;               //сохранение предыдущего состояния
  }
 }// END VOID


// ФУНКЦИЯ конфигурации режимов 
void enc_mode(int in){
   modebit= digitalRead(PB1); //состояние кнопки PB1. 0-нажата 
   if (!modebit) {// если сейчас идёт переключение режимов (кнопка нажата)
   mytone(880,30); //звук переключения режимов
     mode+=in; 
     if(mode>8){mode=8;}
      if (si5351_found) { if(mode<0){mode=0;}  } else { if(mode<1){mode=1;} }
       if (mode==1 || mode==2 ){timer_set(0);}
        if (mode==0){clock_gen();}
        if (mode==8) {freq=0;}
        if (mode >2 && mode !=8) {if (freq>DDSMAX) {freq=DDSMAX;} }
         mon_flag=1; 
         return; 
          } //сюда попадает при изменении частоты (вращение без нажатия)
         mytone(4400,10); //звук изменения частоты
       switch(mode){ //если сейчас идёт изменение частоты
     case 0:  freq+=(encstep*in); clock_gen(); break;
     case 1:   timer_set(in); break;
     case 2:   duty_in+=in; timer_set(0);  break;  
    case 8:   break; // в частотометре не реагировать на вращение энкодера 
   default: freq+=(encstep*in); if (freq>DDSMAX) {freq=DDSMAX;}  //DDS режимы
  } //end switch case
 if (freq<0){freq=0;}   
mon_flag=1; 
}//end enc_mode


// обработчик кнопки энкодера
void key_enc_int(){
if(!modebit){ // если до этого менялся режим то выдержать паузу (низкий звук в спикер) и выйти
   TIMER2_BASE->SR=0;//доп.защита от дребезга: обнулить флаги таймера2 что бы знать когда генератор закончит пищать
     mytone(30,150); //150ms примерно соответсвует времени отпускания кнопки после вращения
   while( (TIMER2_BASE->SR)==0);//доп.защита от дребезга: подождать пока пропищит
  modebit=1; return;
  }  
    if (mode==2) { mytone(880,30); mode=1; enc_step_control() ; return;} //сменить режим и выйти если были в duty mode
     while((GPIOB_BASE->IDR&2)==0); // после отпускания кнопки если попался LOW -значит это был дребезг, дождёмся HIGH
     TIMER2_BASE->SR=0;//доп.защита от дребезга: обнулить флаги таймера2 что бы знать когда генератор закончит пищать
     mytone(160,100); //выдать звук переключения шага
   while( (TIMER2_BASE->SR)==0);//доп.защита от дребезга: подождать пока пропищит
  encstep*=10; 
 enc_step_control();
}//end 

void enc_step_control(){ //ограничение шага в зависимости от режимов и частот
// для CLOCK режима если  шаг более 1 МГц то шаг сбросить на 0,1 или 1 Герц (зацикливание переключений)
if (mode==0 &&  encstep >1E7) {encstep=1;} // Для CLOCK режима макс шаг 1МГц
if (mode==1 && encstep >1E5) {encstep=1;} //для PWM макс шаг 10 000 Гц
if (mode>2 &&  encstep >1E6) {encstep=1;} //для DDS  макс шаг 100 000 Гц
if (encstep==1 && freq >=10000 ) encstep=10;// менять шаг 0,1 Гц -> 1Гц  на частотах выше 1кГц для всех режимов
mon_flag=1; //флаг вывода на дисплей  
}



////////////////НАСТРОЙКА ТАЙМЕРА-ГЕНЕРАТОРА/////////////////////////////////////////////
void timer_set(int in){ //принимает +1 -1 или 0
int tim_arr; uint32_t imp_long, imp_hi; 
//общие настройки таймера1
GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
 GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
  TIMER1_BASE->CR1=0;
   TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; 
    TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
     //TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
       TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
         if(freq < 84850){ //изменение частоты таймера по заданной частоте
          if (in) {freq+=(encstep*in);}//если передавалось изменение частоты, то рассчитать
           if (freq<1){freq=1;}  if (freq>(F_CPU/2*10)) {freq=F_CPU/2*10;}// ограничение макс. частоты *10
           tim_arr = F_CPU*10/freq; 
          divider=1; while ( (tim_arr/divider) > 65535) {divider++;} 
         TIMER1_BASE->PSC=divider-1;
        TIMER1_BASE->ARR=(tim_arr/divider)-1;
       } //end f (freq < 84850)
     else { // изменение частоты таймера инкрементом регистра ARR
    tim_arr=TIMER1_BASE->ARR; //снять тукущее состояния регистра     
   if (tim_arr<1000 && encstep > 1000) encstep=1000; // уменьшать шаг с ростом частоты
 if (tim_arr<100 && encstep > 100) encstep=100; // уменьшать шаг с ростом частоты
if (tim_arr<10 && encstep > 10) encstep=10;   // уменьшать шаг с ростом частоты    
in*=(encstep/10);
 tim_arr-=in;
  if (tim_arr<1) {tim_arr=1;} if (tim_arr>65535) {tim_arr=65535;}
   TIMER1_BASE->ARR=tim_arr;
    } // END  изменение частоты таймера инкрементом регистра ARR
     // установка заданного DUTY
      if(duty_in>99){duty_in=99;} if(duty_in<1){duty_in=1;} 
       if (mode==1 && TIMER1_BASE->ARR<100){ TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; duty_in=50;} //сбрасывать duty на 50% на высоких частотах 
        else{TIMER1_BASE->CCR1= (float) (TIMER1_BASE->ARR+1)* duty_in/100.0 ;} //или рассчитать 
         freq= F_CPU*10 /((TIMER1_BASE->ARR+1)*divider);// рассчёт фактической частоты
          duty_out=  (float) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ; //расчёт фактического duty
          duty_out= floorf(duty_out); //округление
         imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider); //длина периода в тактах
       imp_hi=(uint32_t)  ((TIMER1_BASE->CCR1)*divider); // длина импульса в тактах
     t_low= (imp_long-imp_hi) /(F_CPU/1E6) ; //время LOW
   t_hi=  imp_hi /(F_CPU/1E6); //время HI
 TIMER1_BASE->CR1=1;
mon_flag=1;
}//end timer_set
 

// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
void dds_set(){
 static byte oldmode=255;
   TIMER1_BASE->CCER=0; //timer output pins disable
    #ifdef DACINT
     rcc_clk_enable(RCC_DAC);
      rcc_reset_dev(RCC_DAC);
     gpio_set_mode(GPIOA, 4, GPIO_MODE_ANALOG);
    DAC->regs->CR = DAC_CR_BOFF1 | DAC_CR_EN1 ;
   #define DDS_OUT DAC->regs->DHR8R1
  #else 
#define DDS_OUT GPIOA_BASE->ODR
GPIOA_BASE->CRL = 0x33333333;// pa0-pa7  выход
  #endif 
     if (oldmode !=mode) {
      if (mode==3) {for(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*float(n)/512)+1)/2 ;}}// синус
       else if (mode==4){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=n;} else {wave[n]=(511-n);}}}//треугол
      else if (mode==5){ for(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}                               //пила1
     else if (mode==6){ for(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}                            //пила2
    else if (mode==7){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=0;} else {wave[n]=255;}}}    //меандр
  oldmode=mode; } 
uint32_t dds_shag= (double)freq/10 * ((F_CPU==72E6)? dds_mpl_72 : dds_mpl_128) ;//  шаг= частота*коэффициент
asm volatile (
"mov   R9, %[port];"  "\n\t" // записать в r9 адресс порта "A"-ODR 
 "mov   R8, %[wave];"       "\n\t" //адресс массива положить в r8  
 "mov   R7, %[shag];"   "\n\t" // значение шага в r7
  "dds_loop:"                  "\n\t"
   "add R6, r7;"               "\n\t"  //(1)добавить к аккумулятору шаг
    "lsrs r2, r6, #23;"         "\n\t"  //(1) положить в R2 сдвинутый на 23 бита аккумулятор
   "ldrb r2, [r8, r2];"        "\n\t"  //(2)загрузить в R2 выбранный байт из массива
   "strb  r2, [r9];"           "\n\t"  //(2) запиcать этот байт в PORTA-ODR
  "ldr  R2, [%[flag]];"       "\n\t"  //(2) подгрузить в  R2 флаг 
 "cmp r2, 1;"                "\n\t"  //(1) сравнить 
"bne dds_loop;"              "\n\t"  //(1) перейти в цикл
: : [wave]"r" (&wave),[shag]"r"(dds_shag),[port]"r"(&DDS_OUT),[flag]"r"(&mon_flag)
: "r9","r8","r7","r6","r2" 
);
  GPIOA_BASE->CRL=0x44444444;// все пины в Z
}//END DDS set()
//


  void mytone(int frq, int ms ){
 uint16_t psc=1; uint32_t tim_arr;
 // настройка генератора звука на таймере3
  tim_arr = (F_CPU/2)/frq;
  while ( (tim_arr/psc) > 65535) {psc++;} 
  __asm volatile( "cpsid i" ); 
   TIMER2_BASE->SMCR=0;
    TIMER3_BASE->CCR3=0; //обнулить регистр соответсвующий используемому выходу
     TIMER3_BASE->PSC=psc-1;
      TIMER3_BASE->ARR=(tim_arr/psc)-1;
       TIMER3_BASE->CCMR2=(1<<5)|(1<<4);// OC3M:011
        TIMER3_BASE->CCER=1<<8;//cc3e  подключить аппаратную ногу
         TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);//SMS:101 && TS:001  строб от 2-го таймера  
          TIMER3_BASE->EGR=1; //перечитать регистры.
           TIMER3_BASE->CR1=1;
           /// настройка выдержки времени на таймере2
          psc=1;
        tim_arr = (F_CPU/1E3) * ms;
       while ( (tim_arr/psc) > 65536) {psc++;} 
      TIMER2_BASE->CCMR2=0;
     TIMER2_BASE->CR2=0;
    TIMER2_BASE->CR1=(1<<3)|(1<<2);//один импульс, без прерываний
   TIMER2_BASE->CNT=0;
  TIMER2_BASE->CR2=(1<<4);  //MMS:001 сигнал разрешения работы другим таймерам
 TIMER2_BASE->PSC=psc-1;
TIMER2_BASE->ARR=(tim_arr/psc)-1;
TIMER2_BASE->EGR=1; //перечитать регистры.
TIMER2_BASE->CR1|=(1<<0);
  __asm volatile( "cpsie i" );
}

void clock_gen(){ //функция работа с синтезатором si5351
if (!si5351_found) {return;} // на всякий случай 
if (freq <4E4){freq=4E4;} if (freq>200E7){ freq=200E7;} //допустимые рамки частот
si5351.set_freq((freq/10)* SI5351_FREQ_MULT, SI5351_CLK0);
mon_flag=1;
}

void comm(){ //коммутация выходов через мультиплексор
if (mode==1 || mode==2 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,LOW);}
  else  if (mode>2 && mode<8 ) {digitalWrite(PB3,HIGH); digitalWrite(PB4,LOW);}
  else if (mode==0 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,HIGH);}
 else if (mode==8) {digitalWrite(PB3,HIGH); digitalWrite(PB4,HIGH);}
}

Схема (кликабельно) та-же, только нет конденсаторов и подтяжек, всё подтянуто программно. Энкодер обязательно типа EC-11, буззер обязательно пассивный. Всё остальное как прежде. Мультиплексор ADG704 и тактовый генератор Si5351 устанавливаются опционально по желанию. Освободился 4-й таймер, можно использовать для чего-то ещё. Появилась возможность переносить ноги энкодера на другие пины.

Просьба не нажимать кнопку "цитировать" что-бы осталась возможность редактировать этот пост.

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

To dimax: Если будет желание ещё что-нибудь подправить, есть предложение внести некоторые "косметические" изменения:

Ввести шаг 10Е7 - по одному мегагерцу щёлкать до 200 долго, проще по 10.

Перенести кнопку на порт РВ5 - этот пин рядом с пинами контактов энкодера, а не по диагонали, как РВ1. Печатку разводить удобнее.

Может, конечно,  я со своей колокольни мыслю, но себе сделал, глядишь и ещё кому пригодится.

На самом деле, изменений побольше, но это так, чтобы глазу приятнее было - запятые на точки заменил, шрифт для всех частот выставил 2, убрал "голубенький" фронт с прямоугольника ну и т.д. В посте #62 это видно.

Вот как-то так.

Частота 9 МГц

 

Ну а вот генератор на 9833, который без резисторов-конденсаторов для энкодера.

Дисплейчик здесь хорош - полноцветный 160х80 IPS, но размер 0.96

73!

 

apeks1
apeks1 аватар
Offline
Зарегистрирован: 19.05.2016

dimax

если можно прикрепите сразу .bin  версии 3.3

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Не факт, что будет работать. Читали, наверное, про чудеса с прошивкой.

http://wdfiles.ru/9V20

apeks1
apeks1 аватар
Offline
Зарегистрирован: 19.05.2016

MAG-N

спасибо ! пока до прошивки далеко, хотя наверно соберу железный com-uart (почитал по посылкам с китая что ждать мне еще долго и не факт что приедет и как всегда законотворцы принимают законы к которым почта постоянно не готова)

для проверки подвешу дисплей к отладочной и энкодер без ЦАПа

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

На муське был обзор этого DDS-085.  Мне трудно понять ход мыслей разработчиков этого девайса. Вместо того, что бы использовать специализированный чип для генерации DDS они сделали софт-DDS на отдельной меге48, соответссно сигналы получились крайне низкого качества. В общем за эти же деньги можно найти генератор с существенно более лучшими характеристиками. Тот-же филтеч например.

miklin
Offline
Зарегистрирован: 08.06.2018

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

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

miklin, а зачем нужен усилитель? Мне обычно требовалось только уменьшать уровень. Тут проблема  в том, что усилить сигнал, не испортив его, технически достаточно сложно. Что явно выходит за рамки основной концепции данной конструкции "генератор из говна и палок"  :)

vk007
Онлайн
Зарегистрирован: 16.06.2015

dimax, у меня к Вам просьба-предложение. Тема с Вашими генераторами, как видим, очень популярная. И попурна она весьма справедливо, конструкции получились удачные (от меня тоже большое Вам спасибо за них), и они активно обсуждаются в теме. Но, к сожалению, большинство сообщений удаляется. Это правильно, нечего замусоривать тему, но уж очень радикально.

Так вот насчет просьбы. Не могли бы Вы открыть отдельную тему (тут же в "Проектах" или в "Общем"), скажем, "Обсуждение генератора с регулируемоей частотой на ардуино от dimax", в первом сообщении той темы так и написать: "Здесь ведется обсуждение, задаются вопросы по теме Генератор с регулируемоей частотой на ардуино". А в этой теме написать в последнем сообщении что-типа "Обсуждение и вопросы по сабжу - в теме Обсуждение генератора с регулируемоей частотой на ардуино от dimax (со ссылкой)".

Таким образом и эта тема не будет замусориваться, и общение сохранится. Если что-то полезное и достойное Вашего внимания появится в той теме, то нужное сообщение можно будет скопировать и сюда.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

vk007, такую тему недавно на радиокоте создали, можно там пообсуждать.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Версия 3.4, изменения:
-добавлен шаг 10MHz для режима ClockGen.
-пин дисплея CS посажен на землю, вывод PB12 освободился.
-кнопку  энкодера перекинул с PB1 на  PB5.
-Добавил режим вольтметра (среднеквадратичные вычисления).

В режиме вольтметра дополнительно отображается  внутреннее напряжение питания МК, так что если сделать автономное питание - например запитать напрямую от аккумулятора lifepo4, то можно будет следить за уровнем напряжения АКБ. Так же отображается внешнее напряжение, измеренное с пина PB1(ADC9). Референс берётся от предыдущего измерения Vcc, таким образом при любом допустимом напряжении Vcc измеренные значения должны отображаться верно. Так же есть два настраиваемых предела напряжения -низкий и высокий. При превышении допустимого предела раздатся писк соответственно низкого или высокого тона. Удобно, если нужно следить за каким-то процессом, например за разрядом/зарядом аккумулятора. Как только напряжение выйдет за рамки прибор запищит, чем привлечёт внимание. При номиналах делителя как на схеме максимальное напряжение 20 вольт.

Управление вольтметром- при вращении ручки энкодера изменяется активный предел напряжения. Выбор шага так-же работает обычным способом. Активный предел выбирается нажатием кнопки и поворотом ручки энкодера вправо. Активный предел отображается жёлтым цветом.

Все рисунки кликабельны.

 

Обновлённая схема:

Скетч:

 /*Генератор с регулируемой частотой v3.4 (C)Dimax */
#define pwm2_polar 0 //полярность выхода PWM2
#define paper        0x000000 // цвет фона экрана
#define DDSMAX 1E7 //максимальная частота для генератора DDS (удесятерённая)
#define dds_mpl_72 835.05327478167234049174700635502 //множитель DDS для частоты F_CPU 72МГц
//для пересчёта множителя необходимо: частоту на экране прибора * текущий множитель и разделить на фактически измеренную частоту
#define dds_mpl_128 469.7191655978919104715512499704  //множитель DDS для  частоты F_CPU 128Mhz
#define VrefINT 1209  //внутренее опорное напряжение в милливольтах 
 #define Mn 6.06  //множитель для пересчёта напряжения с учётом резисторного делителя.
 #include <Adafruit_GFX_AS.h>    // Core graphics library
   #include <Adafruit_ST7735.h> // Hardware-specific library
    #include <SPI.h> 
    #include "si5351.h"
     #include <Wire.h> 
      #include <libmaple/dac.h>
       Adafruit_ST7735 tft = Adafruit_ST7735(-1, PB11,PB10); //PB12 освобождён, вывод CS дисплея запаять на землю.
        Si5351 si5351;
         boolean si5351_found=0; boolean modevolt =0;
      volatile int enc_tic=0, duty_in=50, mon_flag, divider, modebit=1;
      volatile int mode=3;// 0- GEN_si5351, 1-PWM, 2-Duty, 3..7 DDS, 8-Freqmeter, 9-VoltMeter 
      volatile int encstep=10; //шаг изменения частоты по умолчанию (желаемый *10)
     volatile int32_t freq=10000; //частота по умолчанию (желаемая *10)
   volatile float duty_out;// переменная счёта скважности
  float t_hi, t_low; //переменные счёта длины импульсов 
  uint32_t Vcc; //переменная внутреннего измерения напряжения питания МК (милливольты)
 int Vin_low=0, Vin_hi=15000; //переменные пределов для вольтмера (милливольты)
 uint8_t wave[512]; //массив для DDS синтеза
uint8_t sine_logo[] __FLASH__ ={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
  44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
   46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
    19,17,16,14,13,12,10,9,8,7,6,5,4,3,2,2,1,1,0,0,0,0,0,0,0,1,
     1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
          void setup() {
          delay(100);// пауза для дисплея
         SPI.setModule(2);// выбор SPI2
        tft.initR(INITR_BLACKTAB);
       tft.setRotation(3);//дисплей горизонтально, контакты слева
      tft.fillScreen(paper);//залить цветом по умолчанию
    tft.setTextWrap(0);//не переносить строки
  Serial.end();// дефолтовый USBCDC не нужен
 nvic_irq_disable_all();//отключить все прерывания 
disableDebugPorts();//отключить режим дебага
systick_disable(); // отключить системный таймер
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0); //включить тактирование tim-2,3,4
 RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);////включить тактирование port-a-b-c,tim1
  AFIO_BASE->MAPR|=(1<<8)|(1<<6); //tim 1 && tim 2 Partial remap
   i2c_master_enable(I2C1, I2C_REMAP); //SDA PB9, SCL PB8
    Wire.begin();
     si5351_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);//если нашёлся si5351 поднимается флажок
 if (si5351_found) { si5351.set_freq(16E6*SI5351_FREQ_MULT, SI5351_CLK1);// фиксированная частота с выхода 1
 si5351.set_freq(20E6*SI5351_FREQ_MULT, SI5351_CLK2); }//фиксированная частота с выхода 2
  pinMode(PB0,PWM); //buzzer
  pinMode (PB1,INPUT_ANALOG); // вход АЦП
   pinMode(PB5,INPUT_PULLUP);//key encoder
    pinMode(PB3,OUTPUT); //мультиплексор
     pinMode(PB4,OUTPUT); //мультиплексор
      pinMode(PB6,INPUT_PULLUP);//encoder
       pinMode(PB7,INPUT_PULLUP);//encoder
        mytone(1000,50);//сигнал после старта
      attachInterrupt(PB5, key_enc_int, RISING);//прерывание кнопки энкодера
     attachInterrupt(PB6, enc_int, CHANGE); attachInterrupt(PB7, enc_int, CHANGE);//прерывания энкодера
   adc_enable_single_swstart(ADC1);//запуск АЦП
   ADC1->regs->CR2 |= ADC_CR2_TSVREFE; // Enable VREFINT conversion
   ADC1->regs->SMPR1=3<<21;  //ADC (vrefint) Sample time = 28.5 cycles 
 ADC1->regs->SMPR2=3<<27; //ADC (channel 9) Sample time = 28.5 cycles 
if (mode==1){ timer_set(0); }
} //end setup


void loop() {
static int old_mode_loop=-1;
  if (mode!=old_mode_loop) {
   tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
     }//чистить полностью экран только при смене режимов
    comm();//коммутация выходов мультиплексором
     if (mode==8) {mon_out(); freq_meter();  }
   if (mode==9) {volt_meter(); mon_out(); }
   if (mode >2 && mode<8) { mon_out();  dds_set(); } // запуск DDS режимов
 if (mon_flag) {mon_flag=0; mon_out();} //в остальных ситуациях при наличии флага вывода на дисплей 
}
  

void freq_meter(){
/////////////////////счётчик импульсов
pinMode(PA15,INPUT_PULLDOWN); // вход частотометра
 uint32_t imp_long,imp_hi;//переменные измерения длины такта
   __asm volatile( "cpsid i" );
   /// Timer2 счёт младших 16 бит
    TIMER2_BASE->CR1=0;//стоп таймер
     TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0;  TIMER2_BASE->CNT=0; 
      TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0; 
       TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
        TIMER2_BASE->CCMR2=0;
         TIMER2_BASE->CR2=1<<5; //MMS:010 управление подчинённым в режиме "Update" 
          TIMER2_BASE->SMCR= (1<<14);// ECE & TS:000  режим 2 внешнего тактирования & разрешение работы от таймера1
         TIMER2_BASE->ARR=65535; //считать до максимума
        TIMER2_BASE->EGR=1; //перечитать регистры.
       TIMER2_BASE->CR1|=(1<<0);//start timer2
      /// Timer3 счёт старших 16 бит
     TIMER3_BASE->CR1=1<<0;//стоп таймер
    TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0; 
   TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0; 
  TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;  
 TIMER3_BASE->CCMR1=0; 
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);//SMS:111 && TS:001  такт брать от 2-го таймера  
 TIMER3_BASE->ARR=65535; //считать до 
  TIMER3_BASE->EGR=1; //перечитать регистры.
   TIMER3_BASE->CR1|=(1<<0);//start timer3
    /// настройка времени разрешения на таймере1 для таймера2
     TIMER1_BASE->CR1=(1<<3)|(1<<2);//один импульс, без прерываний
      TIMER1_BASE->CNT=0;
       TIMER1_BASE->CR2=(1<<4);  //MMS:001 сигнал разрешения работы другим таймерам
        TIMER1_BASE->CCER=0;// отключить выходы таймера на физ ноги
         TIMER1_BASE->PSC=F_CPU/36000 -1;// 1999; // 72000000/2000= 36000кГц тактовая таймера 
          TIMER1_BASE->ARR=35999;//считать до 36000 (1секунда) 
          TIMER1_BASE->EGR=1; //перечитать регистры.
         TIMER1_BASE->CR1|=(1<<0);
       __asm volatile( "cpsie i" );
      while (TIMER1_BASE->CR1&1) {asm volatile("nop"); if(mon_flag) {return;}  }
     freq=  TIMER3_BASE->CNT<<16  | TIMER2_BASE->CNT ; //частота не удесятерённая
    if (freq>1E5){freq*=10; t_low=0;t_hi=0; duty_out=0; mon_flag=1; return;} //выйти если freq больше 100кГц
   // Перенастройка таймера 2 в режии измерения длительности импульса и скважности для частот менее 100 кГц
 divider=1;                                  
while ((F_CPU/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
 __asm volatile( "cpsid i" );
  TIMER2_BASE->CR1=0;//стоп таймер
   TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0;  TIMER2_BASE->CNT=0; 
    TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0; 
     TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
      TIMER2_BASE->CCMR2=0;
       TIMER2_BASE->CR2=0;
        TIMER2_BASE->PSC= divider-1;
         TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);// TS:101 SMS:100  вход TI1FP1  , Режим сброса
          TIMER2_BASE->CCMR1=(1<<0)|(1<<9);//CC1 input,mapped on TI1, CC2 input,mapped on TI1
         TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);//cc1-Hi,cc2-lo 
        TIMER2_BASE->EGR=1; //перечитать регистры.
   /// настройка таймера1 для счёта  тайм-аута при измерения PWM 
     TIMER1_BASE->CR1=(1<<3);//один импульс, без прерываний
      TIMER1_BASE->CNT=0; TIMER1_BASE->CR2=0;  TIMER1_BASE->CCER=0;
         TIMER1_BASE->PSC=F_CPU/15625 -1; // тактовая таймера 15625 Герц 
          TIMER1_BASE->ARR=31250;//считать до 31250 (2 секунды) 
          TIMER1_BASE->EGR=1; //перечитать регистры.
          timer_attach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT, myint);
         TIMER1_BASE->CR1|=(1<<0);// старт счёта 2х секунд      
      __asm volatile( "cpsie i" );
       TIMER2_BASE->CR1=(1<<0);// старт захвата PWM
     while( (TIMER2_BASE->SR&0x65F)!=0x65F) {
      asm volatile("nop"); if(mon_flag) {timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT); return;} }
    TIMER2_BASE->CR1=0;// стоп таймер
   timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
   imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
  imp_hi=(uint32_t)  ((TIMER2_BASE->CCR2)*divider);
 if (freq <1000){ freq= F_CPU*10 /imp_long ;} //если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты (*10)
 else {freq*=10; } //иначе просто удесятерить результат для корректного вывода информации. 
duty_out=  (float) imp_hi / (imp_long / 100.0) ;
if (duty_out > 100 || duty_out < 0) {duty_out=0;} // на всякий случай ограничение
 t_low= (double)(imp_long-imp_hi) / (F_CPU/1E6) ;
  t_hi=  (double) imp_hi /(F_CPU/1E6);
   mon_flag=1;
   } //END freq meter

// прерывание тайм-аута при отсутствиии сигнала на входе при измерении PWM 
void myint(){ mon_flag=1; t_low=0; t_hi=0; duty_out=0;  freq=0; 
timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
} 

///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[15];
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print("  Clock Gen  ");  break;       
        case 1: tft.print("  PWM Mode   "); break;
          case 2: tft.print("  Duty Mode  "); break;
           case 3: tft.print("  Sinus DDS  "); break;
            case 4: tft.print(" Triangle DDS"); break;
           case 5: tft.print("  Pila1 DDS  "); break;
          case 6: tft.print("  Pila2 DDS  "); break;
         case 7: tft.print("  Meandr DDS "); break;
       case 8: tft.print(" Freq. meter "); break;
      case 9: tft.print(" Volt. meter "); break;
       }
 //*****************Вывод второй строчки*****************************
tft.setTextColor(ST7735_WHITE, paper); 
tft.setCursor(0, 19); tft.setTextSize(3);
 if (freq>=1E8) {tft.print("         ");   tft.setTextSize(2);tft.setCursor(0, 21);}    
     if (freq<10) {sprintf(mybuf,"   0,%ld   ", freq );}                //9 -> 0,9
else if (freq<100){sprintf(mybuf,"   %ld,%ld   ", freq/10, freq%10 );}   //99 -> 9,9                
else if (freq<1E3){sprintf(mybuf,"   %ld,%ld  ", freq/10, freq%10 );}    //999 -> 99,9           
else if (freq<1E4){sprintf(mybuf,"  %ld,%ld  ", freq/10, freq%10 );}    //9999 -> 999,9             
else if (freq<1E5){sprintf(mybuf,"  %ld %03ld  ", freq/10000, (freq/10)%1000 );} //99999 -> 9.999               
else if (freq<1E6){sprintf(mybuf,"  %ld %03ld ", freq/10000, (freq/10)%1000 );}  //999999 -> 99.999                       
else if (freq<1E7){sprintf(mybuf," %ld %03ld ", freq/10000, (freq/10)%1000 );} //999999 -> 999.999                       
else if (freq<1E8){sprintf(mybuf,"%ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //9999999 -> 9.999.999                       
else              {sprintf(mybuf,"%3ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //99999999 -> 99.999.999                       
tft.print(mybuf);    //вывод частоты
  //********************Вывод третьей строчки*****************************
                 tft.setTextSize(3); //крупно
                tft.setCursor(50, 43); //  вперёд, вниз
               tft.setTextColor(ST7735_RED,paper);
             if (mode <9)  tft.print("Herz"); else  tft.print(" mV"); 
  //********************* "осциллограммы"******************************
     if (mode!=9){ tft.fillRect(5,90, 100,38,paper); }// зачистка пяточка (вправо, вниз, ширина вправо, длина вниз)
     tft.drawRect(0,67, 160,61,ST7735_MAGENTA);//рамка: вправо, вниз, ширина вправо, длина вниз
      if (mode==1 ||mode==2 || mode==8){
        tft.drawFastVLine(5, 90, 30, ST7735_CYAN); // восход фронта статическая вер линия
         tft.drawFastHLine(5, 91, (int)duty_out, ST7735_YELLOW);//длина единицы
          tft.drawFastHLine(5, 90, (int)duty_out, ST7735_YELLOW);//паралельная линия для выделения
           tft.drawFastVLine((int)duty_out+5, 91, 30, ST7735_YELLOW);// спад
            tft.drawFastVLine((int)duty_out+4, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
             tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);//спад конец такта статическая вер. линия
              tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
               tft.drawFastHLine((int)duty_out+5, 120, (100-(int)duty_out), ST7735_YELLOW);//линия единицы 2-го такта
                tft.drawFastHLine((int)duty_out+5, 119, (100-(int)duty_out), ST7735_YELLOW);//паралельная линия для выделения
                 }
                 if (mode==3){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+ sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==3)
                       else if (mode==4){// логотип треугольника                         
                      tft.drawLine(5,98,30,73,ST7735_YELLOW);
                     tft.drawLine(30,73,80,123,ST7735_YELLOW);
                    tft.drawLine(80,123,105,98,ST7735_YELLOW); 
                  } //END  mode==4
                         else if (mode==5){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==5)
                            else if (mode==6){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==6)
                                else if (mode==7 || mode==0){ //логотип меандра
                                  tft.drawFastVLine(5,73,25,ST7735_YELLOW);
                                  tft.drawFastHLine(5,73,50,ST7735_YELLOW);
                                  tft.drawFastVLine(55,73,50,ST7735_YELLOW);
                                  tft.drawFastHLine(55,123,50,ST7735_YELLOW);
                                  tft.drawFastVLine(105,98,25,ST7735_YELLOW);
                                }
                   //*********************** характеристики сигнала****************************************
                  tft.setCursor(5, 70); //  вперёд, вниз
                 tft.setTextColor(ST7735_WHITE, paper);
                tft.setTextSize(1);
                 if (mode==1 ||mode==2 || mode==8){
               tft.print("+Width="); if (t_hi<1E3) {tft.print(t_hi); tft.print(" uS  ");} else {tft.print(t_hi/1000); tft.print(" mS  ");}  
              tft.setCursor(5, 80); //  вперёд, вниз
            tft.print("-Width="); if (t_low<1E3) {tft.print(t_low); tft.print(" uS  ");} else {tft.print(t_low/1000); tft.print(" mS  ");}  
           tft.setCursor(114, 70); tft.print("Duty=");
          tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(" %   ");
          } //END if (mode < 2 || mode==8)
   if (mode==9) {
    tft.print("Vcc=");tft.print(Vcc); tft.print(" mV   ");          }
       
         if (mode!=8){ // выводить шаг всегда, кроме частотометра
         if (mode!=9) { tft.setCursor(114, 95);}
         tft.print("Step=");
     if (mode!=9) { tft.setCursor(114, 105);} 
       switch (encstep) { 
         case 1: tft.print(" 0,1"); break;
         case 10: tft.print("   1"); break;
        case 100: tft.print("  10"); break;
       case 1E3: tft.print(" 100");break;
       case 1E4: tft.print(" 1E3");break;
      case 1E5:  tft.print(" 1E4");break;
      case 1E6:  tft.print(" 1E5");break;
      case 1E7:  tft.print(" 1E6");break;
      case 1E8:  tft.print(" 1E7");break;

        }// END switch case        
      } // END if (mode!=8)
/// вывод пределов для вольтметра
if (mode==9) {
 tft.setCursor(5, 80); //  вперёд, вниз
    tft.setTextSize(2); 
     tft.setTextColor(modevolt? ST7735_YELLOW : ST7735_WHITE , paper);//выбрать жёлтый цвет если активен
      sprintf(mybuf,"Low_mv=%5d", Vin_low);//выводить 5 символов
      tft.print(mybuf);    //вывод нижнего предела
      tft.setTextColor(modevolt? ST7735_WHITE : ST7735_YELLOW , paper);
    tft.setCursor(5, 100); //  вперёд, вниз
  sprintf(mybuf," Hi_mv=%5d", Vin_hi);
tft.print(mybuf); //вывод верхнего предела 
 }
}//END mon_out




//обработчик прерываний энкодера
void enc_int(){   
static char EncPrev=0;      //предыдущее состояние энкодера
 static char EncPrevPrev=0;  //пред-предыдущее состояние энкодера
  char EncCur = 0;
   if(!(  GPIOB_BASE->IDR&64  )){EncCur  = 1;} //опрос фазы 1 энкодера
    if(!(  GPIOB_BASE->IDR&128 )){ EncCur |= 2;} //опрос фазы 2 энкодера
    if(EncCur != EncPrev)             //если состояние изменилось,
    {
    if(EncPrev == 3 &&        //если предыдущее состояние 3
       EncCur != EncPrevPrev )      //и текущее и пред-предыдущее не равны,
    {
      if(EncCur == 2)          //если текущее состояние 2,
        enc_mode(-1);            //шаг вверх
      else                          //иначе
        enc_mode(1);            //шаг вниз
    }
    EncPrevPrev = EncPrev;          //сохранение пред-предыдущего состояния
    EncPrev = EncCur;               //сохранение предыдущего состояния
  }
 }// END VOID


// ФУНКЦИЯ конфигурации режимов 
void enc_mode(int in){
   modebit= digitalRead(PB5); //состояние кнопки PB5. 0-нажата 
   if (!modebit) {// если сейчас идёт переключение режимов (кнопка нажата)
   mytone(880,30); //звук переключения режимов
     mode+=in; 
     if(mode>9){mode=9; modevolt=!modevolt; }
      if (si5351_found) { if(mode<0){mode=0;}  } else { if(mode<1){mode=1;} }
       if (mode==1 || mode==2 ){timer_set(0);}
        if (mode==0){clock_gen();}
        if (mode==8) {freq=0;}
        if (mode >2 && mode !=8) {if (freq>DDSMAX) {freq=DDSMAX;} }
         mon_flag=1; enc_step_control();
         return; 
          } //сюда попадает при изменении частоты (вращение без нажатия)
         mytone(4400,10); //звук изменения частоты
       switch(mode){ //если сейчас идёт изменение частоты
     case 0:  freq+=(encstep*in); clock_gen(); break;
     case 1:   timer_set(in); break;
     case 2:   duty_in+=in; timer_set(0);  break;  
    case 8:   break; // в частотометре не реагировать на вращение энкодера 
   case 9:   modevolt?  Vin_low+=(encstep/10*in) : Vin_hi+=(encstep/10*in); //регулировка пределов вольтметра
   if (Vin_low<0||Vin_low>99999 ){Vin_low=0;} if (Vin_hi<0||Vin_hi >99999){Vin_hi=0;} break; //ограничения не менее ноля и не более 4х символов 
  default: freq+=(encstep*in); if (freq>DDSMAX) {freq=DDSMAX;}  //DDS режимы
 } //end switch case
 if (freq<0){freq=0;}   
mon_flag=1; 
}//end enc_mode


// обработчик кнопки энкодера
void key_enc_int(){

if(!modebit){ // если до этого менялся режим то выдержать паузу (низкий звук в спикер) и выйти
     mytone(30,150); //150ms примерно соответсвует времени отпускания кнопки после вращения
   while( (TIMER2_BASE->SR)==0);//доп.защита от дребезга: подождать пока пропищит
  modebit=1; return;
  }  
    if (mode==2) { mytone(880,30); mode=1; enc_step_control() ; return;} //сменить режим и выйти если были в duty mode
     while( digitalRead(PB5)==0 ); // после отпускания кнопки если попался LOW -значит это был дребезг, дождёмся HIGH
     mytone(160,100); //выдать звук переключения шага
   while( (TIMER2_BASE->SR)==0);//доп.защита от дребезга: подождать пока пропищит
  encstep*=10; 
 enc_step_control();
}//end 

void enc_step_control(){ //ограничение шага в зависимости от режимов и частот
// для CLOCK режима если  шаг более 1 МГц то шаг сбросить на 0,1 или 1 Герц (зацикливание переключений)
if (mode==0 &&  encstep >1E8) {encstep=1;} // Для CLOCK режима макс шаг 10МГц
if (mode==1 && encstep >1E5) {encstep=1;} //для PWM макс шаг 10 000 Гц
if (mode>2 && mode<8 && encstep >1E6) {encstep=1;} //для DDS  макс шаг 100 000 Гц
if (encstep==1 && freq >=10000 ) encstep=10;// менять шаг 0,1 Гц -> 1Гц  на частотах выше 1кГц для всех режимов
if (mode==9 && (encstep >1E4 || encstep ==1)  ) {encstep=10;} //для вольтметра макс 1000 мв
mon_flag=1; //флаг вывода на дисплей  
}



////////////////НАСТРОЙКА ТАЙМЕРА-ГЕНЕРАТОРА/////////////////////////////////////////////
void timer_set(int in){ //принимает +1 -1 или 0
int tim_arr; uint32_t imp_long, imp_hi; 
//общие настройки таймера1
GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
 GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
  TIMER1_BASE->CR1=0;
   TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; 
    TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
     //TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
       TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
         if(freq < 84850){ //изменение частоты таймера по заданной частоте
          if (in) {freq+=(encstep*in);}//если передавалось изменение частоты, то рассчитать
           if (freq<1){freq=1;}  if (freq>(F_CPU/2*10)) {freq=F_CPU/2*10;}// ограничение макс. частоты *10
           tim_arr = F_CPU*10/freq; 
          divider=1; while ( (tim_arr/divider) > 65535) {divider++;} 
         TIMER1_BASE->PSC=divider-1;
        TIMER1_BASE->ARR=(tim_arr/divider)-1;
       } //end f (freq < 84850)
     else { // изменение частоты таймера инкрементом регистра ARR
    tim_arr=TIMER1_BASE->ARR; //снять тукущее состояния регистра     
   if (tim_arr<1000 && encstep > 1000) encstep=1000; // уменьшать шаг с ростом частоты
 if (tim_arr<100 && encstep > 100) encstep=100; // уменьшать шаг с ростом частоты
if (tim_arr<10 && encstep > 10) encstep=10;   // уменьшать шаг с ростом частоты    
in*=(encstep/10);
 tim_arr-=in;
  if (tim_arr<1) {tim_arr=1;} if (tim_arr>65535) {tim_arr=65535;}
   TIMER1_BASE->ARR=tim_arr;
    } // END  изменение частоты таймера инкрементом регистра ARR
     // установка заданного DUTY
      if(duty_in>99){duty_in=99;} if(duty_in<1){duty_in=1;} 
       if (mode==1 && TIMER1_BASE->ARR<100){ TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; duty_in=50;} //сбрасывать duty на 50% на высоких частотах 
        else{TIMER1_BASE->CCR1= (float) (TIMER1_BASE->ARR+1)* duty_in/100.0 ;} //или рассчитать 
         freq= F_CPU*10 /((TIMER1_BASE->ARR+1)*divider);// рассчёт фактической частоты
          duty_out=  (float) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ; //расчёт фактического duty
          duty_out= floorf(duty_out); //округление
         imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider); //длина периода в тактах
       imp_hi=(uint32_t)  ((TIMER1_BASE->CCR1)*divider); // длина импульса в тактах
     t_low= (imp_long-imp_hi) /(F_CPU/1E6) ; //время LOW
   t_hi=  imp_hi /(F_CPU/1E6); //время HI
 TIMER1_BASE->CR1=1;
mon_flag=1;
}//end timer_set
 

// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
void dds_set(){
 static byte oldmode=255;
   TIMER1_BASE->CCER=0; //timer output pins disable
    #if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
     rcc_clk_enable(RCC_DAC);
      rcc_reset_dev(RCC_DAC);
     gpio_set_mode(GPIOA, 4, GPIO_MODE_ANALOG);
    DAC->regs->CR = DAC_CR_BOFF1 | DAC_CR_EN1 ;
   #define DDS_OUT DAC->regs->DHR8R1
  #else 
#define DDS_OUT GPIOA_BASE->ODR
GPIOA_BASE->CRL = 0x33333333;// pa0-pa7  выход
  #endif 
     if (oldmode !=mode) {
      if (mode==3) {for(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*float(n)/512)+1)/2 ;}}// синус
       else if (mode==4){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=n;} else {wave[n]=(511-n);}}}//треугол
      else if (mode==5){ for(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}                               //пила1
     else if (mode==6){ for(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}                            //пила2
    else if (mode==7){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=0;} else {wave[n]=255;}}}    //меандр
  oldmode=mode; } 
uint32_t dds_shag= (double)freq/10 * ((F_CPU==72E6)? dds_mpl_72 : dds_mpl_128) ;//  шаг= частота*коэффициент
asm volatile (
"mov   R9, %[port];"  "\n\t" // записать в r9 адресс порта "A"-ODR 
 "mov   R8, %[wave];"       "\n\t" //адресс массива положить в r8  
 "mov   R7, %[shag];"   "\n\t" // значение шага в r7
  "dds_loop:"                  "\n\t"
   "add R6, r7;"               "\n\t"  //(1)добавить к аккумулятору шаг
    "lsrs r2, r6, #23;"         "\n\t"  //(1) положить в R2 сдвинутый на 23 бита аккумулятор
   "ldrb r2, [r8, r2];"        "\n\t"  //(2)загрузить в R2 выбранный байт из массива
   "strb  r2, [r9];"           "\n\t"  //(2) запиcать этот байт в PORTA-ODR
  "ldr  R2, [%[flag]];"       "\n\t"  //(2) подгрузить в  R2 флаг 
 "cmp r2, 1;"                "\n\t"  //(1) сравнить 
"bne dds_loop;"              "\n\t"  //(1) перейти в цикл
: : [wave]"r" (&wave),[shag]"r"(dds_shag),[port]"r"(&DDS_OUT),[flag]"r"(&mon_flag)
: "r9","r8","r7","r6","r2" 
);
  GPIOA_BASE->CRL=0x44444444;// все пины в Z
}//END DDS set()
//


  void mytone(int frq, int ms ){
 uint16_t psc=1; uint32_t tim_arr;
 // настройка генератора звука на таймере3
  tim_arr = (F_CPU/2)/frq;
  while ( (tim_arr/psc) > 65535) {psc++;} 
  __asm volatile( "cpsid i" ); 
   TIMER2_BASE->SMCR=0;
    TIMER3_BASE->CCR3=0; //обнулить регистр соответсвующий используемому выходу
     TIMER3_BASE->PSC=psc-1;
      TIMER3_BASE->ARR=(tim_arr/psc)-1;
       TIMER3_BASE->CCMR2=(1<<5)|(1<<4);// OC3M:011
        TIMER3_BASE->CCER=1<<8;//cc3e  подключить аппаратную ногу
         TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);//SMS:101 && TS:001  строб от 2-го таймера  
          TIMER3_BASE->EGR=1; //перечитать регистры.
           TIMER3_BASE->CR1=1;
           /// настройка выдержки времени на таймере2
          psc=1;
        tim_arr = (F_CPU/1E3) * ms;
       while ( (tim_arr/psc) > 65536) {psc++;} 
      TIMER2_BASE->CCMR2=0;
     TIMER2_BASE->CR2=0;
    TIMER2_BASE->CR1=(1<<3)|(1<<2);//один импульс, без прерываний
   TIMER2_BASE->CNT=0;
  TIMER2_BASE->CR2=(1<<4);  //MMS:001 сигнал разрешения работы другим таймерам
 TIMER2_BASE->PSC=psc-1;
TIMER2_BASE->ARR=(tim_arr/psc)-1;
TIMER2_BASE->EGR=1; //перечитать регистры.
TIMER2_BASE->SR=0;//отчистить флаги
TIMER2_BASE->CR1|=(1<<0);
  __asm volatile( "cpsie i" );
}

void clock_gen(){ //функция работа с синтезатором si5351
if (!si5351_found) {return;} // на всякий случай 
if (freq <4E4){freq=4E4;} if (freq>200E7){ freq=200E7;} //допустимые рамки частот
si5351.set_freq((freq/10)* SI5351_FREQ_MULT, SI5351_CLK0);
mon_flag=1;
}

void comm(){ //коммутация выходов через мультиплексор
if (mode==1 || mode==2 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,LOW);}
  else  if (mode>2 && mode<8 ) {digitalWrite(PB3,HIGH); digitalWrite(PB4,LOW);}
  else if (mode==0 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,HIGH);}
 else if (mode==8) {digitalWrite(PB3,HIGH); digitalWrite(PB4,HIGH);}
}

void volt_meter() {
static boolean alarm=0;
 uint64_t akkum=0;
 //измерение напряжения питания МК
  for (int n=0; n<=16383; n++ ) {//собирать 16384 выборок 
    akkum += sq(adc_read(ADC1,17)); } //суммировать квадраты
     akkum =  (sqrt(akkum>>14));
        Vcc = (VrefINT <<12) / akkum;
         //измерение напряжения на входе ADC9(PB1)
        akkum=0;
       for (int n=0; n<=16383; n++ ) {//собирать 16384 выборок 
      akkum += sq(adc_read(ADC1,9)); } //суммировать квадраты
     akkum =  (sqrt(akkum>>14));
   for (int n=0; n<=65535; n++ ) {asm volatile("nop"); } //Типа delay
  freq = (double) Mn * ((uint32_t)(akkum * Vcc *10)/4096);
   // если напряжение не удовлетворяет условиям, и буззер не работает -то включить
 if      (freq < Vin_low*10) { alarm =1;  if (TIMER2_BASE->SR!=0) {mytone (200, 10000);} }
  else if (freq >  Vin_hi*10) { alarm =1;  if  (TIMER2_BASE->SR!=0) {mytone (1000, 10000);} }
   else alarm=0;
    //если всё ок, а буззер работает, то выключить.
     if (!alarm && (!TIMER2_BASE->SR) ) {mytone (0, 0);}
  }

Убедительная просьба не нажимать кнопку цитировать!

Max Yakovenko
Offline
Зарегистрирован: 25.05.2019

Есть схема для сборки на ардуино нано

240265
240265 аватар
Offline
Зарегистрирован: 12.08.2015

А зачем ADC1,17 среднеквадратичное вычислять, там же постоянка ?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

240265, у меня было 2 цели- (1) адекватно измерять пульсирующие однополярные сигналы (2) и в целях шумопонижения, -тут проблема в том, что АЦП этого мк f103c8t6 умеет измерять аналоговый сигнал только относительно шины Vcc, на которой стоит собственный шум амплитудой до 70мВ(конкретно на своей плате измерял). Поэтому было бы неприятно, измеряя совершенно бесшумный источник, например аккумулятор, получить скачки измерений +/- 35мВ. А с среднекватическим счётом  получается довольно устойчивые измерения :) Хотя  в дополнении к (1) -тут тоже есть свои нюансы. Например если измерять 5-вольтовый 50% ШИМ, то ср.квд. напряжение будет 3,5 вольта. А просто среднее 2,5в. Т.е. в идеале можно было бы выводить на дисплей  и обычное среднее значение, ну или переключать их. Но что-то меня заломало это делать..

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

Парни, подскажите :( собрал генератор версию 2.4, все запустилось, сигнал выдает, но.....пила 1 , 2 и меандр искаженые, синус и треугольник нормальной формы, его поправил в скетче изменением массива, как советовал dimax. Как можно это поправить??  Arduino пока изучаю

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

AnatolyDem, думаю самый простой способ -использовать рекомендованную версию Arduino IDE 1.6.8.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

dimax, не понял :( я использую Arduino IDE ту что ты выложил в архиве, за что тебе спасибо. Твоя версия что не подходит? сейчас проверил, она 1.8.9 :(

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

AnatolyDem, тот архив сделан для версий на мк  stm32.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

хм......сегодня попробую 1.6.8 ...так вроде в тот архив просто еще добавлено для stm, чем он отличается от чистого?

 

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

AnatolyDem пишет:

хм......сегодня попробую 1.6.8 ...так вроде в тот архив просто еще добавлено для stm, чем он отличается от чистого?

Правильно заданный вопрос - половина ответа :-)

Вот этим и отличается "просто еще добавлено для stm" - на Ардуины не влияет.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

тогда я не понимаю насчет версии 1.6.8 ;(  как это мне поможет?   Я конечно понимаю что все уже переключились на smt, но иногда хватает и атмел, они дома есть, накуплено немало

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

AnatolyDem пишет:

тогда я не понимаю насчет версии 1.6.8 ;(  как это мне поможет?   Я конечно понимаю что все уже переключились на smt, но иногда хватает и атмел, они дома есть, накуплено немало

Разные версии ИДЕ генерят несколько разный код, и что там пришло в голову разработчикам - тайна, покрытая мраком :-) Так что скачайте 1.6.8 и попробуйте - не зря Автор советует.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

скачал, проверил...все то же самое :(...сейчас сижу читаю, пытаюсь разобратся, у меня еще энкодер ерундит, ну это ладно.  Что с формой сигнала делать пока не пойму...синус и треугольник нормальной формы...

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

При использовании разных версий есть некоторые "подводные камни". Если устанавливать через инсталлятор - создается скрытая папка Ардуино15 или как-то так, в которой хранится всякая хрень для работы ИДЕ, при пользовании другой версией данные могут браться оттуда, так что лучше эту папочку удалить.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

я версию 1.8.9 не устанавливал, dimax выложил архив, я его распаковал и использовал, а нормально устанавливал сразу 1.6.8. Папку Arduino15 нашел. Что, её попробовать стереть и снова попробовать прошить? 

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

Все, победил. Большое спасибо. Вот так и учусь, мучюсь мучюсь :) потом умные люди подскажут...в голове что то остается :)) БЛАГОДАРЮ!!!!!

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Эта ИДЕ довольно хитропопая штука, сколько народу здесь мучалось с компиляцией под СТМ32, в результате чего Dimax  выложил полный рабочий архив. Все эти обсуждения давно подтёрты, чтоб ветку не захламлять. Так что коллективный разум - не последняя вещь.

Пограничники - с праздником !!! Особенно 2289, 2139, 2121, 2209.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

еще вопрос появился ;) можно как то поправить энкодер, как я понял существует два варианта, у меня он проскакивает через один сигнал...поискал, что то внятного про эту проблему пока ничего не нашел

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Очень желательно использовать ЕС11 (12, 16) и ему подобные, с ними проблем нет. Была какая-то корректировка скетча в самом начале ветки, пост 17. И конденсаторы от лапок энкодера на землю лучше поставить 0.1 мкф.

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

 

MAG-N, спасибо....пропустил этот пост, кондеры стоят.

С праздником тебя :) ...я служил ЗА границей :))

AnatolyDem
Offline
Зарегистрирован: 09.06.2016

ЗАРАБОТАЛО!!!!! Еще раз благодарю

MAG-N
MAG-N аватар
Offline
Зарегистрирован: 05.06.2017

Не за что, но хорошо, что заработало. У меня 3.2 (почти 3.4) - работает отменно.