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

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