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

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

iarick пишет:

Добрый день, помогите начинающему. Хочу собрать данный генератор. Он мне нужен в первую очередь для отладки УМЗЧ.

Как я понимаю, у генератора будет синусойда смещена верх (постоянная составляющая)..

Просьба подсказать реально хорошую схему буферного усилителя которая:

1. Убирала постоянную составляющую

2. Позволяла бы регулировать амплитуду до 5-7 в...

Заранее признателен.

П.С. отсмотрел много (хотя по факту их мало ;)  ) всяких схем и запутался окончательно, прошу помощи гуру.

если решил заняться усилителями ищи на Авито ГЗ-118, он для этого годится

Piton
Offline
Зарегистрирован: 18.01.2022

iarick, на 13-й странице я выкладывал схему смещения и усилителя. Всё прекрасно работает. Но в последней версии я отказался от регулировки смещения во втором каскаде, сделал как в авторской(верхняя схема), иначе будет зависимость выходного напряжения от нагрузки. Смещение выставляется в первом каскаде один раз подстроечником. Можно при желании и им регулировать, но будет не симметрично. Если нагрузка высокоомная, то можно оставить и так. В этом варианте вам придётся коммутатор делать на реле, или, как в моём случае, на adg604, или ставить смещение и усилитель после коммутатора, подключая его в режиме синуса. Конечно, это не ГЗ-118, но до 20кГц вполне приличный синус и вполне сойдёт для настройки усилителей.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Насчет "вполне приличного синуса" есть некоторые сомнения. Особенно на высоких частотах.

Следовательно, в сигнале будет довольно много гармоник. Уменьшить их количество можно при помощи ФНЧ. Например, типа такого:

Piton
Offline
Зарегистрирован: 18.01.2022

А что нужно начинающему аудиофилу для настройки усилителя?-посмотреть искажения типа "ступенька",   равномерность частотного  диапазона, выходную мощность, плюс есть ешё и прямоугольник для дополнительных испытаний. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Piton пишет:

А что нужно начинающему аудиофилу для настройки усилителя?-посмотреть искажения типа "ступенька",   равномерность частотного  диапазона, выходную мощность, плюс есть ешё и прямоугольник для дополнительных испытаний. 

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

Piton
Offline
Зарегистрирован: 18.01.2022

Ну, если "вгонять", то тогда да. Осциллограф по умолчанию должен быть. А вот измеритель нелинейных искажений с этим генератором, разумеется, будет лишним. Для большинства "даже не начинающих", достаточно будет этого генератора и осциллографа. Это, конечно, моё личное мнение.

iarick
Offline
Зарегистрирован: 24.12.2020

andriano пишет:

iarick пишет:

Просьба подсказать реально хорошую схему буферного усилителя которая:

1. Убирала постоянную составляющую

2. Позволяла бы регулировать амплитуду до 5-7 в...

1. Конденсатор.

2. Потенциометр + ОУ с коэффициентом усиления ~3.

 

Вот тут то и загвоздка....

1. Емкости. Какой номинал предлагаете? Просто в разных схемах от 1 мкф до 100 пик предлагают ставить... Понимаю, что 1 Мкф много... но самому сделать расчет, да так чтоб еще и прямоугольник не валился по фронтам, не умею пока.... Какое значение емкости предлагаете?

2. Какой ОУ предлагаете ? (желательно конкретную модель), понимаю, что для этого генератора верхняя граничная частота должна быть не менее 50 мгц... или я ошибаюсь?

Заранее признателен за помощь...

iarick
Offline
Зарегистрирован: 24.12.2020

andriano пишет:

iarick пишет:

Просьба подсказать реально хорошую схему буферного усилителя которая:

1. Убирала постоянную составляющую

2. Позволяла бы регулировать амплитуду до 5-7 в...

1. Конденсатор.

2. Потенциометр + ОУ с коэффициентом усиления ~3.

 

Вот тут то и загвоздка....

1. Емкости. Какой номинал предлагаете? Просто в разных схемах от 1 мкф до 100 пик предлагают ставить... Понимаю, что 1 Мкф много... но самому сделать расчет, да так чтоб еще и прямоугольник не валился по фронтам, не умею пока.... Какое значение емкости предлагаете?

