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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Иногда при работе с мк бывает нужно подать сигнал определённой частоты, а специального устройства у меня нет, т.к. не сильно то оно нужно, да и ставить ещё один ящик негде. Вот написал скетчик генератора с регулируемой частотой, в большинстве случаев его достаточно. Что он может: -генерит меандр на 16 битном таймере. Диапазон частот 1Гц - 8МГц. Регулировка частоты производится энкодером. До частоты 2,8 кГц разрешение 1 герц, на частотах выше таймер аппаратно уже не может поддерживать это разрешение, поэтому более высокие частоты синтезируются задавая параметром  не требуемую частоту, а просто инкременируя регистр сравнения. Получается чем выше частота -тем больше шаг между щелчками энкодера. Вращая энкодер с ненажатой кнопкой частота меняется на 1Гц , с нажатой кнопкой один шаг -100Гц.  Выше 2,8кГц вращение энкодера с нажатой конпкой так-же ускоряет счёт. Програмного подавления дребезга контактов энкодера нет, поэтому нужно повесить конденсаторы 0,01..0,1 мкф относительно земли. На кнопке конденсатор не обязателен. Рассчитанная математически частота выводится в сериал.  Энкодер использовался самый популярный, для нестандартных возможно придётся корректировать обработчик.

/* Генератор 1 Hz..8 MHz. Энкодер подключен к пинам A0 и A1, кнопка 
энкодера подключена к A2. Требуется использовать конденсаторы  0,01..0,1uf 
относительно земли на каждый из 2х выводов энкодера.
Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
*/

float freq; 
void setup() {
pinMode (9,OUTPUT); // выход генератора
pinMode(A0,INPUT); // с рассчетом, что  энкодере внешняя подтяжка-   
pinMode(A1,INPUT); // -к шине питания. Если нету, то подтянуть программно. 
pinMode(A2,INPUT_PULLUP); //кнопка энкодера 

Serial.begin(9600);
PCICR=1<<PCIE1; //разрешить прерывание PCINT
PCMSK1=(1<<PCINT9);// По сигналу на А1 создавать прерывание
TCCR1A=1<<COM1A0; //подключить выход OC1A первого таймера
TCCR1B=0;//
}


ISR (PCINT1_vect){
static boolean gen_mode=0; //флаг режима управления
static uint32_t enc=1; //переменная счёта энкодера
uint32_t ocr=OCR1A;
uint32_t divider=1; //переменная коэфф. деления прескалера

byte n=PINC&3; //считать значение энкодера
boolean knopka = PINC&(1<<2); // 0-кнопка нажата, 1-кнопка НЕ нажата.
if (freq<2848) gen_mode=0; //переключение режима управления по частоте
if (freq>=2848) gen_mode=1; //переключение режима управления по OCR

// Если  увеличение частоты
if (n==3||n==0){ 
 if (gen_mode){if (knopka){ if(ocr>0) {ocr--; } } else {  if(ocr>9)ocr-=10; }   }
 else knopka?  enc++ : enc+=100; // в нч режиме
 } //end GetUP

// Если уменьшение частоты
if (n==2||n==1){ 
 if (gen_mode){ if (knopka){ if(ocr<65535) {ocr++; } } else {  if(ocr<=65525)ocr+=10; }   }
 else {if (knopka) { if (enc>=2)enc--;  } else { if (enc>100) enc-=100; }  }
 } //end GetDown


if(gen_mode){ OCR1A=ocr;  freq= (float)F_CPU/2 / (OCR1A+1);  } 
else { //расчёт прескалера и OCR по нужной частоте
divider=1; ocr = (F_CPU / enc /2 /divider) -1;
if (ocr >65536) { divider=8; ocr = F_CPU / enc /2 /divider;
    if (ocr >65536) { divider=64; ocr = F_CPU / enc /2 /divider;
        if (ocr >65536)  {divider=256; ocr = F_CPU / enc /2 /divider;
            if (ocr >65536) { divider=1024; ocr = F_CPU / enc /2 /divider;
                if (ocr >65536){ocr=65536; }}}}} OCR1A=ocr-1; 
//запись в регистр прескалера            
switch (divider) {
  case 1: TCCR1B=1|(1<<WGM12); break;
   case 8: TCCR1B=2|(1<<WGM12); break;
    case 64: TCCR1B=3|(1<<WGM12); break;
     case 256: TCCR1B=4|(1<<WGM12); break;
      case 1024: TCCR1B=5|(1<<WGM12); break;  }

    freq= (float) F_CPU/2 / (OCR1A+1) /divider;
  } //end if !gen_mode
}


void loop() {

  
if (freq <10000) { Serial.print(freq,1);Serial.println(" Hz "); }
if (freq >10000) { Serial.print(freq/1000,3);Serial.println(" kHz");}


delay(100);
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Кстати заметил ошибку в стартовом сообщении. В 48 строке нужно убрать -1  в конце строки.

Kolchugin
Offline
Зарегистрирован: 15.12.2013

dimax. Проверил ваш проект в работе.  Смотрел меандр осциллографической приставкой, так же покрутил на частотомере.
Всё отлично! 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

jeka_tm, вот, модифицировал основной код. Эта версия с регулировкой скважности. Частоту ограничивать в итоге не стал, скважность регулируется на всех частотах, просто начиная с какого-то значения регулируется всё бОльшими рывками. На мегагерце например всего 8 градаций.  Но энкодер всё равно везде счёлкает 100 градаций (в процентах).Некоторое неудобство, но избавится от него малыми силами не выйдет. Хочешь, сам повозись, можно по идее ввести разрядность регулировки скважности в зависимости от частоты.

/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер регулировки частоты 
подключен к пинам A0 и A1, кнопка  энкодера подключена к A2. 
Энкодер регулировки скважности  подключен к пинам 8 и 10
Его кнопка не задействована.
Требуется использовать конденсаторы  0,01..0,1uf  относительно земли 
на каждый вывод обоих энкодеров.
Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
*/
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
uint8_t duty=50;
float freq; 
void setup() {
Serial.begin(9600);
pinMode (9,OUTPUT); // выход генератора PB2
pinMode(A0,INPUT); // PC0 вывод энкодера "частота"
pinMode(A1,INPUT); // PC1 вывод энкодера "частота"
pinMode(A2,INPUT_PULLUP);  // PC2 кнопка энкодера 
pinMode(8,INPUT);  // PB0 вывод энкодера "скважность"
pinMode(10,INPUT); // PB2 вывод энкодера "скважность"
PCICR=(1<<PCIE1)|(1<<PCIE0); //разрешить прерывания PCINT 0, 1
PCMSK1=1<<PCINT9;// По сигналу на А1 создавать прерывание (устновка частоты)
PCMSK0=1<<PCINT0;// По сигналу на pin8 создавать прерывание (скважность)
TCCR1A=1<<COM1A1; //подключить выход OC1A первого таймера
TCCR1B=0;//
}

ISR (PCINT1_vect){
static boolean gen_mode=0; //флаг режима управления
static uint32_t enc=1; //переменная счёта энкодера
uint32_t icr=ICR1;
uint16_t divider=1; //переменная коэфф. деления прескалера

byte n=PINC&3; //считать значение энкодера частоты
boolean knopka = PINC&(1<<2); // 0-кнопка нажата, 1-кнопка НЕ нажата.
if (freq<2848) gen_mode=0; //переключение режима управления по частоте
if (freq>=2848) gen_mode=1; //переключение режима управления по OCR

// Если  увеличение частоты
if (n==3||n==0){ 
 if (gen_mode){if (knopka){ if(icr>2) {icr--; } } else {  if(icr>12)icr-=10; }   }
 else knopka?  enc++ : enc+=100; // в нч режиме
 } //end GetUP

// Если уменьшение частоты
if (n==2||n==1){ 
 if (gen_mode){ if (knopka){ if(icr<65535) {icr++; } } else {  if(icr<=65525)icr+=10; }   }
 else {if (knopka) { if (enc>=2)enc--;  } else { if (enc>100) enc-=100; }  }
 } //end GetDown


if(gen_mode){ ICR1=icr; set_duty(); freq= (float)F_CPU/2 / ICR1;  } 
else { //расчёт прескалера и ICR по нужной частоте
divider=1; icr = F_CPU / enc /2 /divider;
if (icr >65536) { divider=8; icr = F_CPU / enc /2 /divider;
    if (icr >65536) { divider=64; icr = F_CPU / enc /2 /divider;
        if (icr >65536)  {divider=256; icr = F_CPU / enc /2 /divider;
            if (icr >65536) { divider=1024; icr = F_CPU / enc /2 /divider;
                if (icr >65536){icr=65536; }}}}} ICR1=icr-1; set_duty();
//запись в регистр прескалера            
switch (divider) {
  case 1: TCCR1B=1|(1<<WGM13); break;
   case 8: TCCR1B=2|(1<<WGM13); break;
    case 64: TCCR1B=3|(1<<WGM13); break;
     case 256: TCCR1B=4|(1<<WGM13); break;
      case 1024: TCCR1B=5|(1<<WGM13); break;  }

    freq= (float) F_CPU/2 / (ICR1+1) /divider;
  } //конец "если  работа в НЧ режиме"
}

ISR (PCINT0_vect){ //обработчик энкодера "скважность"
byte m=PINB&B00000101; //считать значение энкодера2
if (m==0||m==5){ if(duty<100) {duty++; }  }
if (m==1||m==4){ if(duty>0) {duty--; }  }
set_duty();
}

void loop() {
if (freq <10000) { Serial.print(freq,1);Serial.print(" Hz "); }
if (freq >10000) { Serial.print(freq/1000,3);Serial.print(" kHz");}
Serial.print("<br>"); 
Serial.print("Duty="); Serial.print(duty); Serial.print("%");
Serial.println();
delay(100);
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Сделал генератор-шилд для UNO, как и задумывал ранее. За основу взята версия с регулировкой скважности, но вместо второго энкодера добавил кнопку, которая переключает  энкодер в режим частоты или скважности.  Фотки и видео:

И видео с демонстрацией режимов (быстрая регулировка частоты, по-герцевая регулировка, регулировка скважности) https://cloud.mail.ru/public/CaFy/QxWcRpwSy

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

а можеim скинуть код как в 18 посте но кнопкой плюс энкодер как на видео?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

jeka_tm, да, конечно. 

/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер 
подключен к пинам A1 и A2, кнопка  энкодера подключена к A0. 
Кнопка частота/скважность 2
Требуется использовать конденсаторы  0,01..0,1uf  относительно земли 
на каждый вывод энкодера. Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
Дрова для 5110 взяты из  http://www.rinkydinkelectronics.com/library.php?id=44
*/
#include <LCD5110_Basic.h>
LCD5110 lcd(3,4,5,6,7);
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
uint8_t duty=50;
float freq; 
volatile uint8_t enc_mode_flag=1; // По умолчанию энкодер регулирует частоту

void setup() {
lcd.InitLCD();
lcd.setContrast(65); 
pinMode (13,OUTPUT); // LED
pinMode (9,OUTPUT); // выход генератора PB2
pinMode(A0,INPUT); // PC0 кнопка энкодера 
pinMode(A1,INPUT); // PC1 вывод энкодера
pinMode(A2,INPUT);  // PC2 вывод энкодера 
pinMode(2,INPUT_PULLUP); // подтяжка кнопки "режим энкодера"
PCICR=1<<PCIE1; //разрешить прерывание PCINT1
PCMSK1=1<<PCINT9;// По сигналу на А1 создавать прерывание
TCCR1A=1<<COM1A1; //подключить выход OC1A первого таймера
TCCR1B=0;//
EICRA=1<<ISC01; //int0 -falling mode
EIMSK=1; //int0 enable
}

ISR (INT0_vect){
  if ((PIND & (1<<2)) == 0)
      { enc_mode_flag = !enc_mode_flag; }
      }
ISR (PCINT1_vect){;
     uint8_t n=PINC&6; //считать значение энкодера частоты
     if (enc_mode_flag)  enc_freq(n); // если энкодер в режиме частоты
     if (!enc_mode_flag) enc_duty(n); // если энкодер в режиме скважности
     }


void enc_freq(uint8_t n){
static boolean gen_mode=0; //флаг режима управления
static uint32_t enc=1; //переменная счёта энкодера
uint32_t icr=ICR1;
uint16_t divider=1; //переменная коэфф. деления прескалера
boolean knopka = PINC&(1<<0); // 0-кнопка нажата, 1-кнопка НЕ нажата.
if (freq<2848) gen_mode=0; //переключение режима управления по частоте
if (freq>=2848) gen_mode=1; //переключение режима управления по OCR
// Если  увеличение частоты
if (n==4||n==2){ 
 if (gen_mode){if (knopka){ if(icr>2) {icr--; } } else {  if(icr>12)icr-=10; }   }
 else knopka?  enc++ : enc+=100; // в нч режиме
 } //end GetUP

// Если уменьшение частоты
if (n==6||n==0){ 
 if (gen_mode){ if (knopka){ if(icr<65535) {icr++; } } else {  if(icr<=65525)icr+=10; }   }
 else {if (knopka) { if (enc>=2)enc--;  } else { if (enc>100) enc-=100; }  }
 } //end GetDown


if(gen_mode){ ICR1=icr; set_duty(); freq= (float)F_CPU/2 / ICR1;  } 
else { //расчёт прескалера и ICR по нужной частоте
divider=1; icr = F_CPU / enc /2 /divider;
if (icr >65536) { divider=8; icr = F_CPU / enc /2 /divider;
    if (icr >65536) { divider=64; icr = F_CPU / enc /2 /divider;
        if (icr >65536)  {divider=256; icr = F_CPU / enc /2 /divider;
            if (icr >65536) { divider=1024; icr = F_CPU / enc /2 /divider;
                if (icr >65536){icr=65536; }}}}} ICR1=icr-1; set_duty();
//запись в регистр прескалера            
switch (divider) {
  case 1: TCCR1B=1|(1<<WGM13); break;
   case 8: TCCR1B=2|(1<<WGM13); break;
    case 64: TCCR1B=3|(1<<WGM13); break;
     case 256: TCCR1B=4|(1<<WGM13); break;
      case 1024: TCCR1B=5|(1<<WGM13); break;  }

    freq= (float) F_CPU/2 / (ICR1+1) /divider;
  } //конец "если  не Gen_mode (частота менее 2848 герц)
} // конец enc_freq 


void enc_duty(uint8_t n){
 // Если  увеличение скважности
 if (n==4||n==2){if(duty<100) {duty++; }  }
 // Если уменьшение скважности
 if (n==6||n==0){ if(duty>0) {duty--; }}
 set_duty();
}

void loop() {
String freqstr, dutystr,modestr;

if (freq <10000) {
freqstr= String( String(freq,2)+ " Hz        ");  
}
if (freq >10000) {
freqstr= String( String((freq/1000),3)+ " KHz       ");  }
lcd.setFont(SmallFont);
//byte l= freqstr.length();
lcd.print(freqstr, LEFT, 0);

dutystr= String("Duty="+ String(duty)+ " %        ");  
lcd.print(dutystr, LEFT, 8);

enc_mode_flag? modestr=String("ENCMode=Freq") : modestr=String("ENCMode=Duty");  
lcd.print(modestr, LEFT, 16);
delay(100);// что-б слишком часто в дисплей не писало..
}


 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

спасибо. только под 1202 переделаю. кнопка без фиксации?

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

получается почти как у тебя

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Внёс рацпредложение от Максима в последний код, заодно исправил мелкий баг обратного перехода через частоту 2848. Если уменьшать по-герцу то на частоте 2847 перебрасывалось сразу на 2900, и так по кругу.  Проскочить можно было только щёлкнув в обратную сторону 100 герц.  Последняя версия..

/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер 
подключен к пинам A1 и A2, кнопка  энкодера подключена к A0. 
Требуется использовать конденсаторы  0,01..0,1uf  относительно земли 
на каждый вывод энкодера. Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
Дрова для 5110 взяты из  http://www.rinkydinkelectronics.com/library.php?id=44
*/
#include <LCD5110_Basic.h>
LCD5110 lcd(3,4,5,6,7);
extern uint8_t SmallFont[];
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
uint8_t duty=50;
float freq=0; 
volatile uint8_t enc_mode_flag=1; // По умолчанию энкодер регулирует частоту
static boolean gen_mode=0; //флаг режима управления

void setup() {
lcd.InitLCD();
lcd.setContrast(65); 
pinMode (9,OUTPUT); // выход генератора PB2
pinMode(A0,INPUT); // PC0 кнопка энкодера 
pinMode(A1,INPUT); // PC1 вывод энкодера
pinMode(A2,INPUT);  // PC2 вывод энкодера 
pinMode(2,INPUT_PULLUP); // подтяжка кнопки "режим энкодера"
PCICR=1<<PCIE1; //разрешить прерывание PCINT1
PCMSK1=1<<PCINT9;// По сигналу на А1 создавать прерывание
TCCR1A=1<<COM1A1; //подключить выход OC1A первого таймера
TCCR1B=0;//
EICRA=1<<ISC01; //int0 -falling mode
EIMSK=1; //int0 enable
}

ISR (INT0_vect){
  if ((PIND & (1<<2)) == 0)
      { enc_mode_flag = !enc_mode_flag; }
      }
ISR (PCINT1_vect){;
     uint8_t n=PINC&6; //считать значение энкодера частоты
     if (enc_mode_flag)  enc_freq(n); // если энкодер в режиме частоты
     if (!enc_mode_flag) enc_duty(n); // если энкодер в режиме скважности
     }


void enc_freq(uint8_t n){
static uint32_t enc=0; //переменная счёта энкодера
uint32_t icr=ICR1;
uint16_t divider=1; //переменная коэфф. деления прескалера
boolean knopka = PINC&(1<<0); // 0-кнопка нажата, 1-кнопка НЕ нажата.
// Если  увеличение частоты
if (n==4||n==2){ 
 if (gen_mode){if (knopka){ if(icr>2) {icr--; } } else {  if(icr>12)icr-=10; }   }
 else knopka?  enc++ : enc+=100; // в нч режиме
 } //end GetUP
// Если уменьшение частоты
if (n==6||n==0){ 
 if (gen_mode){ if (knopka){ if(icr<65535) {icr++; } } else {  if(icr<=65525)icr+=10; }   }
 else {if (knopka) { if (enc>=2)enc--;  } else { if (enc>100) enc-=100; }  }
 } //end GetDown
if(gen_mode){ ICR1=icr; set_duty(); freq= (float) F_CPU/2 / ICR1; enc=freq;} 
else { //расчёт прескалера и ICR по нужной частоте
 icr = (F_CPU / enc /2 /divider); 
 byte shifts[] = {3,3,2,2};
   for(byte i = 0; i < 4; i++){
      if (icr > 65536) {
          divider <<= shifts[i];
          icr = F_CPU / enc /2 /divider;
         }
    else {  //запись в регистр прескалера
        TCCR1B = (i+1)|(1<<WGM13);  break;
         }
    }
ICR1=icr-1; set_duty();
freq= (float) F_CPU/2 / (ICR1+1) /divider;
  } //конец "если  частота менее 2848 герц)

if (freq>2848) gen_mode=1; //переключение режима управления по OCR
else gen_mode=0; //переключение режима управления по частоте
} // конец enc_freq 


void enc_duty(uint8_t n){
 // Если  увеличение скважности
 if (n==4||n==2){if(duty<100) {duty++; }  }
 // Если уменьшение скважности
 if (n==6||n==0){ if(duty>0) {duty--; }}
 set_duty();
}

void loop() {
String freqstr, dutystr,modestr;

if (freq <10000) {
freqstr= String( String(freq,2)+ " Hz        ");  
}
if (freq >10000) {
freqstr= String( String((freq/1000),3)+ " KHz       ");  }
lcd.setFont(SmallFont);
lcd.print(freqstr, LEFT, 0);

dutystr= String("Duty="+ String(duty)+ " %        ");  
lcd.print(dutystr, LEFT, 8);

enc_mode_flag? modestr=String("ENCMode=Freq") : modestr=String("ENCMode=Duty");  
lcd.print(modestr, LEFT, 16);

delay(100);// что-б слишком часто в дисплей не писало..
}

 

Iwan73
Offline
Зарегистрирован: 02.11.2013

jeka_tm печаткой шилда не поделитесь народу ? 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

пожалуйста

https://yadi.sk/d/mYeiZu4jm8cRv

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Генератор версия 2.0

Позаимствована функция на ассемблере с алгоритмом DDS (отсюда),
с ней добавилось возможность генерить табличные формы сигналов
синуса, треугольника, пилы, можно добавить массив с другой формой сигнала,
свободного места во флэше полно. В качестве АЦП используется традиционная схема R2R. Под АЦП задействован весь порт D (выводы ардуино 0...7). Выход АЦП соединён с выходом таймера, в скетче порты соответствующим образом коммутируются. Точнось резисторов принципиальной роли не играет, 5% вполне достаточно. В идеале нужно ещё
добавить какой нибудь усилитель на скоростном  ОУ  (типа OP37), сделать смещение и регулировку амплитуды. Меня пока устраивает и без всего этого :)

Скетч:

/*Программа генератора с регулируемой частотой и скважностью для МК ATMEGA328
 * Для синтезатора частоты была позаимствована функция на ассемблере из источника
 * http://www.scienceprog.com/avr-dds-signal-generator-v20/ 
 */

const PROGMEM uint8_t  sinewave[]=
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};

const PROGMEM uint8_t squarewave[]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};

const PROGMEM uint8_t trianglewave[]=
{
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
};

const PROGMEM uint8_t sawtoothwave[]= 
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};

const PROGMEM uint8_t rewsawtoothwave[]=
{
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
};


const PROGMEM char musor_mass[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

// глобальные переменные
volatile int32_t freq=1000;// частота генератора по умолчанию, Герц 
volatile uint16_t shag=100; //шаг переключения частоты,значение по умолчанию
volatile uint8_t regim=5;// режим работы по умолчанию (0)синус DDS, (1)треугольник DDS,
//(2)прямоугольник DDS, (3)правая пила DDS, (4) левая пила DDS. (5) частота генерации таймером 
// (6) скважность генерации таймером
volatile uint8_t ad2,ad1,ad0; //байты частоты для функции на ассемблере
volatile uint8_t duty=50;//скважность (коэффициент заполнения), процентов по умолчанию  
volatile uint32_t icr=0; //переменная для работы с регистром ICR1

// DDS algorithm 
static inline void signalOUT(const uint8_t *signal){ 
   asm volatile(  "eor r18, r18   ;r18<-0"  "\n\t"
    "eor r19, r19   ;r19<-0"  "\n\t"
     "1:"            "\n\t"
      "add r18, %0  ;1 cycle"     "\n\t"
       "adc r19, %1  ;1 cycle"     "\n\t"  
        "adc %A3, %2  ;1 cycle"     "\n\t"
         "lpm      ;3 cycles"  "\n\t"
         "out %4, __tmp_reg__  ;1 cycle" "\n\t"
        "sbis %5, 7   ;1 cycle if no skip" "\n\t"     // если стоит бит в переменной %5, то
       "rjmp 1b    ;2 cycles. Total 10 cycles" "\n\t" //пропустить эту команду ухода в цикл
      :
     :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTD)), "I" (_SFR_IO_ADDR(PORTB))
    :"r18", "r19" );
    }

void setup(){
Serial.begin(115200); 
for (int n=0; n <sizeof(musor_mass); n++ ) {PORTD=musor_mass[n]; }   
  TCCR0B=0; //заглушить 0 таймер что б не мешал DDS
   PCICR=(1<<PCIE1)|(1<<PCIE0); //разрешить прерывания PCINT 0,1
    PCMSK1=1<<PCINT9;// По сигналу на А1 создавать прерывание (енкодер)
     PCMSK0=(1<<PCINT0)|(1<<PCINT2);// По сигналу на 8 , 10  пине ардуины создавать прерывание
      PORTB|=(1<<PB0)|(1<<PB2); // подтяжка 8,10 пин 
        TCCR1A=0; TCCR1B=0;
         check_regim(); //проверка и настройка режимов
         } //end setup

void up_down(boolean x){// функция режимов енкодера
// если режим генерации таймером и частота более 2800 Гц, то
if (TCCR1B==17 && ICR1<2800 && regim==5){ 
if(x) { if (icr<1000 && shag > 100) shag=100; 
        if (icr<100 && shag > 10) shag=10;
        if (icr<10 && shag > 1) shag=1;          
         icr-=shag;
          if (icr<2) icr=2;    }
       else {  if (icr > 1800 && shag >100) shag =100;
          icr+=shag ;   } 
               return; 
            }
if (regim==6){ // если енкодер в режиме регулировки скважности, то
 // если возможна регулировка с точностью  не хуже 1%, то:
        if (ICR1>100){ 
              // Если  увеличение скважности
              if (x){if(duty<100) {duty++; }  }
              // Если уменьшение скважности
              if (!x){ if(duty>0) {duty--; }}
               }
         else{ // если точность 1% уже невозможна, то будем крутить регистр OCR2A напрямую
                if (x){if(OCR1A<ICR1) {OCR1A++; }  }         
                 else {if(OCR1A>0) {OCR1A--; }  }         
               if(OCR1A>ICR1) OCR1A=ICR1-1;
                duty=(uint32_t)100*OCR1A/ICR1;                 
         }
     return;
  } //конец настройки режима скважности
// в остальных режимах DDS :
x? freq+=shag : freq-=shag;  
if (freq < 1) freq=1;
}

        ISR (PCINT1_vect){ // прерывание энкодера
        PORTD=0; //обнулить порт DDS при любых манипуляциях
        up_down((PINC&1)^((PINC&2)>>1)); //конечная обработка в другой функции  
        check_regim();//проверка и установка режимов генератора
         }

ISR (PCINT0_vect){ // прерывание обработчик кнопок
PORTD=0; //обнулить порт DDS при любых манипуляциях
  if ((PINB&(1<<0))==0){ // обработчик кнопки энкодера (8 пин arduino)
        while ((PINB&(1<<0))==0);// ждать пока отпустят кнопку
     if (regim==6) { regim=5; check_regim(); return; }
          switch (shag){ //установка шага регулировки  
                  case 1: shag=10;     break;
                   case 10: shag=100;    break;
                   case 100: shag=1000;   break;
                   case 1000: shag=1;  
                  }
               check_regim();
            } //конец если нажата кнопка энкодера
if ((PINB&(1<<2))==0){ //обработчик кнопки режимов (10 пин PB2)
   PORTD=0;
    while ((PINB&(1<<2))==0);// ждать пока отпустят кнопку
     regim++; if (regim==7) regim=0; 
     check_regim();
      }
}//end pcint0


void pwm_gen(){//функция программирования таймера1
 uint16_t divider=1; //переменная коэфф. деления прескалера
  icr = (F_CPU / freq /2 /divider); 
   byte shifts[] = {3,3,2,2};
     for(byte i = 0; i < 4; i++){
        if (icr > 65536) {
           divider <<= shifts[i];
           icr = F_CPU / freq /2 /divider;
           }
    else {  //запись в регистр прескалера
        TCCR1B = (i+1)|(1<<WGM13);  break;
         }
    }
 ICR1=icr-1; 
set_duty();
} //end pwm_gen

    
void loop() {
               if (regim <5){ PORTB&= ~(1<<7);// флаг ухода в цикл DDS
               DDRD=0xFF;//set D port as output
               uint32_t temp=(float)freq /0.095367431640625; //пересчёт частоты
               ad2=temp>>16; ad1=temp>>8; ad0=temp;
                 switch (regim){ 
                  case 0: signalOUT(sinewave); break;
                   case 1: signalOUT(trianglewave); break;
                    case 2: signalOUT(squarewave); break;
                     case 3: signalOUT(sawtoothwave); break;
                      case 4: signalOUT(rewsawtoothwave);
                       } //end switch
                     DDRD=0; PORTD=0;
                } //end if (regim<5)

 }


void monitor_out(){
 Serial.print("FReq=");
   if (freq <10000) { Serial.print(freq);   Serial.print("Hz"); }
   if (freq >10000) { Serial.print((float)freq/1000,3);   Serial.print("kHz"); }
 Serial.print("  Shag="); Serial.print(shag);
     switch(regim){ 
       case 0: Serial.print("   Sinus"); break;
        case 1: Serial.print("   Triangle"); break;
         case 2: Serial.print("   Meandr"); break;
        case 3: Serial.print("   Sawtooth"); break;
       case 4: Serial.print("   REWSawtooth"); break;
      case 5: Serial.print("   Square_Timer"); Serial.print("  Duty="); Serial.print(duty); Serial.print("% "); break;
    case 6: Serial.print("   Duty_Timer"); Serial.print("  Duty="); Serial.print(duty);Serial.print("% ");
     }
 Serial.println();

}//end monitor out


void set_duty(){
//float ocr,delta_ocr;
if (regim==6&&ICR1<100) return;
if (regim==5 && ICR1<100){
    OCR1A=ICR1/2; duty=50; 
    return;
     }
static uint16_t ocr;
ocr=(uint32_t)ICR1*duty/100;
 if (ocr < 1) { // если значение оср менее 1, то
             while (ocr<1) { // менять скважность пока оср не станет больше ноля
                           (duty>50)? duty-- : duty++ ; 
                           ocr=(uint32_t)ICR1*duty/100; 
                               }
               }
OCR1A=ocr;
}