2. Какой ОУ предлагаете ? (желательно конкретную модель), понимаю, что для этого генератора верхняя граничная частота должна быть не менее 50 мгц... или я ошибаюсь?

Заранее признателен за помощь...

iarick
Offline
Зарегистрирован: 24.12.2020

Piton пишет:

А что нужно начинающему аудиофилу для настройки усилителя?-посмотреть искажения типа "ступенька",   равномерность частотного  диапазона, выходную мощность, плюс есть ешё и прямоугольник для дополнительных испытаний. 

 

В десятку!!!!. ;) ну, еще б правда хотелось, ШИМ иметь, как запас на будущее ;)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iarick пишет:

1. Емкости. Какой номинал предлагаете? Просто в разных схемах от 1 мкф до 100 пик предлагают ставить... Понимаю, что 1 Мкф много... но самому сделать расчет, да так чтоб еще и прямоугольник не валился по фронтам, не умею пока.... Какое значение емкости предлагаете?

2. Какой ОУ предлагаете ? (желательно конкретную модель), понимаю, что для этого генератора верхняя граничная частота должна быть не менее 50 мгц... или я ошибаюсь?

Заранее признателен за помощь...

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

2. Вам нужно для усилителя звуковой частоты? Тогда достаточная полоса пропускания - произведение верхней рабочей частоты (20 кГц?) на коэффициент усиления. Откуда там 50 МГц?

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

Посоветую прочитать:

- Хоровиц, Хилл, Искусство схемотехники*,

- Титце, Шенк, Полупроводниковая схемотехника*.

Обе прочитать от корки до корки и не менее двух раз.

* писал по памяти, если где ошибся, в теме "Новичок, прочти" есть точные ссылки с указанием инициалов.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DIMAX! Такой вопрос, аддон Кларка под IDE версии 1.8.19 не пробовал адаптировать?

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

А зачем адаптировать? Под 1.8.19 вроде и так работает.

Под 2.0.3 тоже

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Да пытаюсь дисплей ST7789 прикрутить, по привычке библиотеки у меня лежат /portable/sketchbook/libraries а аддон в /portable/packages  ...что-то не выходит каменный цветок...

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

Так может все дело в библиотеке для 7789, а АДДОН не виноват?

Далеко не все библиотеки дружат с СТМ32

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

MAG-N пишет:

Далеко не все библиотеки дружат с СТМ32

интересно девки пляшут, вот как так, ST7735 работает, а ST7789 - белый экран, подкидываю ST7735 на прошивке с ST7789 - показывает, хоть и с лагами, библиотеки от Adafruit

termak
Offline
Зарегистрирован: 11.09.2020

Уже устал бороться с этим генератором. V.3.5

Прошиваю прошивками с этого сайта ST-Link и ничего кроме подсветки не работает.

Брал и разные платы и уже купил второй индикатор... (думал может индикатор не работает).

Однако не могу запустить ничего.

 

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

2 варианта

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

2 возможно в схеме с разводкой намутили

UEF
Offline
Зарегистрирован: 27.01.2018

Добрый день,

Кто использует версию 3.5 без SI5351? Стабильно работает? У меня запускется через раз и иногда не адекватно работает энкодер, Когда не запускается , то просто пустой экран, после нажатия, несколько раз на сборо , может запустится. 

postal2201
Offline
Зарегистрирован: 05.01.2017

Добрый день! Подскажите, возможно ли на ардуино сделать меандр с частотой 3.58MHz и 4.43MHz при установленном кварце на 16MHz ? Или хотя-бы с заменой кварца, но так чтобы его частота была не ниже 16MHz, и можно было использовать 1 кварц для обеих частот.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Нет

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

 postal2201   Проще 2 генератора на инверторах собрать.

Dushev
Offline
Зарегистрирован: 29.04.2018

[quote=UEF]

Добрый день,

Кто использует версию 3.5 без SI5351? Стабильно работает? У меня запускется через раз и иногда не адекватно работает энкодер, Когда не запускается , то просто пустой экран, после нажатия, несколько раз на сборо , может запустится. 

[quote]

Dushev
Offline
Зарегистрирован: 29.04.2018
/*       Генератор с регулируемой частотой v3.5  (C)Dimax      
  * https://arduino.ru/forum/proekty/generator-s-reguliruemoei-chastotoi-na-...      
 https://disk.yandex.ru/d/_BFVMSyz7Wb0Bg - ARDUINO.IDE от Dimax
 */
    #define pwm2_polar 0 //полярность выхода PWM2 (вывод PA7)
   #define paper        0x000000 // цвет фона экрана
  #define DDSMAX 1E7 //максимальная частота для генератора DDS (удесятерённая)
 #define dds_mpl_72 1133.8314742150209378723713167832 //множитель DDS для частоты F_CPU 72МГц 
                  //835.05327478167234049174700635502 - origin !!!
//для пересчёта множителя необходимо: частоту на экране прибора * текущий множитель и разделить на фактически измеренную частоту
#define dds_mpl_128 469.7191655978919104715512499704  //множитель DDS для  частоты F_CPU 128Mhz
#define VrefINT 1209  //внутренее опорное напряжение в милливольтах 
 #define Mn 6.06  //множитель для пересчёта напряжения с учётом резисторного делителя.
 #include <Adafruit_ST7735.h> // Hardware-specific library
   #include <SPI.h> 
     #include <Wire.h> 
      #include <libmaple/dac.h>
       Adafruit_ST7735 tft = Adafruit_ST7735(-1, PB11,PB10); //PB12 освобождён, вывод CS дисплея запаять на землю.
         boolean  modevolt, infreqpsc; // si5351_found,
      volatile int enc_tic=0, duty_in=50, mon_flag, divider, modebit=1;
      volatile int mode=1;//  1-PWM, 2-Duty, 3-impuls , 4..7 DDS, 8-Freqmeter, 9-VoltMeter 
      volatile byte imp_mode=1; //единицы счёта длины импульса  по умолчанию 0-мс, 1-мкс, 2 -такт
      volatile byte imp_mode_menu=0; //переменная выбора меню в одновибраторе (значение длины/единица времени/шаг)
      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();
  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 бит
RCC_BASE->APB1RSTR |=  1<<0; //сброс таймера2
 RCC_BASE->APB1RSTR &= ~(1<<0); // сброс таймера2
  TIMER2_BASE->CR2=1<<5; //MMS:010 управление подчинённым в режиме "Update" 
    TIMER2_BASE->SMCR= (1<<14)|(infreqpsc<<13)|(infreqpsc<<12);// режим 2 внешнего тактирования + делитель/8 + разрешение работы от таймера1 
     TIMER2_BASE->ARR=65535; //считать до максимума
       TIMER2_BASE->EGR=1; //перечитать регистры.
       TIMER2_BASE->CR1|=(1<<0);//start timer2
      /// Timer3 счёт старших 16 бит
RCC_BASE->APB1RSTR |=  1<<1; //сброс таймера3
 RCC_BASE->APB1RSTR &= ~(1<<1); // запуск таймера 3
 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 (infreqpsc) {freq*=8;}// если включен делитель на 8 то результат умножить на 8
    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" );
RCC_BASE->APB1RSTR |=  1<<0; //сброс таймера2
 RCC_BASE->APB1RSTR &= ~(1<<0); // запуск таймера 2
  TIMER2_BASE->CR1=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 1: tft.print("  PWM Mode   "); break;
          case 2: tft.print("  Duty Mode  "); break; 
           case 3: tft.print("  Sine DDS  "); break; //3
            case 4: tft.print(" Triangle DDS"); break; //4
           case 5: tft.print("  Saw 1 DDS  "); break; //5
          case 6: tft.print("  Saw 2 DDS  "); break; //6
         case 7: tft.print("  Square DDS "); break;
       case 8: tft.print(" Freq. meter "); break; //7
      case 9: tft.print(" Volt. meter "); break; //8
       }
 
 //*****************Вывод второй строчки*****************************
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.setTextColor(ST7735_RED,paper); //красный цет строки для всех вариантов              
              
              if (mode==9){ //если вольтметр
                tft.setTextSize(3); //крупно
               tft.setCursor(50, 43);
                tft.print(" mV"); }              

              else if (mode==8) { //если частотометр
               tft.setTextSize(2);
                tft.setCursor(20, 43);
                tft.print("Herz ");
                tft.setCursor(75, 50); tft.setTextSize(1);
                infreqpsc? tft.print("max 180MHz") : tft.print("max 32MHz ") ;
                 }