void check_regim(){// 
if (regim <5){ if (freq > 100000) freq=100000;
    TCCR1B=0;//отключить генерацию таймера
    TCCR1A=0;  DDRB&=~(1<<DDB1); // 9 pin arduino set Z-mode 
    }// end if regim <5

  if (regim > 4) { 
      TCCR1A=1<<COM1A1; DDRB|=1<<DDB1; // 9 pin set output (pwm out)
      if (TCCR1B==17 && ICR1<2800){ 
              ICR1=icr; freq= (float) 8000000UL/ICR1;  
              set_duty();  
                    }
                 else { pwm_gen(); } // запуск функции вывода частоты через таймер      
         }
   PORTB|= 1<<7; //флажок для выхода из цикла DDS  
  monitor_out(); //вывод информации 
}

Тех. Характеристики генерации таймером:
Генерация прямоугольного сигнала  0..4 Мгц ,
 минимальный шаг регулировки частоты в диапазоне 0...2,8кГц - 1Гц
 свыше 2,8кГц минимальный шаг постепенно возрастает.
Регулировка коэффициента заполнения (скважности) 1..100%
в диапазоне 1Гц..80кГц регулировка производится с разрешением 1%
Свыше 80кГц разрешение (шаг) регулировки скважности увеличивается.
При изменении рабочей частоты в диапазоне 1Гц-80кГц выбранная скважность сохраняется,
а при изменении частоты свыше 80кГц  сбрасывается на 50 %,
но в режиме регулировки скважности её можно снова изменять.
 

Тех. Характеристики генерации сигнала через DDS:
Почему-то в оригинальной статье автора алгоритма DDS о характеристиках нет ни слова.
Указана только максимальная частота -65кГц. Откуда её взял автор непонятно, я поставил ограничение на 100кГц C увеличением частоты сильно падает разрешение получаемого сигнала на высоких частотах. А конкретно, в диапазонах:
0...6,25 кГц - разрядность от 256 до 128 градаций
6,25...12,5 кГц - разрядность от 128 до 64 градаций
12,5...25кГц -разрядность от 64 до 32 градаций
25кГц...50кГц -разрядность от 32 до 16 градаций
50кГц...100кГц -разрядность от 16 до 8 градаций
Помимо этого с увеличением частоты вырастает джиттер,
особенно заметно на сигналах с резкими фронтами (прямоугольник, пила).
Но на точно установленных частотах 6,25кГц ; 12,5кГц ; 25кГц; 50кГц; 100 кГц джиттера нет, их можно использовать для точных измерений. Остальные частоты для большинства применений тоже подойдут.
Но если нужен идеальный сигнал -то только генерация таймером.
Во всех диапазонах DDS минимальный шаг регулировки частоты - 1Гц.

Для сравнения синус 20кГц и синус 100кГц:

Управление: кнопка переключения режимов переключает последовательно
(0)синус DDS, (1)треугольник DDS, (2)прямоугольник DDS, (3)правая пила DDS,
(4) левая пила DDS. (5) частота генерации таймером (6) скважность генерации таймером

Кнопка на энкодере переключает шаг изменения частоты 1000-1-10-100
в режиме регулировки скважности(6) нажатие на кнопку переключает энкодер в режим (5).

Особенности:
(1) Функция на ассемблере использует не стандартный способ чтения массива, записанного через PROGMEM Для этого способа требуется что бы расположение всех таблиц-массивов было кратно адресу 0x100 В ардуино нет штатных средств что бы положить конкретный массив по конкретному адресу во флэш область, поэтому пришлось обойти этот недостаток не очень красивым способом.  Я создал специальный массив musor_mass, который занимает всё свободное место вплоть до адреса 0x100, а после него массивы с таблицами сами попадают точно на нужные адреса. Что бы компилятор не выбросил этот массив пришлось его как-то задействовать ( в строке 147 сетапа). При изменении в коде или при использовании версии ардуино отличной от 1.6.8 возможно потребуется изменение размера мусорного массива таким образом, что бы первый байт табличных массивов сел точно на адрес 0x100. Возможно в какой-то версии компилятора musor_mass нужно будет расположить в скетче не последним, а первым. После нажатия кнопки "проверить" лучше сразу заглянуть в скомпилившийся hex файл. Пример правильного расположения массивов на картинке.

(2) В функции на ассемблере использован свободный 7й бит регистра PORTB, нога физически отключена и задействавана под кварц, так что бит можно использовать в своих целях в качестве булевой переменной. А у автора оригинального DDS генератора в  аналогичных нетрадиционных целях задействован бит регистра SPCR (конфигурационный регистр SPI)

В данном скетче вывод информации происходит через Serial, в функции monitor_out() можно настроить вывод на свой дисплей, на плате ардуино ещё свободны 7 выводов (11,12,13,A2-A5).
 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Версия под дисплей NOKIA 5110

 

Дисплей подключен на пины A2-A5.  Других отличий от сериальной нет. Библа для дисплея LCD5510  от  ринки-динка, но подпатченная нашим форумчанином  ssvs111 что б дисплей работал на 4 пинах, ссылка на пропатченую версию была тут Скетч:

/*Программа генератора с регулируемой частотой и скважностью для МК ATMEGA328
 * Для синтезатора частоты была позаимствована функция на ассемблере из источника
 * http://www.scienceprog.com/avr-dds-signal-generator-v20/ 
 */

const PROGMEM uint8_t  sinewave[]=
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};

const PROGMEM uint8_t squarewave[]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};

const PROGMEM uint8_t trianglewave[]=
{
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
};

const PROGMEM uint8_t sawtoothwave[]= 
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};

const PROGMEM uint8_t rewsawtoothwave[]=
{
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
};


const PROGMEM char musor_mass[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

// глобальные переменные
volatile int32_t freq=1000;// частота генератора по умолчанию, Герц 
volatile uint16_t shag=100; //шаг переключения частоты,значение по умолчанию
volatile uint8_t regim=5;// режим работы по умолчанию (0)синус DDS, (1)треугольник DDS,
//(2)прямоугольник DDS, (3)правая пила DDS, (4) левая пила DDS. (5) частота генерации таймером 
// (6) скважность генерации таймером
volatile uint8_t ad2,ad1,ad0; //байты частоты для функции на ассемблере
volatile uint8_t duty=50;//скважность (коэффициент заполнения), процентов по умолчанию  
volatile uint32_t icr=0; //переменная для работы с регистром ICR1

#include <LCD5110_SSVS.h>
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
LCD5110 lcd(A5,A4,A3,A2);

// DDS algorithm 
static inline void signalOUT(const uint8_t *signal){ 
   asm volatile(  "eor r18, r18   ;r18<-0"  "\n\t"
    "eor r19, r19   ;r19<-0"  "\n\t"
     "1:"            "\n\t"
      "add r18, %0  ;1 cycle"     "\n\t"
       "adc r19, %1  ;1 cycle"     "\n\t"  
        "adc %A3, %2  ;1 cycle"     "\n\t"
         "lpm      ;3 cycles"  "\n\t"
         "out %4, __tmp_reg__  ;1 cycle" "\n\t"
        "sbis %5, 7   ;1 cycle if no skip" "\n\t"     // если стоит бит в переменной %5, то
       "rjmp 1b    ;2 cycles. Total 10 cycles" "\n\t" //пропустить эту команду ухода в цикл
      :
     :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTD)), "I" (_SFR_IO_ADDR(PORTB))
    :"r18", "r19" );
    }

void setup(){
lcd.InitLCD();
for (int n=0; n <sizeof(musor_mass); n++ ) {PORTD=musor_mass[n]; }   
  TCCR0B=0; //заглушить 0 таймер что б не мешал DDS
   PCICR=(1<<PCIE1)|(1<<PCIE0); //разрешить прерывания PCINT 0,1
    PCMSK1=1<<PCINT9;// По сигналу на А1 создавать прерывание (енкодер)
     PCMSK0=(1<<PCINT0)|(1<<PCINT2);// По сигналу на 8 , 10  пине ардуины создавать прерывание
      PORTB|=(1<<PB0)|(1<<PB2); // подтяжка 8,10 пин 
        TCCR1A=0; TCCR1B=0;
         check_regim(); //проверка и настройка режимов
         } //end setup

void up_down(boolean x){// функция режимов енкодера
// если режим генерации таймером и частота более 2800 Гц, то
if (TCCR1B==17 && ICR1<2800 && regim==5){ 
if(x) { if (icr<1000 && shag > 100) shag=100; 
        if (icr<100 && shag > 10) shag=10;
        if (icr<10 && shag > 1) shag=1;          
         icr-=shag;
          if (icr<2) icr=2;    }
       else {  if (icr > 1800 && shag >100) shag =100;
          icr+=shag ;   } 
               return; 
            }
if (regim==6){ // если енкодер в режиме регулировки скважности, то
 // если возможна регулировка с точностью  не хуже 1%, то:
        if (ICR1>100){ 
              // Если  увеличение скважности
              if (x){if(duty<100) {duty++; }  }
              // Если уменьшение скважности
              if (!x){ if(duty>0) {duty--; }}
               }
         else{ // если точность 1% уже невозможна, то будем крутить регистр OCR1A напрямую
                if (x){if(OCR1A<ICR1) {OCR1A++; }  }         
                 else {if(OCR1A>0) {OCR1A--; }  }         
               if(OCR1A>ICR1) OCR1A=ICR1-1;
                duty=(uint32_t)100*OCR1A/ICR1;                 
         }
     return;
  } //конец настройки режима скважности
// в остальных режимах DDS :
x? freq+=shag : freq-=shag;  
if (freq < 1) freq=1;
}

        ISR (PCINT1_vect){ // прерывание энкодера
        PORTD=0; //обнулить порт DDS при любых манипуляциях
        up_down((PINC&1)^((PINC&2)>>1)); //конечная обработка в другой функции  
        check_regim();//проверка и установка режимов генератора
         }

ISR (PCINT0_vect){ // прерывание обработчик кнопок
PORTD=0; //обнулить порт DDS при любых манипуляциях
  if ((PINB&(1<<0))==0){ // обработчик кнопки энкодера (8 пин arduino)
        while ((PINB&(1<<0))==0);// ждать пока отпустят кнопку
     if (regim==6) { regim=5; check_regim(); return; }
          switch (shag){ //установка шага регулировки  
                  case 1: shag=10;     break;
                   case 10: shag=100;    break;
                   case 100: shag=1000;   break;
                   case 1000: shag=1;  
                  }
               check_regim();
            } //конец если нажата кнопка энкодера
if ((PINB&(1<<2))==0){ //обработчик кнопки режимов (10 пин PB2)
   PORTD=0;
    while ((PINB&(1<<2))==0);// ждать пока отпустят кнопку
     regim++; if (regim==7) regim=0; 
     check_regim();
      }
}//end pcint0


void pwm_gen(){//функция программирования таймера1
 uint16_t divider=1; //переменная коэфф. деления прескалера
  icr = (F_CPU / freq /2 /divider); 
   byte shifts[] = {3,3,2,2};
     for(byte i = 0; i < 4; i++){
        if (icr > 65536) {
           divider <<= shifts[i];
           icr = F_CPU / freq /2 /divider;
           }
    else {  //запись в регистр прескалера
        TCCR1B = (i+1)|(1<<WGM13);  break;
         }
    }
 ICR1=icr-1; 
set_duty();
} //end pwm_gen

    
void loop() {
               if (regim <5){ PORTB&= ~(1<<7);// флаг ухода в цикл DDS
               DDRD=0xFF;//set D port as output
               uint32_t temp=(float)freq /0.095367431640625; //пересчёт частоты
               ad2=temp>>16; ad1=temp>>8; ad0=temp;
                 switch (regim){ 
                  case 0: signalOUT(sinewave); break;
                   case 1: signalOUT(trianglewave); break;
                    case 2: signalOUT(squarewave); break;
                     case 3: signalOUT(sawtoothwave); break;
                      case 4: signalOUT(rewsawtoothwave);
                       } //end switch
                     DDRD=0; PORTD=0;
                } //end if (regim<5)

 }


void monitor_out(){
String freqstr, dutystr,stepstr;
dutystr= String("Duty="+ String(duty)+ "%");  
//pwmstr=String("PWM Mode "+ String(duty)+ "%");                   
stepstr=String("Step= "+String(shag));
if (freq <10000) {freqstr= String("Frequency, Hz");  }
if (freq >10000) {freqstr= String("Frequency, kHz");  }
lcd.clrScr();
lcd.setFont(SmallFont);
lcd.print(freqstr,LEFT,0);
lcd.setFont(MediumNumbers);
if (freq <10000) { lcd.printNumI(freq, CENTER, 8); }
if (freq >10000u && freq < 1000000ul ) { lcd.printNumF( ((float)freq/1000),3 ,CENTER, 8);  }
if (freq >=1000000ul ) { lcd.printNumF( ((float)freq/1000),2 ,CENTER, 8);  }
lcd.setFont(SmallFont);
     switch(regim){ 
       case 0: lcd.print("Sinus DDS",CENTER, 32); break;
        case 1: lcd.print("Triangle DDS",CENTER, 32); break;
         case 2: lcd.print("Meandr DDS",CENTER, 32); break;
        case 3: lcd.print("Pila1 DDS",CENTER, 32); break;
       case 4: lcd.print("Pila2 DDS",CENTER, 32); break;
      case 5: lcd.print("PWM Mode",CENTER,32); break;
    case 6: lcd.print("Duty Mode", CENTER, 32);
     }
 if (regim==6) lcd.print(dutystr, CENTER, 40);
         else lcd.print(stepstr, CENTER, 40);
}//end monitor out


void set_duty(){
//float ocr,delta_ocr;
if (regim==6&&ICR1<100) return;
if (regim==5 && ICR1<100){
    OCR1A=ICR1/2; duty=50; 
    return;
     }
static uint16_t ocr;
ocr=(uint32_t)ICR1*duty/100;
 if (ocr < 1) { // если значение оср менее 1, то
             while (ocr<1) { // менять скважность пока оср не станет больше ноля
                           (duty>50)? duty-- : duty++ ; 
                           ocr=(uint32_t)ICR1*duty/100; 
                               }
               }
OCR1A=ocr;
}

void check_regim(){// 
if (regim <5){ if (freq > 100000) freq=100000;
    TCCR1B=0;//отключить генерацию таймера
    TCCR1A=0;  DDRB&=~(1<<DDB1); // 9 pin arduino set Z-mode 
    }// end if regim <5

  if (regim > 4) { 
      TCCR1A=1<<COM1A1; DDRB|=1<<DDB1; // 9 pin set output (pwm out)
      if (TCCR1B==17 && ICR1<2800){ 
              ICR1=icr; freq= (float) 8000000UL/ICR1;  
              set_duty();  
                    }
                 else { pwm_gen(); } // запуск функции вывода частоты через таймер      
         }
   PORTB|= 1<<7; //флажок для выхода из цикла DDS  
  monitor_out(); //вывод информации 
}

 

Лекс 59
Лекс 59 аватар
Offline
Зарегистрирован: 02.04.2016

Ну пока без энкодера. Проба пера. ЦАП и выходные цепи использую с ранее сделаного генератора с Паяльника, который меня не устроил.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Ну и для полной коллекции -последняя версия с более удобной распиновкой дисплея и кнопок для печатной платы. Так же вроде подправил все мелкие недочёты.

Из существенный отличий -использовал 8 ногу для включения/отключения подсветки. Либо можно что-то ещё включать, например сделать управляемое смещение, или переключать выход релюшкой , или ещё что - пригодится в любом случае. Включается комбинацией- нажать кнопку энкодера, + удерживая её нажать кнопку режима. Затем отпустить кнопку энкодера, и последней отпустить кнопку режима. Скетч:

/* Генератор с регулируемой частотой и скважностью на Ардуино Уно v 2.3
 * Распиновка: Энкодер A0,A1 (in)
 * Кнопка энкодера A3 (in)
 * Кнопка "режим" A2 (in)
 * Подсветка 8 (out) (вывод BL дисплея)
 * Дисплей nokia 5110 - 13,12,11,10 (out)  
 *  Выход генератора 0..7 + 9 (out)
 */ 


const PROGMEM uint8_t  sinewave[]= // массив синуса
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};

const PROGMEM uint8_t squarewave[]= //массив меандра
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};

const PROGMEM uint8_t trianglewave[]= //массив треугольника
{
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
};

const PROGMEM uint8_t sawtoothwave[]= //массив пила1
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};

const PROGMEM uint8_t rewsawtoothwave[]= //массив пила2
{
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
};


const PROGMEM char musor_mass[]= //массив для подгонки адреса в флэш-памяти
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};


volatile int32_t freq=1000;// частота по умолчанию
volatile uint16_t shag=100; //шаг энкодера по умолчанию
volatile uint8_t regim=5; //режим генератора по умолчанию
volatile uint8_t monitor_flag; // флаг для вывода на дисплей
volatile uint8_t ad2,ad1,ad0; //служебные байты для функции на ассемблере
volatile uint8_t duty=50; //скважность
volatile uint32_t icr=0;  //переменная для управления регистром сравнения таймера1

#include <LCD5110_SSVS.h>
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
LCD5110 lcd(10,11,12,13);

// DDS algorithm 
static inline void signalOUT(const uint8_t *signal){ 
   asm volatile(  "eor r18, r18   ;r18<-0"  "\n\t"
    "eor r19, r19   ;r19<-0"  "\n\t"
     "1:"            "\n\t"
      "add r18, %0  ;1 cycle"     "\n\t"
       "adc r19, %1  ;1 cycle"     "\n\t"  
        "adc %A3, %2  ;1 cycle"     "\n\t"
         "lpm      ;3 cycles"  "\n\t"
         "out %4, __tmp_reg__  ;1 cycle" "\n\t"
        "sbis %5, 7   ;1 cycle if no skip" "\n\t"     
       "rjmp 1b    ;2 cycles. Total 10 cycles" "\n\t" 
      :
     :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTD)), "I" (_SFR_IO_ADDR(PORTB))
    :"r18", "r19" );
    }

void setup(){
lcd.InitLCD();
for (int n=0; n <sizeof(musor_mass); n++ ) {PORTD=musor_mass[n]; }   
 PORTC|=(1<<PC2)|(1<<PC3); //подтяжка кнопок на A2 A3
  TCCR0B=0; TCCR1A=0; TCCR1B=0;
  DDRB|=1<<PB0; // 8 пин -включение подсветки дисплея = выход
   PCICR=(1<<PCIE1); //включить прерывание PCINT
    PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); //выбор пинов прерывания (A1,A2,A3)
      check_regim(); 
         } //end setup


void up_down(boolean x){ // управление регулировками

if (TCCR1B==17 && ICR1<2800 && regim==5){ 
if(x) { if (icr<1000 && shag > 100) shag=100; 
        if (icr<100 && shag > 10) shag=10;
        if (icr<10 && shag > 1) shag=1;          
         icr-=shag;
          if (icr<2) icr=2;    }
       else {  if (icr > 1800 && shag >100) shag =100;
          icr+=shag ;   } 
               return; 
            }
if (regim==6){ 
 
        if (ICR1>100){ 
             
              if (x){if(duty<100) {duty++; }  }
             
              if (!x){ if(duty>0) {duty--; }}
               }
         else{ 
                if (x){if(OCR1A<ICR1) {OCR1A++; }  }         
                 else {if(OCR1A>0) {OCR1A--; }  }         
               if(OCR1A>ICR1) OCR1A=ICR1-1;
                duty=(uint32_t)100*OCR1A/ICR1;                 
         }
     return;
  } 

x? freq+=shag : freq-=shag ;  
if (freq < 1) freq=1;
}

        
        
// ПРЕРЫВАНИЕ от кнопок и энкодера         
        ISR (PCINT1_vect){ 
        PCMSK1=0; PCICR=0; 
        PORTD=0;
   // если нажата кнопка энкодера     
  if ((PINC&(1<<3))==0){ 
        while ((PINC&(1<<3))==0);// подождать до тех пор, когда кнопку отпустят

      //блок вкл/отк подсветки дисплея (8 пин ардуино)
        if ((PINC&(1<<2))==0){ //если после этого нажата кнопка режимов,
         PINB|=1<<PB0; //менять состояние 8 пина
          while ((PINC&(1<<2))==0);// теперь подождать пока отпустят кнопку режим 
          PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1); return;
          }
     if (regim==6) { regim=5; check_regim(); PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1); return; }
          switch (shag){ 
                  case 1: shag=10;     break;
                   case 10: shag=100;    break;
                   case 100: shag=1000;   break;
                   case 1000: shag=1;  
                  }
               check_regim();
              PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1); return;
            } 
if ((PINC&(1<<2))==0){ // если нажата кнопка режимов
   PORTD=0;
    while ((PINC&(1<<2))==0);
     regim++; if (regim==7) regim=0; 
     check_regim(); PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11);PCICR=(1<<PCIE1);  return;
      }

// если кнопки не нажимались -значит крутили энкодер:
        up_down(  (PINC&1)^((PINC&2)>>1) ); //отправить считанное энкодеров в другую функцию
        // если при вращении счёт идёт не в нужную сторону, то вставить символ '!'   up_down(! 
        check_regim(); 
          PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);//включить прерывания

         } //конец функции обработки прерываний от кнопок PCINT1_vect 


void pwm_gen(){ //настройка таймера1
 uint16_t divider=1; 
  icr = (16000000ul / freq /2 /divider); 
   byte shifts[] = {3,3,2,2};
     for(byte i = 0; i < 4; i++){
        if (icr > 65536) {
           divider <<= shifts[i];
           icr = 16000000ul / freq /2 /divider;
           }
    else {  
        TCCR1B = (i+1)|(1<<WGM13);  break;
         }
    }
 ICR1=icr-1; 
set_duty();
} //end pwm_gen

    
void loop() {
   if (monitor_flag) { monitor_flag=0; monitor_out(); }
               if (regim <5){ PORTB&= ~(1<<7);
               DDRD=0xFF;//set D port as output
               uint32_t temp=(float)freq /0.095367431640625; 
               ad2=temp>>16; ad1=temp>>8; ad0=temp;
                 switch (regim){ 
                  case 0: signalOUT(sinewave); break;
                   case 1: signalOUT(trianglewave); break;
                    case 2: signalOUT(squarewave); break;
                     case 3: signalOUT(sawtoothwave); break;
                      case 4: signalOUT(rewsawtoothwave);
                       } //end switch
                     DDRD=0; PORTD=0;
              
                } //end if (regim<5)

 }


void monitor_out(){
String dutystr,stepstr;
dutystr= String("Duty="+ String(duty)+ "%");                
stepstr=String("Step= "+String(shag));
lcd.clrScr();
//Вывод первой строчки
lcd.setFont(SmallFont);
if (freq <10000) {lcd.print("Frequency, Hz",LEFT,0 );  }
if (freq >=10000) {lcd.print("Frequency, kHz",LEFT,0 );  }
//Вывод второй строчки
lcd.setFont(MediumNumbers);
if (freq <10000) { lcd.printNumI(freq, CENTER, 8); }
if (freq >=10000u && freq < 1000000ul ) { lcd.printNumF( ((float)freq/1000),3 ,CENTER, 8);  }
if (freq >=1000000ul ) { lcd.printNumF( ((float)freq/1000),2 ,CENTER, 8);  }
//Вывод третьей строчки
lcd.setFont(SmallFont);
     switch(regim){ 
       case 0: lcd.print("Sinus DDS",CENTER, 32); break;
        case 1: lcd.print("Triangle DDS",CENTER, 32); break;
         case 2: lcd.print("Meandr DDS",CENTER, 32); break;
        case 3: lcd.print("Pila1 DDS",CENTER, 32); break;
       case 4: lcd.print("Pila2 DDS",CENTER, 32); break;
      case 5: lcd.print("PWM Mode",CENTER,32); break;
    case 6: lcd.print("Duty Mode", CENTER, 32);
     }
//Вывод четвёртой строчки
 if (regim==6) lcd.print(dutystr, CENTER, 40);
         else lcd.print(stepstr, CENTER, 40);
}//end monitor out


void set_duty(){
//float ocr,delta_ocr;
if (regim==6&&ICR1<100) return;
if (regim==5 && ICR1<100){
    OCR1A=ICR1/2; duty=50; 
    return;
     }
static uint16_t ocr;
ocr=(uint32_t)ICR1*duty/100;
OCR1A=ocr;
}

void check_regim(){// проверка и установка режимов генератора
if (regim <5){ if (freq > 100000) freq=100000;
    TCCR1B=0;
    TCCR1A=0;  DDRB&=~(1<<DDB1); // 9 pin arduino set Z-mode 
    }// end if regim <5

  if (regim > 4) { 
      TCCR1A=1<<COM1A1; DDRB|=1<<DDB1; // 9 pin set output (pwm out)
      if (TCCR1B==17 && ICR1<2800){ 
              ICR1=icr; freq= (float) 8000000UL/ICR1;  
              set_duty();  
                    }
                 else { pwm_gen(); } 
         }
   PORTB|= 1<<7; 
monitor_flag=1;
}

Как и первый вариант генератора сделал в виде шилда для UNO.

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

dimax, спасибо, замечательное устройство.

Предполагаемую схему на ОУ для смещения нуля попробовал, но убедился, что LM358 для этих целей не подходит. Заказал LM833, а пока собрал схему в пректически неизменном варианте:

ftor
Offline
Зарегистрирован: 27.06.2016

dimax, спасибо

Кстати, не получаеться исправить в новой версии кода сам енкодер. Пробовал вставить исправления не получилось считает по два.

Етот генератор можно использовать как медицынский прибор при частоте 10Кгц и Синусе после усиления до 60В получиться аналог русского миостимулятора Мион-1

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ftor, фикс  двойного щелчка энкодера для кода из #215


void up_down(boolean x){ // управление регулировками
static boolean n=0;  if (n=!n){return;} //далее без изменений

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Дело было вечером, делать было нечего.. А сделать что-то хотелось. Решил добавить в поделку пару полезных функций:

Генератор V2.4, в код  добавлены частотометр и вольтметр.

Частотометр -измеряет  методом тактирования первого таймера от источника сигнала. Измеряемый диапазон: 1Гц ... 7,999 МГц*
* при превышении верхнего диапазона частотометр будет выдавать неправильные показания. Точность измерений зависит от точности установленного на плате кварцевого резонатора.
Вход подключать к пину D5, в идеале отключая от этой ноги резистор из схемы ЦАП. Но можно и не отключать, -работает и так.

Вольтметр - Измеряет постоянное напряжение на шине Vcc и на пинах A4, A5. Используется оверсемплинг до псевдо 14 бит разрешения. , Вольтметр самокалибруется по встроенному в М.К. И.О.Н. на 1,1 вольта.
Измеряемое напряжение от 0в до напряжения питания М.К. (Vcc), отображаемое разрешение -1мв.


При загрузке можно кнопками выбрать что запускать, вольтметр или частотометр. Генератор запустится через 5 секунд по умолчанию, если выбор не сделан. Выхода из выбранных режимов не предусмотрено, только перезапуск по питанию или ресетом.

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

Скетч:


/* Генератор, частотометр, вольтметр на Ардуино Уно v 2.4
 * Распиновка: Энкодер A0,A1 (in)
 * Кнопка энкодера A2 (in)
 * Кнопка "режим" A3 (in)
 * Подсветка 8 (out) (вывод BL дисплея)
 * Дисплей nokia 5110 - 13,12,11,10 (out)  
 * Выход генератора 0..7 + 9 (out)
 * Вход частотометра - 5 (in)
 * Входы вольтметра -А4,А5 (in)
*/ 


const PROGMEM uint8_t  sinewave[]= // массив синуса
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};

const PROGMEM uint8_t squarewave[]= //массив меандра
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};

const PROGMEM uint8_t trianglewave[]= //массив треугольника
{
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
};

const PROGMEM uint8_t sawtoothwave[]= //массив пила1
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};

const PROGMEM uint8_t rewsawtoothwave[]= //массив пила2
{
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
};


const PROGMEM char musor_mass[]= //массив для подгонки адреса в флэш-памяти
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};


volatile int32_t freq=1000;// частота по умолчанию
volatile uint32_t icr=0;  //переменная для управления регистром сравнения таймера1
volatile uint16_t shag=100; //шаг энкодера по умолчанию
volatile uint16_t int_tic=0; 
volatile uint8_t regim=5; //режим генератора по умолчанию
volatile uint8_t monitor_flag; // флаг для вывода на дисплей
volatile uint8_t ad2,ad1,ad0; //служебные байты для функции на ассемблере
volatile uint8_t duty=50; //скважность
volatile uint8_t main_flag=0;//флаг работа в режиме генератора или нет

#define int_on() PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
//включить прерывание PCINT1, выбор пинов прерывания A1,A2,A3
#define int_off() PCMSK1=0; PCICR=0; //отключить PCINT1
#include <LCD5110_SSVS.h>
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
LCD5110 lcd(10,11,12,13);