if (imp_mode_menu == 1) {tft.setTextColor(ST7735_RED,ST7735_WHITE);}  /////   изтрий ако горното е вкл.
              
               else if (mode <8) { //если генераторы
                tft.setTextSize(3); 
                tft.setCursor(50, 43);
               tft.print("Herz");  
                }
          
  //********************* "осциллограммы"******************************
     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==5
                         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){ return;} // в режиме  частотометра выводить на экран больше ничего не нужно.
          
  /////////// установка курсора и вывод шага в разных режимах ///////////////   
  
                else if (mode<9) { tft.setCursor(114, 95); tft.print("Step="); tft.setCursor(114, 105);} 
                 else if (mode==9)  {tft.print("Step=");} //только в вольтметре
                
                   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 */          
   // вывод прочей информации

/// вывод пределов для вольтметра
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  = 2;} //опрос фазы 1 энкодера
    if(!(  GPIOB_BASE->IDR&128 )){ EncCur |= 1;} //опрос фазы 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; } 
      else { if(mode<1){mode=1;} }
     if(mode<1){mode=1;} ///////////////////////////
       if (mode==1 || mode==2 ){timer_set(0);}
        if (mode==8) {freq=0;}// сбросить в ноль freq в режиме частотометра 
        if (mode >2 && mode !=8) {if (freq>DDSMAX) {freq=DDSMAX;} }////////////////***********///////////
         mon_flag=1; enc_step_control();
         return; 
          } //сюда попадает при изменении частоты (вращение без нажатия)
         mytone(4400,10); //звук изменения частоты
       switch(mode){ //если сейчас идёт изменение частоты
     case 1:   timer_set(in); break;
     case 2:   duty_in+=in; timer_set(0);  break;  
    // case 3:  if (imp_mode_menu==0){ freq+=(encstep*in); if (freq<10) freq=10; if (freq> 500000) {freq=500000;}   }  //переключение длительности импульса
         //else 
         if (imp_mode_menu==1){ imp_mode++;  if (imp_mode >2){imp_mode=0;} }//переключение 0-мс, 1-мкс, 2 -такт           
         //else
         if (imp_mode_menu==2){ encstep*=10; enc_step_control();} //переключение шага

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

void enc_step_control(){ //ограничение шага в зависимости от режимов и частот
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
pinMode(PA7,PWM); pinMode(PA8,PWM);
 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" 
);
  #if defined (STM32_MEDIUM_DENSITY) 
  GPIOA_BASE->CRL=0x44444444;// все пины в Z для резисторного цап
 #endif
} //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 comm(){ //коммутация выходов через мультиплексор
if (mode>0 && mode<3 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,LOW);}
  else  if (mode>2 && mode<8 ) {digitalWrite(PB3,HIGH); digitalWrite(PB4,LOW);}
 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);}
  }

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Для терминальной версии под STM32G431 как дефайны найти, читал даташит, совсем не понял адресацию

nickjust
Offline
Зарегистрирован: 29.04.2021

а можно добавить калибратор 0-20 мв ?

vokovl
Offline
Зарегистрирован: 12.12.2021

Возник такой вопрос, собрал генератор, мне он нужен для работы до 500кгц, работает отлично, но мне нужно что бы в точке, которая на рисунке, включался другой пин на HIGH

 

 

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

Добрый день!!!

Ребята у кого есть Файлы AN619,AN1234 для si5351.

Они закрыты,может кто скинет на почту miklin@mail.ru 

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

Тема видно заглохла,все наделали генераторов.

Тоже сделал генератор давно но без si5351,не было необходимости, хотя подключение

предусмотрел. Решил подключить (v3.5) проц её не видит. Прошиваю другие платы

не работают(ST link),как прошивал  рабочую не помню. Вроде писали что ST перемычки

без разницы. Как выйти в личку на автора или на MAG-N, не знаю. Вроде на этом сайте

нет лички.