// DDS algorithm 
static inline void signalOUT(const uint8_t *signal){ 
   asm volatile(  "eor r18, r18   ;r18<-0"  "\n\t"
    "eor r19, r19   ;r19<-0"  "\n\t"
     "1:"            "\n\t"
      "add r18, %0  ;1 cycle"     "\n\t"
       "adc r19, %1  ;1 cycle"     "\n\t"  
        "adc %A3, %2  ;1 cycle"     "\n\t"
         "lpm      ;3 cycles"  "\n\t"
         "out %4, __tmp_reg__  ;1 cycle" "\n\t"
        "sbis %5, 7   ;1 cycle if no skip" "\n\t"     
       "rjmp 1b    ;2 cycles. Total 10 cycles" "\n\t" 
      :
     :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTD)), "I" (_SFR_IO_ADDR(PORTB))
    :"r18", "r19" );
    }

void setup(){
lcd.InitLCD();
for (int n=0; n <sizeof(musor_mass); n++ ) {PORTD=musor_mass[n]; }   
PORTD=0; DDRD=0; TCCR1A=0; TCCR1B=0; TIMSK1=0; 
PORTC|=(1<<PC2)|(1<<PC3); //подтяжка кнопок на A2 A3
DDRB|=1<<PB0; // 8 пин -включение подсветки дисплея = выход
main_screen(); //стартовое сообщение и выбор задач
//далее загрузка генератора по умолчанию
TCCR0B=0;  check_regim(); 
int_on();//включить прерывание PCINT1
main_flag=1;
} //end setup


void up_down(boolean x){ // управление регулировками
// static boolean n=0;  if (n=!n){return;} // снять ремарку для энкодеров с двойным щелчком
if (TCCR1B==17 && ICR1<2800 && regim==5){ 
if(x) {if (icr<1000 && shag > 100) shag=100; 
       if (icr<100 && shag > 10) shag=10;
       if (icr<10 && shag > 1) shag=1;          
       icr-=shag;
         if (icr<2) icr=2;    }
         else {  if (icr > 1800 && shag >100) shag =100; icr+=shag ;  } 
  return;   }
if (regim==6){if (ICR1>100){ 
                if (x){if(duty<100) {duty++; }  }
                if (!x){ if(duty>0) {duty--; }} }
              else{ 
                    if (x){if(OCR1A<ICR1) {OCR1A++; }  }         
                    else {if(OCR1A>0) {OCR1A--; }  }         
                    if(OCR1A>ICR1) OCR1A=ICR1-1;
                    duty=(uint32_t)100*OCR1A/ICR1;  }
         return;
  } 
x? freq+=shag : freq-=shag ;  
if (freq < 1) freq=1;
}

        
////////////////////////////////////////////////////////////////        
//****** ПРЕРЫВАНИЕ от кнопок и энкодера*******/////////////////         
 ISR (PCINT1_vect){ 
 int_off();  PORTD=0;
 ///блок для обработки событий не в режиме генератора///
if (main_flag==0) { if ((PINC&(1<<3))==0){ while ((PINC&(1<<3))==0); //вкл. подсветки
                  if ((PINC&(1<<2))==0){ PINB|=1<<PB0; while ((PINC&(1<<2))==0);}
                  int_on(); return; //выходить если не в режиме генератора 
                  }}
 //далее всё в режиме генаратора
 // если нажата кнопка энкодера     
 if ((PINC&(1<<3))==0){ 
        while ((PINC&(1<<3))==0);// подождать до тех пор, когда кнопку отпустят
      //блок вкл/отк подсветки дисплея (8 пин ардуино)
        if ((PINC&(1<<2))==0){ //если после этого нажата кнопка режимов,
         PINB|=1<<PB0; //менять состояние 8 пина
          while ((PINC&(1<<2))==0);// теперь подождать пока отпустят кнопку режим 
          int_on(); return;
          }
     if (regim==6) { regim=5; check_regim(); int_on(); return; }
          switch (shag){ 
                   case 1: shag=10;     break;
                   case 10: shag=100;    break;
                   case 100: shag=1000;   break;
                   case 1000: shag=1; break; 
                  } check_regim(); int_on(); return;
            } //конец блока *если нажата кнопка энкодера*
if ((PINC&(1<<2))==0){ // если нажата кнопка режимов
   PORTD=0;
    while ((PINC&(1<<2))==0);
     regim++; if (regim==7) regim=0; 
     check_regim(); int_on();  return;
      } //конец блока *если нажата кнопка режимов*
// если кнопки не нажимались -значит крутили энкодер:
        up_down( ! (PINC&1)^((PINC&2)>>1) ); //отправить считанное энкодеров в другую функцию
        // если при вращении счёт идёт не в нужную сторону, то (вставить/убрать) символ '!'   up_down(! 
        check_regim(); int_on();
         } //конец функции обработки прерываний от кнопок PCINT1_vect 
////////////////////КОНЕЦ_ПРЕРЫВАНИЕ_от_кнопок_и_энкодера///////////////////////////////


void pwm_gen(){ //настройка таймера1
 uint16_t divider=1; 
  icr = (16000000ul / freq /2 /divider); 
   byte shifts[] = {3,3,2,2};
     for(byte i = 0; i < 4; i++){
        if (icr > 65536) {
           divider <<= shifts[i];
           icr = 16000000ul / freq /2 /divider; }
        else {  TCCR1B = (i+1)|(1<<WGM13);  break;  } }
ICR1=icr-1; 
set_duty();
} //end pwm_gen

    
void loop() {
   if (monitor_flag) { monitor_flag=0; monitor_out(); }
               if (regim <5){ PORTB&= ~(1<<7);
               DDRD=0xFF;//set D port as output
               uint32_t temp=(float)freq /0.095367431640625; 
               ad2=temp>>16; ad1=temp>>8; ad0=temp;
                 switch (regim){ 
                  case 0: signalOUT(sinewave); break;
                   case 1: signalOUT(trianglewave); break;
                    case 2: signalOUT(squarewave); break;
                     case 3: signalOUT(sawtoothwave); break;
                      case 4: signalOUT(rewsawtoothwave);
                       } //end switch
                     DDRD=0; PORTD=0;
              
                } //end if (regim<5)
  }//end loop


void monitor_out(){
String dutystr,stepstr;
dutystr= String("Duty="+ String(duty)+ "%");                
stepstr=String("Step= "+String(shag));
lcd.clrScr();
//Вывод первой строчки
lcd.setFont(SmallFont);
if (freq <10000) {lcd.print("Frequency, Hz",LEFT,0 );  }
if (freq >=10000) {lcd.print("Frequency, kHz",LEFT,0 );  }
//Вывод второй строчки
lcd.setFont(MediumNumbers);
if (freq <10000) { lcd.printNumI(freq, CENTER, 8); }
if (freq >=10000u && freq < 1000000ul ) { lcd.printNumF( ((float)freq/1000),3 ,CENTER, 8);  }
if (freq >=1000000ul ) { lcd.printNumF( ((float)freq/1000),2 ,CENTER, 8);  }
//Вывод третьей строчки
lcd.setFont(SmallFont);
     switch(regim){ 
       case 0: lcd.print("Sinus DDS",CENTER, 32); break;
        case 1: lcd.print("Triangle DDS",CENTER, 32); break;
         case 2: lcd.print("Meandr DDS",CENTER, 32); break;
        case 3: lcd.print("Pila1 DDS",CENTER, 32); break;
       case 4: lcd.print("Pila2 DDS",CENTER, 32); break;
      case 5: lcd.print("PWM Mode",CENTER,32); break;
    case 6: lcd.print("Duty Mode", CENTER, 32);
     }
//Вывод четвёртой строчки
 if (regim==6) lcd.print(dutystr, CENTER, 40);
         else lcd.print(stepstr, CENTER, 40);
}//end monitor out


void set_duty(){
if (regim==6&&ICR1<100) return;
if (regim==5 && ICR1<100){
    OCR1A=ICR1/2; duty=50; 
    return;   }
static uint16_t ocr;
ocr=(uint32_t)ICR1*duty/100;
OCR1A=ocr;
}

void check_regim(){// проверка и установка режимов генератора
if (regim <5){ if (freq > 100000) freq=100000;
    TCCR1B=0;
    TCCR1A=0;  DDRB&=~(1<<DDB1); // 9 pin arduino set Z-mode 
    }// end if regim <5
  if (regim > 4) { 
      TCCR1A=1<<COM1A1; DDRB|=1<<DDB1; // 9 pin set output (pwm out)
      if (TCCR1B==17 && ICR1<2800){ 
              ICR1=icr; freq= (float) 8000000UL/ICR1;  
              set_duty();                      }
       else { pwm_gen(); } 
         }
   PORTB|= 1<<7; 
monitor_flag=1;
}


ISR (TIMER1_OVF_vect){ int_tic++; } // прерывание частотомера

void freq_meter(){
   lcd.clrScr();
   lcd.setFont(SmallFont);
   lcd.print("Freq.counter",LEFT, 0);
  int_on();//включить прерывание PCINT1
  TIMSK1 = 1<<TOIE1;// подключить прерывание
  uint32_t freqm=0;// Переменная измерения частоты
  int_tic=0; TCNT1=0; TIFR1=0;//всё обнулить
while(1){
   TCCR1B=7;//тактировани от входа Т1
   delay(2000); TCCR1B=0;
   freqm= (((uint32_t)int_tic<<16) | TCNT1)/2; //сложить что натикало
   int_tic=0; TCNT1 = 0; 
   lcd.clrScr(); lcd.setFont(SmallFont);
   lcd.print("Freq.counter",LEFT, 0);
   lcd.setFont(MediumNumbers);
   if (freqm <10000) { lcd.printNumI(freqm, CENTER, 8); lcd.setFont(SmallFont); lcd.print("Herz",CENTER, 32 ); }
   if (freqm >=10000u && freqm < 1000000ul ) { lcd.printNumF( ((float)freqm/1000),3 ,CENTER, 8);lcd.setFont(SmallFont);  lcd.print("KiloHerz",CENTER, 32 ); }
   if (freqm >=1000000ul ) { lcd.printNumF( ((float)freqm/1000000ul),3 ,CENTER, 8); lcd.setFont(SmallFont); lcd.print("MegaHerz",CENTER, 32 ); }    
   }  
 }

//////////////////////////////////////////////////////////////////////
void volt_meter() {
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
ADCSRB=0; DIDR0=48; int_on();
float ain,vcc;  String ainstr,vccstr;
lcd.clrScr(); lcd.setFont(SmallFont);
lcd.print("Volt meter",LEFT, 0);
while(1){
ADMUX = (1<<REFS0)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1); //Vcc measure
delay(1); vcc=(float)(1.1*65472) / analog_func();
vccstr=String("Vcc= "+String(vcc,3)+" v  ");
lcd.print(vccstr,LEFT, 40);
ADMUX = (1<<REFS0)|(1<<MUX2); //A4 measure
delay(1); ain= analog_func()*vcc /65472 ; 
ainstr=String("Ain4= "+String(ain,3)+" v  ");
lcd.print(ainstr,LEFT, 16);
ADMUX = (1<<REFS0)|(1<<MUX2)|(1<<MUX0); //A5 measure
delay(1); ain= analog_func() *vcc /65472 ; 
ainstr=String("Ain5= "+String(ain,3)+" v  ");
lcd.print(ainstr,LEFT, 24);
  };//end while
}//end volt meter

uint32_t analog_func(){
uint32_t adc_buff=0;
for (int n=0; n<=4095; n++ ) {
    ADCSRA |= (1<<ADSC);  
    while (bit_is_set(ADCSRA,ADSC));
    adc_buff += ADC;
   } return adc_buff>>=6;
}

/////////////////////////////////////////////////////////////////
void main_screen(){
static boolean flag_exit =0; uint8_t n=84;
lcd.setFont(SmallFont);
lcd.print("Freq.counter >",LEFT, 8 );
lcd.print("**************",LEFT,24);
lcd.print("  Volt meter >",LEFT, 40 );
while(flag_exit==0) { //ждать нажатия кнопки режимов.
delay(350); lcd.clrRow(3,n); n-=6;  
if (n==0) flag_exit=1;
if ((PINC&(1<<2))==0){ // если нажата кнопка режимов
                      while ((PINC&(1<<2))==0);
                      freq_meter();   }
if ((PINC&(1<<3))==0){ // если нажата кнопка энкодера
                      while ((PINC&(1<<3))==0);
                      volt_meter();   }
        }// end while
}// end main_screen

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

нет ничего такого. бери исходники димакса, выбери плату уно например и заливай с помощью программатора и все))

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

+ не тестировал, просто включил. вроде работает, экран показывает, режимы переключаются

man_200
Offline
Зарегистрирован: 29.04.2016

treur
Offline
Зарегистрирован: 18.10.2015

Доброго времени суток dimax,

воспользовался кодом из поста #6 и появилось 2 вопроса:

1. Даю генератору частоту 0Hz, результат - если сразу после запуска, то не генерит, если до этого была дана частота, генерит предыдущую заданную. Как заставить остановиться по 0?

2. Есть ли какая возможность перевесить выход с D9 на D3?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

"Терминальная" версия генератора. Запустить терминалку, послать требуемую частоту в герцах. В ответ напишется та частота, которую смог сделать МК.

void setup() {
Serial.begin(9600);
pinMode (9,OUTPUT); // выход генератора
TCCR1A=0;TCCR1B=0;
}
void loop() {
static uint32_t reqfreq=0; //переменная запроса частоты
uint32_t ocr=OCR1A;  uint16_t divider=1;  float freq; 
if (Serial.available() > 0){ reqfreq = Serial.parseInt(); 
if (reqfreq==0 || reqfreq>F_CPU/2) {return;}
 ocr = (F_CPU / reqfreq /2 /divider); 
  byte shifts[] = {3,3,2,2};
   for(byte i = 0; i < 4; i++){
     if (ocr > 65536) { divider <<= shifts[i];
       ocr = F_CPU / reqfreq /2 /divider; }
      else { TCCR1B = (i+1)|(1<<WGM12);  break; }  } //Mode4 (CTC)
     OCR1A=ocr-1; TCCR1A=1<<COM1A0;
    freq= (float) F_CPU/2 / (OCR1A+1) /divider;
  if (freq <10000) { Serial.print(freq,1);Serial.println(" Hz "); }
 if (freq >=10000) { Serial.print(freq/1000,3);Serial.println(" kHz");}
 }
} 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Сделал новую версию генератора полностью на новом железе. (Все картинки кликабельны).

На написание ушёл месяц -учитывая что было освоение доселе неизвестного МК, а программа вышла под 400 строк - то наверное это не долго :) В качестве контроллера использована плата на МК STM32F103C8T6, в качестве среды программирования всё тот-же Arduino IDE, но с установленным аддоном для stm32. О плате, и о том как поставить аддон и зашить в неё USB-бутлоадер красочно рассказал наш коллега HWMan, так что освещать этот вопрос не буду. Так же использовал современный TFT дисплей на контроллере st7735, что-б устройство было посимпатичнее. Строка для поиска дисплея на Али 1.8" inch TFT LCD Display module ST7735S 128x160 От кнопки для переключения режимов отказался. Теперь всё делает энкодер. Из-за особенностей обработчика нужно что-бы энкодер выдавал на один щелчок один или два полных импульса. Классический энкодер ардуинщиков ky-040 выдаёт половинку импульса, поэтому крайне не рекомендуется. Он работать будет, но срабатывать станет через щелчок. Строка для поиска на Али: "Rotary encoder EC11"

 

                                        Тех. характеристики:

Генерация прямоугольного сигнала от 1Гц до 36МГц.  От 1Гц  д   8кГц  возможен шаг изменения  1Герц, далее мин.шаг увеличивается в соответствии с делением тактовой частоты (72МГц) на целочисленные делители. Для понимания масштаба: на 20кГц шаг уже 6 Герц, на 50 кГц -35гц, на 100кГц - 140Гц на 500кГц - шаг уже 3,5кГц. на 1МГц -шаг 14кГц. Последние частоты в шкале возможных, на которых будет работать генератор такие: 8Мгц, 9МГц, 10.286МГц,12МГц, 14.4МГц, 18МГц, 24МГц, 36МГц Регулировка шага изменения частоты. 1-10-100-1000 Герц В pwm-режиме на частотах свыше 8кГц шаг уже не означает чёткую привязку к Герцам, т.к. шаг тут перепривязан к делителю таймера. Регулировка заполнения( далее duty) в процентах. До частоты 720кГц с шагом 1%. При изменении частоты выбранный duty сохраняется. Выше 720кГц шаг начинает возрастать, но вращение энкодера по прежнему меняет по 1% , т.е. возникает пустой ход. Этот момент я не исправлял в связи с ненадобностью такой регулировки на высоких частотах. duty при регулировке частоты свыше 720кГц сбрасыватся на 50%, но в режиме регулировки duty его снова можно изменить. В режиме DDS возможен выбор следующих форм сигналов: синус, треугольник, пила двух видов, меандр. В этом режиме шаг чётко соответсвует фактическому шагу в герцах. Частота условно ограничена на 200кГц. Ограничение можно изменить в 259 строке. До 100кГц сигнал вполне приличный, без существенных изломов. О фактической разрядности DDS уточнение будет ниже. На осцилограмме синус 150кГц

Частотометр. Измеряемый диапазон от 1Герца до 32МГц. Менее одного герца -покажет 0. Более 33МГц начнёт врать. Относительная точность измерения на частотах до 1кГц -примерно 0,1Гц Выше 1кГц -1 Герц. Вольтметр как в версии 2.4 не стал делать за не надобностью. Да и встроенного опорника в этом МК нету. Но свободный аналоговый вход если что остался (PB1). Теоретически возможно дописать в устройство и функцию осциллографа, - благо и флеша и памяти ещё очень много. Но мне он не нужен, а в образовательных целях ломливо.. так что функционально проект развиваться дальше не будет :(

                                    Интерфейсы.

Устройство имеет: вход частотометра. Ввиду отсутствия каких-либо входных цепей подразумевается подача TTL уровней (размахом 3..5 вольт). Два комплементарных выхода таймера (A8 и A7). И выход DDS. Один из комплементарных выходов таймера (А7) физически сидит на старшем бите порта АЦП, поэтому в PWM Mode можно снимать сигнал таймера как с пина A7, так и с выхода R2R АЦП, но стоит учитывать, что фронты на высоких частотах будут сильно сглаживаться. Поэтому для работы с В.Ч. предпочтительней использовать отдельный выход с пина. Выход PWM2 по умолчанию негатив, его можно изменить на позитив в первом дефайне программы. Комплементарные выходы можно сделать с настраиваемым dead-таймом, но за отсутствием надобности и в связи с тем, что это не увязывается с регулировкой заполнения, я в программе не использовал такую возможность. Но пример использования оставил в закомментированной строчке 293. В ней максимальный дидтайм, и выглядит это вот так:

На дисплее отображается название режима, частота. В режимах PWMMode, Duty, Freq.meter так же отображатся условная осциллограмма , тайминги, и процент заполнения. В DDS режимах отображается статическая картинка режима. Так же добавлен электромагнитный излучатель (без встроенного генератора!) для "озвучивания" энкодера. Каждое переключение (частота/режим/ шаг) сопровождается звуковым сигналом соответствующей тональности. С озвучкой работа с генератором стала удобнее. Уровни выходных сигналов: С выходов PWM -стандартный TTL 3.3v . С выхода DDS 1,2v амплитудное (0,9v rms) -это конечно маловато, но я не вспомнил случаев когда мне требовалось напряжение более 1 вольта. А вот менее вольта -часто требовалось, поэтому установил ещё подстроечный резистор на всякий случай. Если его не ставить, то напряжение будет выше. В идеале конечно стоит установить ОУ на выход DDS что б усилить сигнал по напряжению и по току. Мне было лень..) Монтаж: когда развёл в спринте половину платы то понял, что кол-во перемычек уже превышает здравый смысл. Нужно было перерисовывать на 2х сторонний вариант. Это показалось долгим и нудным, поэтому сделал просто на макетке, и все соединения МГТФом. На разведённой печатке только матрица R2R.

                           Управление:

Вращение ручки энкодера меняет частоту с установленным шагом. Если вращать с нажатой ручкой то будет меняться режим работы Если просто нажать и отпустить ручку - меняется шаг. Если в режиме Duty нажать и отпустить -перейдёт обратно в режим PWM Mode

                              Схема:

Что бы не делать кучу разъёмов можно использовать реле, которое будет коммутировать выходы на разъём в зависимости от текущего режима.  

                             Программа:

pwm генератор работает на тех-же принципах что и в предыдущей версии, изменения коснулись сущих мелочей. DDS генератор благодаря 32-битному МК упростился, теперь вставка на ассемблере не нужна. Алгоритм написан буквально в одной строчке на Си. Синус и прочие формы рассчитываются перед запуском режима автоматически и записываются в буфер на 512 байт. Скорость отправки на R2R АЦП примерно 3M Samples/sec Что в два раза больше, чем было на меге328. Соответссно полная таблица wave-формы может считываться до частоты ~6кГц На более высоких частотах алгоритм начинает пропуски элементов таблицы. Нетрудно подсчитать число фактических градаций сигнала: нужно 3М разделить на частоту сигнала. Частотометр изменяет частоту в два захода. В первом измеряется количество тактов входящего сигнала за 1 секунду - в этом режиме трудятся в симбиозе аж три таймера. Первый даёт разрешение на счёт в течении 1 секунды второму таймеру. Второй досчитав до 65535 даёт такт третьему таймеру, а сам продолжает с ноля. Всё без использования прерываний. Таким образом суммарное разрешение таймеров в этом режиме 32 бита. Если измеренная частота оказалась ниже 1МГц, то вторым этапом измеряется длина единицы и ноля, вычисляется duty. Я не придавал особого значения точности, второй режим как дополнительный для украшения дисплея псевдо-осциллограмкой, которая отображает только соотношения распознанных 0 и 1 , но не форму сигнала. Но частоты менее 1кГц он измеряет точнее чем первый проход. Поэтому как конечный результат измерения в этом случае выводится результат второго захода. Программа написана с использованием возможностей Maple Library Это библиотека поддержки перефирии STM32 от компании Лифлабс, производителя ардуино-образных плат на мк STM, которая уже входит в состав аддонa для Arduino IDE. Для дисплея используется две библиотеки. Одна (Adafruit_GFX) уже есть в составе аддона. Вторую нужно поставить дополнительно, брать тут.



/*Генератор с регулируемой частотой v3.0 (C)Dimax */
#define pwm2_polar 0 //полярность выхода PWM2
 #define paper        0x000000 // цвет фона экрана
  #include <Adafruit_GFX_AS.h>    // Core graphics library
   #include <Adafruit_ST7735.h> // Hardware-specific library
    #include <SPI.h> 
     Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10); 
      volatile int enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
       volatile int encstep=1; //шаг изменения частоты по умолчанию 
      volatile float freq=1000; //частота по умолчанию
    volatile float duty_out;// переменная счёта скважности
   float t_hi, t_low; //переменные счёта длины импульсов
  uint8_t wave[512]; //массив для DDS синтеза
byte sine_logo[]={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() {
         SPI.setModule(2);// выбор SPI2
        tft.initR(INITR_BLACKTAB);
       tft.setRotation(3);//дисплей горизонтально, контакты слева
      tft.fillScreen(paper);//залить цветом по умолчанию
    tft.setTextWrap(0);//не переосить строки
   nvic_irq_disable_all();//отключить все прерывания
  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
  pinMode(PB0,PWM); //buzzer
   pinMode(PB6,INPUT_PULLUP);//enc
    pinMode(PB7,INPUT_PULLUP);//enc
     pinMode(PB8,INPUT_PULLUP);//key enc
      mytone(1000,50);//сигнал после старта
///////// Таймер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; 
 timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
TIMER4_BASE->CR1|=(1<<0);//запуск 
//прерывание кнопки энкодера
 attachInterrupt(PB8, key_enc_int, RISING);
  timer_set();
   }



void loop() {
static int old_mode_loop=-1;
if (freq > 8485) {tim_mode=1;} else {tim_mode=0;}// переключать режимы таймера
  if (mode!=old_mode_loop) {
   tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
     }//чистить полностью экран только при смене режимов
    if (mode==7) {mon_out(); freq_meter();  }
   if (mode >1 && mode<7) { mon_out();  dds_set(); } // запуск DDS режимов
 if (mon_flag) { 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=1999; // 72000000/20000= 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>1E6){ t_low=0;t_hi=0; duty_out=50;return;} //выйти если freq больше мегагерца
   // Перенастройка таймера 2 в режии измерения длительности импульса и скважности
 divider=1;                                  
while ((72E6/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; //перечитать регистры.
       TIMER2_BASE->CR1=(1<<0);
      __asm volatile( "cpsie i" );
     while( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile("nop");  if(mon_flag) {return;} }
    TIMER2_BASE->CR1=0;// стоп таймер
   imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
  imp_hi=(uint32_t)  ((TIMER2_BASE->CCR2)*divider);
 if (freq <1000){ freq=72E6 /imp_long;} //если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
duty_out=  (float) imp_hi / (imp_long / 100.0) ;
 t_low= (imp_long-imp_hi) /72.0 ;
  t_hi=  imp_hi /72.0;
   mon_flag=1;
   } //END freq meter


///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[10]; float freq_out;
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print("  PWM Mode   "); break;
       case 1: tft.print("  Duty Mode  "); break;
       case 2: tft.print("  Sinus DDS  "); break;
       case 3: tft.print(" Triangle DDS"); break;
       case 4: tft.print("  Pila1 DDS  "); break;
       case 5: tft.print("  Pila2 DDS  "); break;
       case 6: tft.print("  Meandr DDS "); break;
       case 7: tft.print(" Freq. meter "); break;
       }
 //*****************Вывод второй строчки*****************************
        tft.setTextColor(ST7735_WHITE, paper);
         tft.setTextSize(3);   tft.setCursor(0, 19);
           if (freq >=1E6) { freq_out=freq/1E6;}
             else if (freq>=1E4) { freq_out=freq/1000;}
              else { freq_out=freq;}              
              if (mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print("   "); } 
             else {dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}    
  //********************Вывод третьей строчки*****************************
                tft.setCursor(50, 43); //  вперёд, вниз
               tft.setTextColor(ST7735_RED,paper);
              if(freq>=1E6){tft.print("MHz");}
             else if (freq>=1E4){tft.print("kHz");}
           else {tft.print(" Hz");}  
  //********************* "осциллограммы"******************************
     tft.fillRect(5,90, 100,38,paper);//вправо, вниз, ширина вправо, длина вниз
     tft.drawRect(0,67, 160,61,ST7735_MAGENTA);//вправо, вниз, ширина вправо, длина вниз
      if (mode<2 || mode==7){
        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==2){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==2)
                       else if (mode==3){// логотип треугольника                         
                      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==3
                         else if (mode==4){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==3)
                            else if (mode==5){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==4)
                                else if (mode==6){ //логотип меандра
                                  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 < 2 || mode==7){ 
               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==7)
         if (mode!=7){ // выводить шаг кроме частотометра
         tft.setCursor(114, 95); tft.print("Step=");
       tft.setCursor(114, 105); 
       switch (encstep) { 
         case 1: tft.print("   1"); break;
        case 10: tft.print("  10"); break;
       case 100: tft.print(" 100");break;
      case 1000: tft.print("1000");break;
       }// END switch case        
        } // END if (mode!=7)
if ( freq < 1)  {tft.fillRect(1,68, 158,59,paper); }
mon_flag=0;
}//END mon_out




//обработчик прерываний энкодера
void enc_int(){   
  if((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);} 
  else {enc_mode(-1);} 
  }// END VOID


// ФУНКЦИЯ конфигурации режимов 
void enc_mode(int in){
  modebit= (GPIOB_BASE->IDR&0x100); //состояние кнопки. 0-нажата 
   if (!modebit) { 
    mytone(880,30); //звук переключения режимов
     mode+=in; 
     if(mode>7){mode=7;}
      if(mode<0){mode=0;} 
       if (mode<2){timer_set();}
        if (mode==7) {freq=0;}
         mon_flag=1; 
         return; 
          } 
         mytone(4400,10); //звук изменения частоты
       switch(mode){
      case 0:   if (tim_mode==0) { freq+=(encstep*in); timer_set(); }   else { timer_hi_set(in); } break;
     case 1:   duty_in+=in; if(duty_in>99){duty_in=99;} if(duty_in<1){duty_in=1;} set_duty(); calc_freq();  break;  
    case 7:   break; // в частотометре не реагировать на вращение энкодера 
   default: freq+=(encstep*in); mon_flag=1; //wave режимы
   // проверка на корректность запроса частоты
  if (freq<0){freq=0;} if (freq>2E5) {freq=2E5;}
  } //end switch case
}//end enc_mode


// обработчик кнопки энкодера
void key_enc_int(){
  if(!modebit){modebit=1; mon_flag=1;return;}// если менялся режим -выйти  
   if (mode==1) { mytone(880,30); mode=0;  mon_flag=1; ;return;} //сменить режим и выйти если были в duty mode
     mytone(220,75); //звук переключения шага
        switch (encstep){ 
          case 1: encstep=10;  break;
            case 10: encstep=100;   break;
            case 100: encstep=1000; break;
          case 1000: encstep=1;  break; 
         } 
    mon_flag=1; //флаг вывода на дисплей
}//end 

void set_duty(){
if (mode==0 && TIMER1_BASE->ARR<100){ 
  TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; 
  duty_in=50;  return;   
  }
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
}

//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
void timer_set(){
        GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
      GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
     TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1;  TIMER1_BASE->CR1=1;
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
 if (freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1; return;} 
 if (freq>36E6) {freq=36E6;}
   divider=1;
    int tim_arr = 72E6/freq;
      while ( (tim_arr/divider) > 65535) {divider++;} 
       TIMER1_BASE->PSC=divider-1;
        TIMER1_BASE->ARR=(tim_arr/divider)-1;
          set_duty();
           calc_freq();
         }

//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
void timer_hi_set(int arr){
__asm volatile( "cpsid i" );
 GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
  GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
   TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
   //насильно уменьшить шаг с ростом частоты
    if (TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100; 
     if (TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
     if (TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;      
    arr*=encstep;
   int icr= TIMER1_BASE->ARR-=arr;
  if (icr<1) {icr=1;} if (icr>65535) {icr=65535;}
 TIMER1_BASE->ARR=icr;
__asm volatile( "cpsie i" );
set_duty();
calc_freq();
}

// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
void calc_freq(){
uint32_t imp_long, imp_hi;
 freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
  duty_out=  (float) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
   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) /72.0 ;
  t_hi=  imp_hi /72.0;
mon_flag=1;
    }


// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
void dds_set(){
static uint32_t akkum;
 static byte oldmode=255;
   TIMER1_BASE->CCER=0; //timer output pins disable && перечитать регистры. 
    GPIOA_BASE->CRL = 0x33333333;// pa0-pa7  выход
     GPIOA_BASE->CRH =0x44444444; //pa8-pa15 вход
      if (oldmode !=mode) {
       if (mode==2) {for(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*float(n)/512)+1)/2 ;}}// синус
       else if (mode==3){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=n;} else {wave[n]=(511-n);}}}//треугол
      else if (mode==4){ for(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}                               //пила1
     else if (mode==5){ for(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}                            //пила2
    else if (mode==6){ 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 * 1670.1100841768461768461768461768;//  шаг= частота*коэффициент
while(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];} //генерация DDS
  GPIOA_BASE->CRL=0x44444444;// все пины в Z
}//END DDS set()
//

  void mytone(int frq, int ms ){
if (mode==7){return;} //таймер в режиме частотометра занят
 uint16_t psc=1; uint32_t tim_arr;
 // настройка генератора звука на таймере3
  tim_arr = 36E6/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 = 72E3 * 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" );
}


Убедительная просьба не использовать кнопку "Цитировать" под этим постом, т.к. у меня пропадёт возможность редактировать ошибки, да и просто засирать тему повторной информацией не стоит.

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

Спасибо !

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

Чисто для информации http://radiokot.ru/forum/viewtopic.php?f=59&t=146491

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

240265,  Это приложение к какому-то обзору,  или всё что есть?  Там же  ни тех. характеристик, ни схемы, ни исходников. В любом случае внутренний ЦАП слишком медленный, для качественного DDS не годится. Мне и 3 мегасемпла в моём маловато кажется.. я сперва даже в сомнениях был, не переписать ли функцию DDS на ассемблере, может ещё удасться выжать скорости.. но потом забил :)

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

Это все что есть. Я пока жду Ф100 камни что бы попробовать. Нажно попробовать списаться с Автором, может скинет исходники. Н а схема ( как сейчас, к несчастью, практикуется) расписана текстом в первом посте (совсем народ рисовать не хочет). К стати если у Вас схема V3 есть в .spl скиньте пожалуйста ivl240265@mail.ru .

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

У меня в перспективе есть мысли сделать АЧХометр на базе AD9851 и Ардуинки с экраном на 9341. Но никак руки не доходят.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

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

Я кстати тоже делаю АЧХометр, по этому готовому проекту. Плата уже на 90% спаяна, ждёт микроконтроллера.. 

Схема в spl

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

Спасибо

Joiner
Offline
Зарегистрирован: 04.09.2014

dimax, классная штука!

Joiner
Offline
Зарегистрирован: 04.09.2014

Иногда в сети встречаются знакомые проекты https://www.drive2.ru/c/459214330949321469/

Он еще и пультоскоп делал, но я не нашел пока. Парень, видимо с руками....

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

V3_128

Переделал под 128х128 экран (st7735  ) , цвета синий с красным попутаны. Но оно и не мешает.

[code]


/*Генератор с регулируемой частотой v3.0 (C)Dimax */
#define pwm2_polar 0 //полярность выхода PWM2
 #define paper        0x000000 // цвет фона экрана
  #include <Adafruit_GFX_AS.h>    // Core graphics library
   #include <Adafruit_ST7735.h> // Hardware-specific library
    #include <SPI.h> 
     Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10); 
      volatile int enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
       volatile int encstep=1; //шаг изменения частоты по умолчанию 
      volatile float freq=1000; //частота по умолчанию
    volatile float duty_out;// переменная счёта скважности
   float t_hi, t_low; //переменные счёта длины импульсов
  uint8_t wave[512]; //массив для DDS синтеза
byte sine_logo[]={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() {
         SPI.setModule(2);// выбор SPI2
        tft.initR(INITR_BLACKTAB);
       tft.setRotation(3);//дисплей горизонтально, контакты слева
      tft.fillScreen(paper);//залить цветом по умолчанию
    tft.setTextWrap(0);//не переосить строки
   nvic_irq_disable_all();//отключить все прерывания
  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
  pinMode(PB0,PWM); //buzzer
   pinMode(PB6,INPUT_PULLUP);//enc
    pinMode(PB7,INPUT_PULLUP);//enc
     pinMode(PB8,INPUT_PULLUP);//key enc
      mytone(1000,50);//сигнал после старта
///////// Таймер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; 
 timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
TIMER4_BASE->CR1|=(1<<0);//запуск 
//прерывание кнопки энкодера
 attachInterrupt(PB8, key_enc_int, RISING);
  timer_set();
   }



void loop() {
static int old_mode_loop=-1;
if (freq > 8485) {tim_mode=1;} else {tim_mode=0;}// переключать режимы таймера
  if (mode!=old_mode_loop) {
   tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
     }//чистить полностью экран только при смене режимов
    if (mode==7) {mon_out(); freq_meter();  }
   if (mode >1 && mode<7) { mon_out();  dds_set(); } // запуск DDS режимов
 if (mon_flag) { 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=1999; // 72000000/20000= 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>1E6){ t_low=0;t_hi=0; duty_out=50;return;} //выйти если freq больше мегагерца
   // Перенастройка таймера 2 в режии измерения длительности импульса и скважности
 divider=1;                                  
while ((72E6/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; //перечитать регистры.
       TIMER2_BASE->CR1=(1<<0);
      __asm volatile( "cpsie i" );
     while( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile("nop");  if(mon_flag) {return;} }
    TIMER2_BASE->CR1=0;// стоп таймер
   imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
  imp_hi=(uint32_t)  ((TIMER2_BASE->CCR2)*divider);
 if (freq <1000){ freq=72E6 /imp_long;} //если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
duty_out=  (float) imp_hi / (imp_long / 100.0) ;
 t_low= (imp_long-imp_hi) /72.0 ;
  t_hi=  imp_hi /72.0;
   mon_flag=1;
   } //END freq meter


///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[10]; float freq_out;
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print(" PWM Mode   "); break;
       case 1: tft.print("Duty Mode  "); break;
       case 2: tft.print("Sinus DDS  "); break;
       case 3: tft.print("Trian DDS"); break;
       case 4: tft.print("Pila1 DDS  "); break;
       case 5: tft.print("Pila2 DDS  "); break;
       case 6: tft.print("Meandr DDS "); break;
       case 7: tft.print("Freq meter "); break;
       }
 //*****************Вывод второй строчки*****************************
        tft.setTextColor(ST7735_WHITE, paper);
         tft.setTextSize(2);   tft.setCursor(0, 19);
           if (freq >=1E6) { freq_out=freq/1E6;}
             else if (freq>=1E4) { freq_out=freq/1000;}
              else { freq_out=freq;}              
              if (mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print("   "); } 
             else {dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}    
  //********************Вывод третьей строчки*****************************
                tft.setCursor(30, 43); //  вперёд, вниз
               tft.setTextColor(ST7735_RED,paper);
              if(freq>=1E6){tft.print("MHz");}
             else if (freq>=1E4){tft.print("kHz");}
           else {tft.print(" Hz");}  
  //********************* "осциллограммы"******************************
     tft.fillRect(5,90, 100,38,paper);//вправо, вниз, ширина вправо, длина вниз
     tft.drawRect(0,67, 128,61,ST7735_MAGENTA);//вправо, вниз, ширина вправо, длина вниз
      if (mode<2 || mode==7){
        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==2){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==2)
                       else if (mode==3){// логотип треугольника                         
                      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==3
                         else if (mode==4){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==3)
                            else if (mode==5){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==4)
                                else if (mode==6){ //логотип меандра
                                  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 < 2 || mode==7){ 
               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(60, 95); tft.print("Duty = ");
         // tft.setCursor(60, 105); 
          tft.print(duty_out,0);tft.print("%");
          } //END if (mode < 2 || mode==7)
         if (mode!=7){ // выводить шаг кроме частотометра
         tft.setCursor(14, 95); tft.print("Step=");
       tft.setCursor(14, 105); 
       switch (encstep) { 
         case 1: tft.print("   1"); break;
        case 10: tft.print("  10"); break;
       case 100: tft.print(" 100");break;
      case 1000: tft.print("1000");break;
       }// END switch case        
        } // END if (mode!=7)
if ( freq < 1)  {tft.fillRect(1,68, 158,59,paper); }
mon_flag=0;
}//END mon_out




//обработчик прерываний энкодера
void enc_int(){   
  if((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);} 
  else {enc_mode(-1);} 
  }// END VOID


// ФУНКЦИЯ конфигурации режимов 
void enc_mode(int in){
  modebit= (GPIOB_BASE->IDR&0x100); //состояние кнопки. 0-нажата 
   if (!modebit) { 
    mytone(880,30); //звук переключения режимов
     mode+=in; 
     if(mode>7){mode=7;}
      if(mode<0){mode=0;} 
       if (mode<2){timer_set();}
        if (mode==7) {freq=0;}
         mon_flag=1; 
         return; 
          } 
         mytone(4400,10); //звук изменения частоты
       switch(mode){
      case 0:   if (tim_mode==0) { freq+=(encstep*in); timer_set(); }   else { timer_hi_set(in); } break;
     case 1:   duty_in+=in; if(duty_in>99){duty_in=99;} if(duty_in<1){duty_in=1;} set_duty(); calc_freq();  break;  
    case 7:   break; // в частотометре не реагировать на вращение энкодера 
   default: freq+=(encstep*in); mon_flag=1; //wave режимы
   // проверка на корректность запроса частоты
  if (freq<0){freq=0;} if (freq>2E5) {freq=2E5;}
  } //end switch case
}//end enc_mode


// обработчик кнопки энкодера
void key_enc_int(){
  if(!modebit){modebit=1; mon_flag=1;return;}// если менялся режим -выйти  
   if (mode==1) { mytone(880,30); mode=0;  mon_flag=1; ;return;} //сменить режим и выйти если были в duty mode
     mytone(220,75); //звук переключения шага
        switch (encstep){ 
          case 1: encstep=10;  break;
            case 10: encstep=100;   break;
            case 100: encstep=1000; break;
          case 1000: encstep=1;  break; 
         } 
    mon_flag=1; //флаг вывода на дисплей
}//end 

void set_duty(){
if (mode==0 && TIMER1_BASE->ARR<100){ 
  TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; 
  duty_in=50;  return;   
  }
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
}

//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
void timer_set(){
        GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
      GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
     TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1;  TIMER1_BASE->CR1=1;
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
 if (freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1; return;} 
 if (freq>36E6) {freq=36E6;}
   divider=1;
    int tim_arr = 72E6/freq;
      while ( (tim_arr/divider) > 65535) {divider++;} 
       TIMER1_BASE->PSC=divider-1;
        TIMER1_BASE->ARR=(tim_arr/divider)-1;
          set_duty();
           calc_freq();
         }

//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
void timer_hi_set(int arr){
__asm volatile( "cpsid i" );
 GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
  GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
   TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
   //насильно уменьшить шаг с ростом частоты
    if (TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100; 
     if (TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
     if (TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;      
    arr*=encstep;
   int icr= TIMER1_BASE->ARR-=arr;
  if (icr<1) {icr=1;} if (icr>65535) {icr=65535;}
 TIMER1_BASE->ARR=icr;
__asm volatile( "cpsie i" );
set_duty();
calc_freq();
}

// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
void calc_freq(){
uint32_t imp_long, imp_hi;
 freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
  duty_out=  (float) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
   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) /72.0 ;
  t_hi=  imp_hi /72.0;
mon_flag=1;
    }


// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
void dds_set(){
static uint32_t akkum;
 static byte oldmode=255;
   TIMER1_BASE->CCER=0; //timer output pins disable && перечитать регистры. 
    GPIOA_BASE->CRL = 0x33333333;// pa0-pa7  выход
     GPIOA_BASE->CRH =0x44444444; //pa8-pa15 вход
      if (oldmode !=mode) {
       if (mode==2) {for(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*float(n)/512)+1)/2 ;}}// синус
       else if (mode==3){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=n;} else {wave[n]=(511-n);}}}//треугол
      else if (mode==4){ for(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}                               //пила1
     else if (mode==5){ for(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}                            //пила2
    else if (mode==6){ 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 * 1670.1100841768461768461768461768;//  шаг= частота*коэффициент
while(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];} //генерация DDS
  GPIOA_BASE->CRL=0x44444444;// все пины в Z
}//END DDS set()
//

  void mytone(int frq, int ms ){
if (mode==7){return;} //таймер в режиме частотометра занят
 uint16_t psc=1; uint32_t tim_arr;
 // настройка генератора звука на таймере3
  tim_arr = 36E6/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 = 72E3 * 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" );
}



[/code]

DIMAX СПАСИБО !

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

240265, :) В идеале бы для квадратного дисплея сделать повыше рамку, и вставить step и duty верхней строчкой.

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

Да я особо не заморачивался. Мне срочо понадобился частотомер. ;)

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

Т.К. энкодер у меня не тот , прилепил кнопки.

[code]


/*Генератор с регулируемой частотой v3.0 (C)Dimax 
IVL (240265) Добавил кнопки. Экран 128х128 ST7735 */
#define pwm2_polar 0 //полярность выхода PWM2
 #define paper        0x000000 // цвет фона экрана
  #include <Adafruit_GFX_AS.h>    // Core graphics library
   #include <Adafruit_ST7735.h> // Hardware-specific library
    #include <SPI.h> 
     Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10); 
      volatile int enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
       volatile int encstep=1; //шаг изменения частоты по умолчанию 
      volatile float freq=1000; //частота по умолчанию
    volatile float duty_out;// переменная счёта скважности
   float t_hi, t_low; //переменные счёта длины импульсов
  uint8_t wave[512]; //массив для DDS синтеза
byte sine_logo[]={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() {
         SPI.setModule(2);// выбор SPI2
        tft.initR(INITR_BLACKTAB);
       tft.setRotation(3);//дисплей горизонтально, контакты слева
      tft.fillScreen(paper);//залить цветом по умолчанию
    tft.setTextWrap(0);//не переосить строки
   nvic_irq_disable_all();//отключить все прерывания
  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
  pinMode(PB0,PWM); //buzzer
  pinMode(PB4,INPUT_PULLUP);//key+
  pinMode(PB5,INPUT_PULLUP);//key-
   pinMode(PB6,INPUT_PULLUP);//enc
    pinMode(PB7,INPUT_PULLUP);//enc
     pinMode(PB8,INPUT_PULLUP);//key enc
      mytone(1000,50);//сигнал после старта
///////// Таймер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; 
 timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
TIMER4_BASE->CR1|=(1<<0);//запуск 
//прерывание кнопки энкодера
 attachInterrupt(PB8, key_enc_int, RISING);
 attachInterrupt(PB4, key_plus_int, RISING);
 attachInterrupt(PB5, key_min_int, RISING);
  timer_set();
   }



void loop() {
static int old_mode_loop=-1;
if (freq > 8485) {tim_mode=1;} else {tim_mode=0;}// переключать режимы таймера
  if (mode!=old_mode_loop) {
   tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
     }//чистить полностью экран только при смене режимов
    if (mode==7) {mon_out(); freq_meter();  }
   if (mode >1 && mode<7) { mon_out();  dds_set(); } // запуск DDS режимов
 if (mon_flag) { 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=1999; // 72000000/20000= 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>1E6){ t_low=0;t_hi=0; duty_out=50;return;} //выйти если freq больше мегагерца
   // Перенастройка таймера 2 в режии измерения длительности импульса и скважности
 divider=1;                                  
while ((72E6/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; //перечитать регистры.
       TIMER2_BASE->CR1=(1<<0);
      __asm volatile( "cpsie i" );
     while( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile("nop");  if(mon_flag) {return;} }
    TIMER2_BASE->CR1=0;// стоп таймер
   imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
  imp_hi=(uint32_t)  ((TIMER2_BASE->CCR2)*divider);
 if (freq <1000){ freq=72E6 /imp_long;} //если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
duty_out=  (float) imp_hi / (imp_long / 100.0) ;
 t_low= (imp_long-imp_hi) /72.0 ;
  t_hi=  imp_hi /72.0;
   mon_flag=1;
   } //END freq meter


///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[10]; float freq_out;
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print(" PWM Mode   "); break;
       case 1: tft.print("Duty Mode  "); break;
       case 2: tft.print("Sinus DDS  "); break;
       case 3: tft.print("Trian DDS"); break;
       case 4: tft.print("Pila1 DDS  "); break;
       case 5: tft.print("Pila2 DDS  "); break;
       case 6: tft.print("Meandr DDS "); break;
       case 7: tft.print("Freq meter "); break;
       }
 //*****************Вывод второй строчки*****************************
        tft.setTextColor(ST7735_WHITE, paper);
         tft.setTextSize(2);   tft.setCursor(0, 19);
           if (freq >=1E6) { freq_out=freq/1E6;}
             else if (freq>=1E4) { freq_out=freq/1000;}
              else { freq_out=freq;}              
              if (mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print("   "); } 
             else {dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}    
  //********************Вывод третьей строчки*****************************
                tft.setCursor(30, 43); //  вперёд, вниз
               tft.setTextColor(ST7735_RED,paper);
              if(freq>=1E6){tft.print("MHz");}
             else if (freq>=1E4){tft.print("kHz");}
           else {tft.print(" Hz");}  
  //********************* "осциллограммы"******************************
     tft.fillRect(5,90, 100,38,paper);//вправо, вниз, ширина вправо, длина вниз
     tft.drawRect(0,67, 128,61,ST7735_MAGENTA);//вправо, вниз, ширина вправо, длина вниз
      if (mode<2 || mode==7){
        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==2){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==2)
                       else if (mode==3){// логотип треугольника                         
                      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==3
                         else if (mode==4){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==3)
                            else if (mode==5){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==4)
                                else if (mode==6){ //логотип меандра
                                  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 < 2 || mode==7){ 
               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(60, 95); tft.print("Duty = ");
         // tft.setCursor(60, 105); 
          tft.print(duty_out,0);tft.print("%");
          } //END if (mode < 2 || mode==7)
         if (mode!=7){ // выводить шаг кроме частотометра
         tft.setCursor(14, 95); tft.print("Step=");
       tft.setCursor(14, 105); 
       switch (encstep) { 
         case 1: tft.print("   1"); break;
        case 10: tft.print("  10"); break;
       case 100: tft.print(" 100");break;
      case 1000: tft.print("1000");break;
       }// END switch case        
        } // END if (mode!=7)
if ( freq < 1)  {tft.fillRect(1,68, 158,59,paper); }
mon_flag=0;
}//END mon_out




//обработчик прерываний энкодера
void enc_int(){   
  if((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);} 
  else {enc_mode(-1);} 
  }// END VOID

// обработчик кнопки +
void key_plus_int(){
// delay(1);
  enc_mode(1);
}

// обработчик кнопки -
void key_min_int(){
 // delay(1);
  enc_mode(-1);
}

// ФУНКЦИЯ конфигурации режимов 
void enc_mode(int in){
  modebit= (GPIOB_BASE->IDR&0x100); //состояние кнопки. 0-нажата 
   if (!modebit) { 
    mytone(880,30); //звук переключения режимов
     mode+=in; 
     if(mode>7){mode=7;}
      if(mode<0){mode=0;} 
       if (mode<2){timer_set();}
        if (mode==7) {freq=0;}
         mon_flag=1; 
         return; 
          } 
         mytone(4400,10); //звук изменения частоты
       switch(mode){
      case 0:   if (tim_mode==0) { freq+=(encstep*in); timer_set(); }   else { timer_hi_set(in); } break;
     case 1:   duty_in+=in; if(duty_in>99){duty_in=99;} if(duty_in<1){duty_in=1;} set_duty(); calc_freq();  break;  
    case 7:   break; // в частотометре не реагировать на вращение энкодера 
   default: freq+=(encstep*in); mon_flag=1; //wave режимы
   // проверка на корректность запроса частоты
  if (freq<0){freq=0;} if (freq>2E5) {freq=2E5;}
  } //end switch case
}//end enc_mode


// обработчик кнопки энкодера
void key_enc_int(){
  //delay(10);
  if(!modebit){modebit=1; mon_flag=1;return;}// если менялся режим -выйти  
   if (mode==1) { mytone(880,30); mode=0;  mon_flag=1; ;return;} //сменить режим и выйти если были в duty mode
     mytone(220,75); //звук переключения шага
        switch (encstep){ 
          case 1: encstep=10;  break;
            case 10: encstep=100;   break;
            case 100: encstep=1000; break;
          case 1000: encstep=1;  break; 
         } 
    mon_flag=1; //флаг вывода на дисплей
}//end 

void set_duty(){
if (mode==0 && TIMER1_BASE->ARR<100){ 
  TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; 
  duty_in=50;  return;   
  }
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
}

//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
void timer_set(){
        GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
      GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
     TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1;  TIMER1_BASE->CR1=1;
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
 if (freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1; return;} 
 if (freq>36E6) {freq=36E6;}
   divider=1;
    int tim_arr = 72E6/freq;
      while ( (tim_arr/divider) > 65535) {divider++;} 
       TIMER1_BASE->PSC=divider-1;
        TIMER1_BASE->ARR=(tim_arr/divider)-1;
          set_duty();
           calc_freq();
         }

//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
void timer_hi_set(int arr){
__asm volatile( "cpsid i" );
 GPIOA_BASE->CRL=0xB4444444; //PA7 alt_output
  GPIOA_BASE->CRH=0x4444444B; //PA8 alt_output
   TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);//PWM mode 1
   TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
   //насильно уменьшить шаг с ростом частоты
    if (TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100; 
     if (TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
     if (TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;      
    arr*=encstep;
   int icr= TIMER1_BASE->ARR-=arr;
  if (icr<1) {icr=1;} if (icr>65535) {icr=65535;}
 TIMER1_BASE->ARR=icr;
__asm volatile( "cpsie i" );
set_duty();
calc_freq();
}

// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
void calc_freq(){
uint32_t imp_long, imp_hi;
 freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
  duty_out=  (float) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
   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) /72.0 ;
  t_hi=  imp_hi /72.0;
mon_flag=1;
    }


// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
void dds_set(){
static uint32_t akkum;
 static byte oldmode=255;
   TIMER1_BASE->CCER=0; //timer output pins disable && перечитать регистры. 
    GPIOA_BASE->CRL = 0x33333333;// pa0-pa7  выход
     GPIOA_BASE->CRH =0x44444444; //pa8-pa15 вход
      if (oldmode !=mode) {
       if (mode==2) {for(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*float(n)/512)+1)/2 ;}}// синус
       else if (mode==3){ for(uint16_t n=0; n<512; n++){if (n<256){ wave[n]=n;} else {wave[n]=(511-n);}}}//треугол
      else if (mode==4){ for(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}                               //пила1
     else if (mode==5){ for(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}                            //пила2
    else if (mode==6){ 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 * 1670.1100841768461768461768461768;//  шаг= частота*коэффициент
while(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];} //генерация DDS
  GPIOA_BASE->CRL=0x44444444;// все пины в Z
}//END DDS set()
//

  void mytone(int frq, int ms ){
if (mode==7){return;} //таймер в режиме частотометра занят
 uint16_t psc=1; uint32_t tim_arr;
 // настройка генератора звука на таймере3
  tim_arr = 36E6/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 = 72E3 * 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" );
}



[/code]

Дребезг убрал аппаратно.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

240265, не тот -это какой? Работать же должно с любым.

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

По Вашим картинкам , который не тот. В плюс идет, а в минус не в какую.

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

Блин не везде на схеме убрал 5В , НУЖНО ВСЕ ПОДТЯЖКИ И ПИТАНИЕ ЭКРАНА САЖАТЬ НА 3,3В.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

240265, ky-40 что на платке? Должен работать в любую сторону, я его тоже подключал и тестировал.  Просто через щелчок срабатывать будет. Я ж специально ушёл от внешних прерываний (вернее одно осталось вынужденно) на аппаратный обработчик энкодера на таймере,  потому, что  прерывания с любыми кнопкообразными устройствами ввода глючат. Попробуйте для эксперимента выставить высокую частоту (от 50 кГц). И понажимать ваши кнопки. Не будут ли происходить ложные срабатывания?

PS делай работать не будет потому, что в сетапе я системный таймер отрубил. Он мешает DDS процессу.

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

Да энкодер на платке, может он хреновый х.з. , сделал кнопки ( я энкодеров в автомогнитолах столько уже наперебирал поэтому у меня к ним сложилось сильное недоверие) . С теми цепями атидребезга что на схеме , ложных нет . Кнопки сделал по прерываниям.

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

Вы частотомер с эталонным генератором не проверяли ? Можно ему верить ?

P.S. Да увидел   systick_disable(); // отключить системный таймер .

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

240265, точность частотометра и генератора всецело зависит от точности кварца на плате :) Подавал при тестировании частоты от синтезатора на "малинке", вроде всё ок было.

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

Подключил пултоскоповский генератор к этому частотомеру . Пултоскоп 1,000 МГц - частотомер 1,143 МГц.

Блин ,а я по нему развертки осцилографа на Ф303 подгонял....

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

Dimax надо бы хексы (bin ) выложить, не кажный захочет (сможет) АрдуиноСТМ32 устанавливать.

PS В исходный вариант 160х128 добавьте тоже кнопки . кто хочет- кнопки , кто хочет - энкодер. У меня работает и то, и то.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

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

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

dimax пишет:

  А тут большинство сами в состоянии модифицировать под свои нужды,

Да всякое бывает, может как раз сложность ИДЕ и пугает. Вот я пока пакет СТМ не положил в папку с ардуино ( Arduino / hardware ) фиг я платы СТМ увидел.

ssss
Offline
Зарегистрирован: 01.07.2016

dimax пишет:

точность частотометра и генератора всецело зависит от точности кварца на плате

Да никакая там точность, даже хуже чем никакая (за вариант на СТМ32 говорю). И от кварца там мало что зависит. Но как для простого показометра вполне сойдёт, тем более что его никто уже развивать и не будет.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ssss, "никакая точность" -это не техническое заключение. Если не согласны с моими измерениями -проведите свои, укажите как тестировали, каким оборудованием проверяли, итд итп.

ssss
Offline
Зарегистрирован: 01.07.2016

Да там и тестировать нечего... чисто теория просто... которая не врёт... Ну 4 знака, ну 5... от силы... и уж точно не +- 1Гц... особенно на высоких частотах... что для показометра вполне... И до 30 МГц максимум, а не 33... выше 30 уже врать начнёт...

1. нет возможности программной коррекции, а типовые кварцы имеют приличный разброс.

2. да и коррекция не поможет, у вас ПЛЛ включена, а она плавает, да и как правило тянет частоту вниз. можно усреднять конечно, но это уже...

По хорошему... надо ставить TCXO... и не юзать ПЛЛ... а у 103-их 25МГц максимум... иначе может сваливаться в хардфаулт... Лучше бы ставить 030 (до 32МГц, 38,4МГц тянет) или 105-й (50МГц тактовой)... хотя если заюзать вход ETR, то и на 103-ем 80 МГц макс. входной на 25МГц тактовой получить можно... Но это всё так... побубнеть... развивать, как я понял, никто ничего не будет... а тогда - уж как есть...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

На высоких да, наверное уплывёт. Надо будет  что-то придумать для более тщательного тестирования. Пока не до того -в отпуск еду :)