Иногда при работе с мк бывает нужно подать сигнал определённой частоты, а специального устройства у меня нет, т.к. не сильно то оно нужно, да и ставить ещё один ящик негде. Вот написал скетчик генератора с регулируемой частотой, в большинстве случаев его достаточно. Что он может: -генерит меандр на 16 битном таймере. Диапазон частот 1Гц - 8МГц. Регулировка частоты производится энкодером. До частоты 2,8 кГц разрешение 1 герц, на частотах выше таймер аппаратно уже не может поддерживать это разрешение, поэтому более высокие частоты синтезируются задавая параметром не требуемую частоту, а просто инкременируя регистр сравнения. Получается чем выше частота -тем больше шаг между щелчками энкодера. Вращая энкодер с ненажатой кнопкой частота меняется на 1Гц , с нажатой кнопкой один шаг -100Гц. Выше 2,8кГц вращение энкодера с нажатой конпкой так-же ускоряет счёт. Програмного подавления дребезга контактов энкодера нет, поэтому нужно повесить конденсаторы 0,01..0,1 мкф относительно земли. На кнопке конденсатор не обязателен. Рассчитанная математически частота выводится в сериал. Энкодер использовался самый популярный, для нестандартных возможно придётся корректировать обработчик.
12 | pinMode(A2,INPUT_PULLUP); |
23 | static boolean gen_mode=0; |
29 | boolean knopka = PINC&(1<<2); |
30 | if (freq<2848) gen_mode=0; |
31 | if (freq>=2848) gen_mode=1; |
35 | if (gen_mode){ if (knopka){ if (ocr>0) {ocr--; } } else { if (ocr>9)ocr-=10; } } |
36 | else knopka? enc++ : enc+=100; |
41 | if (gen_mode){ if (knopka){ if (ocr<65535) {ocr++; } } else { if (ocr<=65525)ocr+=10; } } |
42 | else { if (knopka) { if (enc>=2)enc--; } else { if (enc>100) enc-=100; } } |
46 | if (gen_mode){ OCR1A=ocr; freq= ( float )F_CPU/2 / (OCR1A+1); } |
48 | divider=1; ocr = (F_CPU / enc /2 /divider) -1; |
49 | if (ocr >65536) { divider=8; ocr = F_CPU / enc /2 /divider; |
50 | if (ocr >65536) { divider=64; ocr = F_CPU / enc /2 /divider; |
51 | if (ocr >65536) {divider=256; ocr = F_CPU / enc /2 /divider; |
52 | if (ocr >65536) { divider=1024; ocr = F_CPU / enc /2 /divider; |
53 | if (ocr >65536){ocr=65536; }}}}} OCR1A=ocr-1; |
56 | case 1: TCCR1B=1|(1<<WGM12); break ; |
57 | case 8: TCCR1B=2|(1<<WGM12); break ; |
58 | case 64: TCCR1B=3|(1<<WGM12); break ; |
59 | case 256: TCCR1B=4|(1<<WGM12); break ; |
60 | case 1024: TCCR1B=5|(1<<WGM12); break ; } |
62 | freq= ( float ) F_CPU/2 / (OCR1A+1) /divider; |
70 | if (freq <10000) { Serial .print(freq,1); Serial .println( " Hz " ); } |
71 | if (freq >10000) { Serial .print(freq/1000,3); Serial .println( " kHz" );} |
Кстати заметил ошибку в стартовом сообщении. В 48 строке нужно убрать -1 в конце строки.
dimax. Проверил ваш проект в работе. Смотрел меандр осциллографической приставкой, так же покрутил на частотомере.
Всё отлично!
jeka_tm, вот, модифицировал основной код. Эта версия с регулировкой скважности. Частоту ограничивать в итоге не стал, скважность регулируется на всех частотах, просто начиная с какого-то значения регулируется всё бОльшими рывками. На мегагерце например всего 8 градаций. Но энкодер всё равно везде счёлкает 100 градаций (в процентах).Некоторое неудобство, но избавится от него малыми силами не выйдет. Хочешь, сам повозись, можно по идее ввести разрядность регулировки скважности в зависимости от частоты.
01
/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер регулировки частоты
02
подключен к пинам A0 и A1, кнопка энкодера подключена к A2.
03
Энкодер регулировки скважности подключен к пинам 8 и 10
04
Его кнопка не задействована.
05
Требуется использовать конденсаторы 0,01..0,1uf относительно земли
06
на каждый вывод обоих энкодеров.
07
Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
08
*/
09
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
10
uint8_t duty=50;
11
float
freq;
12
void
setup
() {
13
Serial
.begin(9600);
14
pinMode (9,OUTPUT);
// выход генератора PB2
15
pinMode(A0,INPUT);
// PC0 вывод энкодера "частота"
16
pinMode(A1,INPUT);
// PC1 вывод энкодера "частота"
17
pinMode(A2,INPUT_PULLUP);
// PC2 кнопка энкодера
18
pinMode(8,INPUT);
// PB0 вывод энкодера "скважность"
19
pinMode(10,INPUT);
// PB2 вывод энкодера "скважность"
20
PCICR=(1<<PCIE1)|(1<<PCIE0);
//разрешить прерывания PCINT 0, 1
21
PCMSK1=1<<PCINT9;
// По сигналу на А1 создавать прерывание (устновка частоты)
22
PCMSK0=1<<PCINT0;
// По сигналу на pin8 создавать прерывание (скважность)
23
TCCR1A=1<<COM1A1;
//подключить выход OC1A первого таймера
24
TCCR1B=0;
//
25
}
26
27
ISR (PCINT1_vect){
28
static
boolean gen_mode=0;
//флаг режима управления
29
static
uint32_t enc=1;
//переменная счёта энкодера
30
uint32_t icr=ICR1;
31
uint16_t divider=1;
//переменная коэфф. деления прескалера
32
33
byte
n=PINC&3;
//считать значение энкодера частоты
34
boolean knopka = PINC&(1<<2);
// 0-кнопка нажата, 1-кнопка НЕ нажата.
35
if
(freq<2848) gen_mode=0;
//переключение режима управления по частоте
36
if
(freq>=2848) gen_mode=1;
//переключение режима управления по OCR
37
38
// Если увеличение частоты
39
if
(n==3||n==0){
40
if
(gen_mode){
if
(knopka){
if
(icr>2) {icr--; } }
else
{
if
(icr>12)icr-=10; } }
41
else
knopka? enc++ : enc+=100;
// в нч режиме
42
}
//end GetUP
43
44
// Если уменьшение частоты
45
if
(n==2||n==1){
46
if
(gen_mode){
if
(knopka){
if
(icr<65535) {icr++; } }
else
{
if
(icr<=65525)icr+=10; } }
47
else
{
if
(knopka) {
if
(enc>=2)enc--; }
else
{
if
(enc>100) enc-=100; } }
48
}
//end GetDown
49
50
51
if
(gen_mode){ ICR1=icr; set_duty(); freq= (
float
)F_CPU/2 / ICR1; }
52
else
{
//расчёт прескалера и ICR по нужной частоте
53
divider=1; icr = F_CPU / enc /2 /divider;
54
if
(icr >65536) { divider=8; icr = F_CPU / enc /2 /divider;
55
if
(icr >65536) { divider=64; icr = F_CPU / enc /2 /divider;
56
if
(icr >65536) {divider=256; icr = F_CPU / enc /2 /divider;
57
if
(icr >65536) { divider=1024; icr = F_CPU / enc /2 /divider;
58
if
(icr >65536){icr=65536; }}}}} ICR1=icr-1; set_duty();
59
//запись в регистр прескалера
60
switch
(divider) {
61
case
1: TCCR1B=1|(1<<WGM13);
break
;
62
case
8: TCCR1B=2|(1<<WGM13);
break
;
63
case
64: TCCR1B=3|(1<<WGM13);
break
;
64
case
256: TCCR1B=4|(1<<WGM13);
break
;
65
case
1024: TCCR1B=5|(1<<WGM13);
break
; }
66
67
freq= (
float
) F_CPU/2 / (ICR1+1) /divider;
68
}
//конец "если работа в НЧ режиме"
69
}
70
71
ISR (PCINT0_vect){
//обработчик энкодера "скважность"
72
byte
m=PINB&B00000101;
//считать значение энкодера2
73
if
(m==0||m==5){
if
(duty<100) {duty++; } }
74
if
(m==1||m==4){
if
(duty>0) {duty--; } }
75
set_duty();
76
}
77
78
void
loop
() {
79
if
(freq <10000) {
Serial
.print(freq,1);
Serial
.print(
" Hz "
); }
80
if
(freq >10000) {
Serial
.print(freq/1000,3);
Serial
.print(
" kHz"
);}
81
Serial
.print(
"<br>"
);
82
Serial
.print(
"Duty="
);
Serial
.print(duty);
Serial
.print(
"%"
);
83
Serial
.println();
84
delay(100);
85
}
Сделал генератор-шилд для UNO, как и задумывал ранее. За основу взята версия с регулировкой скважности, но вместо второго энкодера добавил кнопку, которая переключает энкодер в режим частоты или скважности. Фотки и видео:
И видео с демонстрацией режимов (быстрая регулировка частоты, по-герцевая регулировка, регулировка скважности) https://cloud.mail.ru/public/CaFy/QxWcRpwSy
а можеim скинуть код как в 18 посте но кнопкой плюс энкодер как на видео?
jeka_tm, да, конечно.
001
/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер
002
подключен к пинам A1 и A2, кнопка энкодера подключена к A0.
003
Кнопка частота/скважность 2
004
Требуется использовать конденсаторы 0,01..0,1uf относительно земли
005
на каждый вывод энкодера. Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
006
Дрова для 5110 взяты из <a href="http://www.rinkydinkelectronics.com/library.php?id=44" title="http://www.rinkydinkelectronics.com/library.php?id=44" rel="nofollow">http://www.rinkydinkelectronics.com/library.php?id=44</a>
007
*/
008
#include <LCD5110_Basic.h>
009
LCD5110 lcd(3,4,5,6,7);
010
extern
uint8_t SmallFont[];
011
extern
uint8_t MediumNumbers[];
012
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
013
uint8_t duty=50;
014
float
freq;
015
volatile uint8_t enc_mode_flag=1;
// По умолчанию энкодер регулирует частоту
016
017
void
setup
() {
018
lcd.InitLCD();
019
lcd.setContrast(65);
020
pinMode (13,OUTPUT);
// LED
021
pinMode (9,OUTPUT);
// выход генератора PB2
022
pinMode(A0,INPUT);
// PC0 кнопка энкодера
023
pinMode(A1,INPUT);
// PC1 вывод энкодера
024
pinMode(A2,INPUT);
// PC2 вывод энкодера
025
pinMode(2,INPUT_PULLUP);
// подтяжка кнопки "режим энкодера"
026
PCICR=1<<PCIE1;
//разрешить прерывание PCINT1
027
PCMSK1=1<<PCINT9;
// По сигналу на А1 создавать прерывание
028
TCCR1A=1<<COM1A1;
//подключить выход OC1A первого таймера
029
TCCR1B=0;
//
030
EICRA=1<<ISC01;
//int0 -falling mode
031
EIMSK=1;
//int0 enable
032
}
033
034
ISR (INT0_vect){
035
if
((PIND & (1<<2)) == 0)
036
{ enc_mode_flag = !enc_mode_flag; }
037
}
038
ISR (PCINT1_vect){;
039
uint8_t n=PINC&6;
//считать значение энкодера частоты
040
if
(enc_mode_flag) enc_freq(n);
// если энкодер в режиме частоты
041
if
(!enc_mode_flag) enc_duty(n);
// если энкодер в режиме скважности
042
}
043
044
045
void
enc_freq(uint8_t n){
046
static
boolean gen_mode=0;
//флаг режима управления
047
static
uint32_t enc=1;
//переменная счёта энкодера
048
uint32_t icr=ICR1;
049
uint16_t divider=1;
//переменная коэфф. деления прескалера
050
boolean knopka = PINC&(1<<0);
// 0-кнопка нажата, 1-кнопка НЕ нажата.
051
if
(freq<2848) gen_mode=0;
//переключение режима управления по частоте
052
if
(freq>=2848) gen_mode=1;
//переключение режима управления по OCR
053
// Если увеличение частоты
054
if
(n==4||n==2){
055
if
(gen_mode){
if
(knopka){
if
(icr>2) {icr--; } }
else
{
if
(icr>12)icr-=10; } }
056
else
knopka? enc++ : enc+=100;
// в нч режиме
057
}
//end GetUP
058
059
// Если уменьшение частоты
060
if
(n==6||n==0){
061
if
(gen_mode){
if
(knopka){
if
(icr<65535) {icr++; } }
else
{
if
(icr<=65525)icr+=10; } }
062
else
{
if
(knopka) {
if
(enc>=2)enc--; }
else
{
if
(enc>100) enc-=100; } }
063
}
//end GetDown
064
065
066
if
(gen_mode){ ICR1=icr; set_duty(); freq= (
float
)F_CPU/2 / ICR1; }
067
else
{
//расчёт прескалера и ICR по нужной частоте
068
divider=1; icr = F_CPU / enc /2 /divider;
069
if
(icr >65536) { divider=8; icr = F_CPU / enc /2 /divider;
070
if
(icr >65536) { divider=64; icr = F_CPU / enc /2 /divider;
071
if
(icr >65536) {divider=256; icr = F_CPU / enc /2 /divider;
072
if
(icr >65536) { divider=1024; icr = F_CPU / enc /2 /divider;
073
if
(icr >65536){icr=65536; }}}}} ICR1=icr-1; set_duty();
074
//запись в регистр прескалера
075
switch
(divider) {
076
case
1: TCCR1B=1|(1<<WGM13);
break
;
077
case
8: TCCR1B=2|(1<<WGM13);
break
;
078
case
64: TCCR1B=3|(1<<WGM13);
break
;
079
case
256: TCCR1B=4|(1<<WGM13);
break
;
080
case
1024: TCCR1B=5|(1<<WGM13);
break
; }
081
082
freq= (
float
) F_CPU/2 / (ICR1+1) /divider;
083
}
//конец "если не Gen_mode (частота менее 2848 герц)
084
}
// конец enc_freq
085
086
087
void
enc_duty(uint8_t n){
088
// Если увеличение скважности
089
if
(n==4||n==2){
if
(duty<100) {duty++; } }
090
// Если уменьшение скважности
091
if
(n==6||n==0){
if
(duty>0) {duty--; }}
092
set_duty();
093
}
094
095
void
loop
() {
096
String freqstr, dutystr,modestr;
097
098
if
(freq <10000) {
099
freqstr= String( String(freq,2)+
" Hz "
);
100
}
101
if
(freq >10000) {
102
freqstr= String( String((freq/1000),3)+
" KHz "
); }
103
lcd.setFont(SmallFont);
104
//byte l= freqstr.length();
105
lcd.print(freqstr, LEFT, 0);
106
107
dutystr= String(
"Duty="
+ String(duty)+
" % "
);
108
lcd.print(dutystr, LEFT, 8);
109
110
enc_mode_flag? modestr=String(
"ENCMode=Freq"
) : modestr=String(
"ENCMode=Duty"
);
111
lcd.print(modestr, LEFT, 16);
112
delay(100);
// что-б слишком часто в дисплей не писало..
113
}
спасибо. только под 1202 переделаю. кнопка без фиксации?
кстати когда первый код тестировал, наверно глюк моего энкодера, между щелками еще один раз срабатывает. получается за один щелчок происходит срабатывание как за два. что можно сделать не знаешь?
получается почти как у тебя
Внёс рацпредложение от Максима в последний код, заодно исправил мелкий баг обратного перехода через частоту 2848. Если уменьшать по-герцу то на частоте 2847 перебрасывалось сразу на 2900, и так по кругу. Проскочить можно было только щёлкнув в обратную сторону 100 герц. Последняя версия..
001
/* Генератор 1 Hz..4 MHz. С регулировкой скважности. Энкодер
002
подключен к пинам A1 и A2, кнопка энкодера подключена к A0.
003
Требуется использовать конденсаторы 0,01..0,1uf относительно земли
004
на каждый вывод энкодера. Скетч для ардуино на мк atmega328 (UNO,Nano, MiniPro)
005
Дрова для 5110 взяты из <a href="http://www.rinkydinkelectronics.com/library.php?id=44" rel="nofollow">http://www.rinkydinkelectronics.com/library.php?id=44</a>
006
*/
007
#include <LCD5110_Basic.h>
008
LCD5110 lcd(3,4,5,6,7);
009
extern
uint8_t SmallFont[];
010
#define set_duty() { OCR1A=(uint32_t)ICR1*duty/100; }
011
uint8_t duty=50;
012
float
freq=0;
013
volatile uint8_t enc_mode_flag=1;
// По умолчанию энкодер регулирует частоту
014
static
boolean gen_mode=0;
//флаг режима управления
015
016
void
setup
() {
017
lcd.InitLCD();
018
lcd.setContrast(65);
019
pinMode (9,OUTPUT);
// выход генератора PB2
020
pinMode(A0,INPUT);
// PC0 кнопка энкодера
021
pinMode(A1,INPUT);
// PC1 вывод энкодера
022
pinMode(A2,INPUT);
// PC2 вывод энкодера
023
pinMode(2,INPUT_PULLUP);
// подтяжка кнопки "режим энкодера"
024
PCICR=1<<PCIE1;
//разрешить прерывание PCINT1
025
PCMSK1=1<<PCINT9;
// По сигналу на А1 создавать прерывание
026
TCCR1A=1<<COM1A1;
//подключить выход OC1A первого таймера
027
TCCR1B=0;
//
028
EICRA=1<<ISC01;
//int0 -falling mode
029
EIMSK=1;
//int0 enable
030
}
031
032
ISR (INT0_vect){
033
if
((PIND & (1<<2)) == 0)
034
{ enc_mode_flag = !enc_mode_flag; }
035
}
036
ISR (PCINT1_vect){;
037
uint8_t n=PINC&6;
//считать значение энкодера частоты
038
if
(enc_mode_flag) enc_freq(n);
// если энкодер в режиме частоты
039
if
(!enc_mode_flag) enc_duty(n);
// если энкодер в режиме скважности
040
}
041
042
043
void
enc_freq(uint8_t n){
044
static
uint32_t enc=0;
//переменная счёта энкодера
045
uint32_t icr=ICR1;
046
uint16_t divider=1;
//переменная коэфф. деления прескалера
047
boolean knopka = PINC&(1<<0);
// 0-кнопка нажата, 1-кнопка НЕ нажата.
048
// Если увеличение частоты
049
if
(n==4||n==2){
050
if
(gen_mode){
if
(knopka){
if
(icr>2) {icr--; } }
else
{
if
(icr>12)icr-=10; } }
051
else
knopka? enc++ : enc+=100;
// в нч режиме
052
}
//end GetUP
053
// Если уменьшение частоты
054
if
(n==6||n==0){
055
if
(gen_mode){
if
(knopka){
if
(icr<65535) {icr++; } }
else
{
if
(icr<=65525)icr+=10; } }
056
else
{
if
(knopka) {
if
(enc>=2)enc--; }
else
{
if
(enc>100) enc-=100; } }
057
}
//end GetDown
058
if
(gen_mode){ ICR1=icr; set_duty(); freq= (
float
) F_CPU/2 / ICR1; enc=freq;}
059
else
{
//расчёт прескалера и ICR по нужной частоте
060
icr = (F_CPU / enc /2 /divider);
061
byte
shifts[] = {3,3,2,2};
062
for
(
byte
i = 0; i < 4; i++){
063
if
(icr > 65536) {
064
divider <<= shifts[i];
065
icr = F_CPU / enc /2 /divider;
066
}
067
else
{
//запись в регистр прескалера
068
TCCR1B = (i+1)|(1<<WGM13);
break
;
069
}
070
}
071
ICR1=icr-1; set_duty();
072
freq= (
float
) F_CPU/2 / (ICR1+1) /divider;
073
}
//конец "если частота менее 2848 герц)
074
075
if
(freq>2848) gen_mode=1;
//переключение режима управления по OCR
076
else
gen_mode=0;
//переключение режима управления по частоте
077
}
// конец enc_freq
078
079
080
void
enc_duty(uint8_t n){
081
// Если увеличение скважности
082
if
(n==4||n==2){
if
(duty<100) {duty++; } }
083
// Если уменьшение скважности
084
if
(n==6||n==0){
if
(duty>0) {duty--; }}
085
set_duty();
086
}
087
088
void
loop
() {
089
String freqstr, dutystr,modestr;
090
091
if
(freq <10000) {
092
freqstr= String( String(freq,2)+
" Hz "
);
093
}
094
if
(freq >10000) {
095
freqstr= String( String((freq/1000),3)+
" KHz "
); }
096
lcd.setFont(SmallFont);
097
lcd.print(freqstr, LEFT, 0);
098
099
dutystr= String(
"Duty="
+ String(duty)+
" % "
);
100
lcd.print(dutystr, LEFT, 8);
101
102
enc_mode_flag? modestr=String(
"ENCMode=Freq"
) : modestr=String(
"ENCMode=Duty"
);
103
lcd.print(modestr, LEFT, 16);
104
105
delay(100);
// что-б слишком часто в дисплей не писало..
106
}
jeka_tm печаткой шилда не поделитесь народу ?
пожалуйста
https://yadi.sk/d/mYeiZu4jm8cRv
Генератор версия 2.0
Позаимствована функция на ассемблере с алгоритмом DDS (отсюда),
с ней добавилось возможность генерить табличные формы сигналов
синуса, треугольника, пилы, можно добавить массив с другой формой сигнала,
свободного места во флэше полно. В качестве АЦП используется традиционная схема R2R. Под АЦП задействован весь порт D (выводы ардуино 0...7). Выход АЦП соединён с выходом таймера, в скетче порты соответствующим образом коммутируются. Точнось резисторов принципиальной роли не играет, 5% вполне достаточно. В идеале нужно ещё добавить какой нибудь усилитель на скоростном ОУ (типа OP37), сделать смещение и регулировку амплитуды. Меня пока устраивает и без всего этого :)
Скетч:
001
/*Программа генератора с регулируемой частотой и скважностью для МК ATMEGA328
002
* Для синтезатора частоты была позаимствована функция на ассемблере из источника
003
* <a href="http://www.scienceprog.com/avr-dds-signal-generator-v20/" title="http://www.scienceprog.com/avr-dds-signal-generator-v20/" rel="nofollow">http://www.scienceprog.com/avr-dds-signal-generator-v20/</a>
004
*/
005
006
const
PROGMEM uint8_t sinewave[]=
007
{
008
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
009
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
010
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
011
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
012
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
013
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
014
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
015
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
016
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
017
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
018
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
019
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
020
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
021
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
022
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
023
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
024
};
025
026
const
PROGMEM uint8_t squarewave[]=
027
{
028
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
029
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
030
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
031
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
032
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
033
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
034
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
035
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
036
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
037
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
038
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
039
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
040
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
041
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
042
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
043
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
044
};
045
046
const
PROGMEM uint8_t trianglewave[]=
047
{
048
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
049
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
050
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
051
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
052
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
053
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
054
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
055
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
056
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
057
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
058
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
059
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
060
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
061
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
062
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
063
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
064
};
065
066
const
PROGMEM uint8_t sawtoothwave[]=
067
{
068
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
069
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
070
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
071
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
072
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
073
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
074
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
075
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
076
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
077
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
078
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
079
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
080
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
081
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
082
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
083
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
084
};
085
086
const
PROGMEM uint8_t rewsawtoothwave[]=
087
{
088
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
089
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
090
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
091
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
092
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
093
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
094
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
095
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
096
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
097
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
098
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
099
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
100
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
101
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
102
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
103
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
104
};
105
106
107
const
PROGMEM
char
musor_mass[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
108
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
109
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
110
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
111
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
112
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
113
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
114
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
115
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
116
};
117
118
// глобальные переменные
119
volatile int32_t freq=1000;
// частота генератора по умолчанию, Герц
120
volatile uint16_t shag=100;
//шаг переключения частоты,значение по умолчанию
121
volatile uint8_t regim=5;
// режим работы по умолчанию (0)синус DDS, (1)треугольник DDS,
122
//(2)прямоугольник DDS, (3)правая пила DDS, (4) левая пила DDS. (5) частота генерации таймером
123
// (6) скважность генерации таймером
124
volatile uint8_t ad2,ad1,ad0;
//байты частоты для функции на ассемблере
125
volatile uint8_t duty=50;
//скважность (коэффициент заполнения), процентов по умолчанию
126
volatile uint32_t icr=0;
//переменная для работы с регистром ICR1
127
128
// DDS algorithm
129
static
inline
void
signalOUT(
const
uint8_t *signal){
130
asm volatile(
"eor r18, r18 ;r18<-0"
"\n\t"
131
"eor r19, r19 ;r19<-0"
"\n\t"
132
"1:"
"\n\t"
133
"add r18, %0 ;1 cycle"
"\n\t"
134
"adc r19, %1 ;1 cycle"
"\n\t"
135
"adc %A3, %2 ;1 cycle"
"\n\t"
136
"lpm ;3 cycles"
"\n\t"
137
"out %4, __tmp_reg__ ;1 cycle"
"\n\t"
138
"sbis %5, 7 ;1 cycle if no skip"
"\n\t"
// если стоит бит в переменной %5, то
139
"rjmp 1b ;2 cycles. Total 10 cycles"
"\n\t"
//пропустить эту команду ухода в цикл
140
:
141
:
"r"
(ad0),
"r"
(ad1),
"r"
(ad2),
"e"
(signal),
"I"
(_SFR_IO_ADDR(PORTD)),
"I"
(_SFR_IO_ADDR(PORTB))
142
:
"r18"
,
"r19"
);
143
}
144
145
void
setup
(){
146
Serial
.begin(115200);
147
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTD=musor_mass[n]; }
148
TCCR0B=0;
//заглушить 0 таймер что б не мешал DDS
149
PCICR=(1<<PCIE1)|(1<<PCIE0);
//разрешить прерывания PCINT 0,1
150
PCMSK1=1<<PCINT9;
// По сигналу на А1 создавать прерывание (енкодер)
151
PCMSK0=(1<<PCINT0)|(1<<PCINT2);
// По сигналу на 8 , 10 пине ардуины создавать прерывание
152
PORTB|=(1<<PB0)|(1<<PB2);
// подтяжка 8,10 пин
153
TCCR1A=0; TCCR1B=0;
154
check_regim();
//проверка и настройка режимов
155
}
//end setup
156
157
void
up_down(boolean x){
// функция режимов енкодера
158
// если режим генерации таймером и частота более 2800 Гц, то
159
if
(TCCR1B==17 && ICR1<2800 && regim==5){
160
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
161
if
(icr<100 && shag > 10) shag=10;
162
if
(icr<10 && shag > 1) shag=1;
163
icr-=shag;
164
if
(icr<2) icr=2; }
165
else
{
if
(icr > 1800 && shag >100) shag =100;
166
icr+=shag ; }
167
return
;
168
}
169
if
(regim==6){
// если енкодер в режиме регулировки скважности, то
170
// если возможна регулировка с точностью не хуже 1%, то:
171
if
(ICR1>100){
172
// Если увеличение скважности
173
if
(x){
if
(duty<100) {duty++; } }
174
// Если уменьшение скважности
175
if
(!x){
if
(duty>0) {duty--; }}
176
}
177
else
{
// если точность 1% уже невозможна, то будем крутить регистр OCR2A напрямую
178
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
179
else
{
if
(OCR1A>0) {OCR1A--; } }
180
if
(OCR1A>ICR1) OCR1A=ICR1-1;
181
duty=(uint32_t)100*OCR1A/ICR1;
182
}
183
return
;
184
}
//конец настройки режима скважности
185
// в остальных режимах DDS :
186
x? freq+=shag : freq-=shag;
187
if
(freq < 1) freq=1;
188
}
189
190
ISR (PCINT1_vect){
// прерывание энкодера
191
PORTD=0;
//обнулить порт DDS при любых манипуляциях
192
up_down((PINC&1)^((PINC&2)>>1));
//конечная обработка в другой функции
193
check_regim();
//проверка и установка режимов генератора
194
}
195
196
ISR (PCINT0_vect){
// прерывание обработчик кнопок
197
PORTD=0;
//обнулить порт DDS при любых манипуляциях
198
if
((PINB&(1<<0))==0){
// обработчик кнопки энкодера (8 пин arduino)
199
while
((PINB&(1<<0))==0);
// ждать пока отпустят кнопку
200
if
(regim==6) { regim=5; check_regim();
return
; }
201
switch
(shag){
//установка шага регулировки
202
case
1: shag=10;
break
;
203
case
10: shag=100;
break
;
204
case
100: shag=1000;
break
;
205
case
1000: shag=1;
206
}
207
check_regim();
208
}
//конец если нажата кнопка энкодера
209
if
((PINB&(1<<2))==0){
//обработчик кнопки режимов (10 пин PB2)
210
PORTD=0;
211
while
((PINB&(1<<2))==0);
// ждать пока отпустят кнопку
212
regim++;
if
(regim==7) regim=0;
213
check_regim();
214
}
215
}
//end pcint0
216
217
218
void
pwm_gen(){
//функция программирования таймера1
219
uint16_t divider=1;
//переменная коэфф. деления прескалера
220
icr = (F_CPU / freq /2 /divider);
221
byte
shifts[] = {3,3,2,2};
222
for
(
byte
i = 0; i < 4; i++){
223
if
(icr > 65536) {
224
divider <<= shifts[i];
225
icr = F_CPU / freq /2 /divider;
226
}
227
else
{
//запись в регистр прескалера
228
TCCR1B = (i+1)|(1<<WGM13);
break
;
229
}
230
}
231
ICR1=icr-1;
232
set_duty();
233
}
//end pwm_gen
234
235
236
void
loop
() {
237
if
(regim <5){ PORTB&= ~(1<<7);
// флаг ухода в цикл DDS
238
DDRD=0xFF;
//set D port as output
239
uint32_t temp=(
float
)freq /0.095367431640625;
//пересчёт частоты
240
ad2=temp>>16; ad1=temp>>8; ad0=temp;
241
switch
(regim){
242
case
0: signalOUT(sinewave);
break
;
243
case
1: signalOUT(trianglewave);
break
;
244
case
2: signalOUT(squarewave);
break
;
245
case
3: signalOUT(sawtoothwave);
break
;
246
case
4: signalOUT(rewsawtoothwave);
247
}
//end switch
248
DDRD=0; PORTD=0;
249
}
//end if (regim<5)
250
251
}
252
253
254
void
monitor_out(){
255
Serial
.print(
"FReq="
);
256
if
(freq <10000) {
Serial
.print(freq);
Serial
.print(
"Hz"
); }
257
if
(freq >10000) {
Serial
.print((
float
)freq/1000,3);
Serial
.print(
"kHz"
); }
258
Serial
.print(
" Shag="
);
Serial
.print(shag);
259
switch
(regim){
260
case
0:
Serial
.print(
" Sinus"
);
break
;
261
case
1:
Serial
.print(
" Triangle"
);
break
;
262
case
2:
Serial
.print(
" Meandr"
);
break
;
263
case
3:
Serial
.print(
" Sawtooth"
);
break
;
264
case
4:
Serial
.print(
" REWSawtooth"
);
break
;
265
case
5:
Serial
.print(
" Square_Timer"
);
Serial
.print(
" Duty="
);
Serial
.print(duty);
Serial
.print(
"% "
);
break
;
266
case
6:
Serial
.print(
" Duty_Timer"
);
Serial
.print(
" Duty="
);
Serial
.print(duty);
Serial
.print(
"% "
);
267
}
268
Serial
.println();
269
270
}
//end monitor out
271
272
273
void
set_duty(){
274
//float ocr,delta_ocr;
275
if
(regim==6&&ICR1<100)
return
;
276
if
(regim==5 && ICR1<100){
277
OCR1A=ICR1/2; duty=50;
278
return
;
279
}
280
static
uint16_t ocr;
281
ocr=(uint32_t)ICR1*duty/100;
282
if
(ocr < 1) {
// если значение оср менее 1, то
283
while
(ocr<1) {
// менять скважность пока оср не станет больше ноля
284
(duty>50)? duty-- : duty++ ;
285
ocr=(uint32_t)ICR1*duty/100;
286
}
287
}
288
OCR1A=ocr;
289
}
290
291
void
check_regim(){
//
292
if
(regim <5){
if
(freq > 100000) freq=100000;
293
TCCR1B=0;
//отключить генерацию таймера
294
TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
295
}
// end if regim <5
296
297
if
(regim > 4) {
298
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
299
if
(TCCR1B==17 && ICR1<2800){
300
ICR1=icr; freq= (
float
) 8000000UL/ICR1;
301
set_duty();
302
}
303
else
{ pwm_gen(); }
// запуск функции вывода частоты через таймер
304
}
305
PORTB|= 1<<7;
//флажок для выхода из цикла DDS
306
monitor_out();
//вывод информации
307
}
Тех. Характеристики генерации таймером:
Генерация прямоугольного сигнала 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).
Версия под дисплей NOKIA 5110
Дисплей подключен на пины A2-A5. Других отличий от сериальной нет. Библа для дисплея LCD5510 от ринки-динка, но подпатченная нашим форумчанином ssvs111 что б дисплей работал на 4 пинах, ссылка на пропатченую версию была тут Скетч:
001
/*Программа генератора с регулируемой частотой и скважностью для МК ATMEGA328
002
* Для синтезатора частоты была позаимствована функция на ассемблере из источника
003
* <a href="http://www.scienceprog.com/avr-dds-signal-generator-v20/" title="http://www.scienceprog.com/avr-dds-signal-generator-v20/" rel="nofollow">http://www.scienceprog.com/avr-dds-signal-generator-v20/</a>
004
*/
005
006
const
PROGMEM uint8_t sinewave[]=
007
{
008
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
009
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
010
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
011
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
012
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
013
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
014
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
015
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
016
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
017
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
018
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
019
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
020
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
021
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
022
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
023
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
024
};
025
026
const
PROGMEM uint8_t squarewave[]=
027
{
028
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
029
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
030
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
031
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
032
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
033
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
034
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
035
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
036
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
037
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
038
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
039
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
040
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
041
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
042
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
043
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
044
};
045
046
const
PROGMEM uint8_t trianglewave[]=
047
{
048
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
049
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
050
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
051
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
052
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
053
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
054
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
055
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
056
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
057
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
058
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
059
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
060
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
061
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
062
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
063
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
064
};
065
066
const
PROGMEM uint8_t sawtoothwave[]=
067
{
068
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
069
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
070
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
071
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
072
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
073
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
074
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
075
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
076
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
077
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
078
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
079
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
080
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
081
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
082
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
083
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
084
};
085
086
const
PROGMEM uint8_t rewsawtoothwave[]=
087
{
088
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
089
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
090
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
091
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
092
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
093
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
094
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
095
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
096
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
097
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
098
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
099
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
100
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
101
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
102
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
103
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
104
};
105
106
107
const
PROGMEM
char
musor_mass[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
108
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
109
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
110
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
111
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
112
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
113
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
114
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
115
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
116
};
117
118
// глобальные переменные
119
volatile int32_t freq=1000;
// частота генератора по умолчанию, Герц
120
volatile uint16_t shag=100;
//шаг переключения частоты,значение по умолчанию
121
volatile uint8_t regim=5;
// режим работы по умолчанию (0)синус DDS, (1)треугольник DDS,
122
//(2)прямоугольник DDS, (3)правая пила DDS, (4) левая пила DDS. (5) частота генерации таймером
123
// (6) скважность генерации таймером
124
volatile uint8_t ad2,ad1,ad0;
//байты частоты для функции на ассемблере
125
volatile uint8_t duty=50;
//скважность (коэффициент заполнения), процентов по умолчанию
126
volatile uint32_t icr=0;
//переменная для работы с регистром ICR1
127
128
#include <LCD5110_SSVS.h>
129
extern
uint8_t SmallFont[];
130
extern
uint8_t MediumNumbers[];
131
LCD5110 lcd(A5,A4,A3,A2);
132
133
// DDS algorithm
134
static
inline
void
signalOUT(
const
uint8_t *signal){
135
asm volatile(
"eor r18, r18 ;r18<-0"
"\n\t"
136
"eor r19, r19 ;r19<-0"
"\n\t"
137
"1:"
"\n\t"
138
"add r18, %0 ;1 cycle"
"\n\t"
139
"adc r19, %1 ;1 cycle"
"\n\t"
140
"adc %A3, %2 ;1 cycle"
"\n\t"
141
"lpm ;3 cycles"
"\n\t"
142
"out %4, __tmp_reg__ ;1 cycle"
"\n\t"
143
"sbis %5, 7 ;1 cycle if no skip"
"\n\t"
// если стоит бит в переменной %5, то
144
"rjmp 1b ;2 cycles. Total 10 cycles"
"\n\t"
//пропустить эту команду ухода в цикл
145
:
146
:
"r"
(ad0),
"r"
(ad1),
"r"
(ad2),
"e"
(signal),
"I"
(_SFR_IO_ADDR(PORTD)),
"I"
(_SFR_IO_ADDR(PORTB))
147
:
"r18"
,
"r19"
);
148
}
149
150
void
setup
(){
151
lcd.InitLCD();
152
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTD=musor_mass[n]; }
153
TCCR0B=0;
//заглушить 0 таймер что б не мешал DDS
154
PCICR=(1<<PCIE1)|(1<<PCIE0);
//разрешить прерывания PCINT 0,1
155
PCMSK1=1<<PCINT9;
// По сигналу на А1 создавать прерывание (енкодер)
156
PCMSK0=(1<<PCINT0)|(1<<PCINT2);
// По сигналу на 8 , 10 пине ардуины создавать прерывание
157
PORTB|=(1<<PB0)|(1<<PB2);
// подтяжка 8,10 пин
158
TCCR1A=0; TCCR1B=0;
159
check_regim();
//проверка и настройка режимов
160
}
//end setup
161
162
void
up_down(boolean x){
// функция режимов енкодера
163
// если режим генерации таймером и частота более 2800 Гц, то
164
if
(TCCR1B==17 && ICR1<2800 && regim==5){
165
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
166
if
(icr<100 && shag > 10) shag=10;
167
if
(icr<10 && shag > 1) shag=1;
168
icr-=shag;
169
if
(icr<2) icr=2; }
170
else
{
if
(icr > 1800 && shag >100) shag =100;
171
icr+=shag ; }
172
return
;
173
}
174
if
(regim==6){
// если енкодер в режиме регулировки скважности, то
175
// если возможна регулировка с точностью не хуже 1%, то:
176
if
(ICR1>100){
177
// Если увеличение скважности
178
if
(x){
if
(duty<100) {duty++; } }
179
// Если уменьшение скважности
180
if
(!x){
if
(duty>0) {duty--; }}
181
}
182
else
{
// если точность 1% уже невозможна, то будем крутить регистр OCR1A напрямую
183
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
184
else
{
if
(OCR1A>0) {OCR1A--; } }
185
if
(OCR1A>ICR1) OCR1A=ICR1-1;
186
duty=(uint32_t)100*OCR1A/ICR1;
187
}
188
return
;
189
}
//конец настройки режима скважности
190
// в остальных режимах DDS :
191
x? freq+=shag : freq-=shag;
192
if
(freq < 1) freq=1;
193
}
194
195
ISR (PCINT1_vect){
// прерывание энкодера
196
PORTD=0;
//обнулить порт DDS при любых манипуляциях
197
up_down((PINC&1)^((PINC&2)>>1));
//конечная обработка в другой функции
198
check_regim();
//проверка и установка режимов генератора
199
}
200
201
ISR (PCINT0_vect){
// прерывание обработчик кнопок
202
PORTD=0;
//обнулить порт DDS при любых манипуляциях
203
if
((PINB&(1<<0))==0){
// обработчик кнопки энкодера (8 пин arduino)
204
while
((PINB&(1<<0))==0);
// ждать пока отпустят кнопку
205
if
(regim==6) { regim=5; check_regim();
return
; }
206
switch
(shag){
//установка шага регулировки
207
case
1: shag=10;
break
;
208
case
10: shag=100;
break
;
209
case
100: shag=1000;
break
;
210
case
1000: shag=1;
211
}
212
check_regim();
213
}
//конец если нажата кнопка энкодера
214
if
((PINB&(1<<2))==0){
//обработчик кнопки режимов (10 пин PB2)
215
PORTD=0;
216
while
((PINB&(1<<2))==0);
// ждать пока отпустят кнопку
217
regim++;
if
(regim==7) regim=0;
218
check_regim();
219
}
220
}
//end pcint0
221
222
223
void
pwm_gen(){
//функция программирования таймера1
224
uint16_t divider=1;
//переменная коэфф. деления прескалера
225
icr = (F_CPU / freq /2 /divider);
226
byte
shifts[] = {3,3,2,2};
227
for
(
byte
i = 0; i < 4; i++){
228
if
(icr > 65536) {
229
divider <<= shifts[i];
230
icr = F_CPU / freq /2 /divider;
231
}
232
else
{
//запись в регистр прескалера
233
TCCR1B = (i+1)|(1<<WGM13);
break
;
234
}
235
}
236
ICR1=icr-1;
237
set_duty();
238
}
//end pwm_gen
239
240
241
void
loop
() {
242
if
(regim <5){ PORTB&= ~(1<<7);
// флаг ухода в цикл DDS
243
DDRD=0xFF;
//set D port as output
244
uint32_t temp=(
float
)freq /0.095367431640625;
//пересчёт частоты
245
ad2=temp>>16; ad1=temp>>8; ad0=temp;
246
switch
(regim){
247
case
0: signalOUT(sinewave);
break
;
248
case
1: signalOUT(trianglewave);
break
;
249
case
2: signalOUT(squarewave);
break
;
250
case
3: signalOUT(sawtoothwave);
break
;
251
case
4: signalOUT(rewsawtoothwave);
252
}
//end switch
253
DDRD=0; PORTD=0;
254
}
//end if (regim<5)
255
256
}
257
258
259
void
monitor_out(){
260
String freqstr, dutystr,stepstr;
261
dutystr= String(
"Duty="
+ String(duty)+
"%"
);
262
//pwmstr=String("PWM Mode "+ String(duty)+ "%");
263
stepstr=String(
"Step= "
+String(shag));
264
if
(freq <10000) {freqstr= String(
"Frequency, Hz"
); }
265
if
(freq >10000) {freqstr= String(
"Frequency, kHz"
); }
266
lcd.clrScr();
267
lcd.setFont(SmallFont);
268
lcd.print(freqstr,LEFT,0);
269
lcd.setFont(MediumNumbers);
270
if
(freq <10000) { lcd.printNumI(freq, CENTER, 8); }
271
if
(freq >10000u && freq < 1000000ul ) { lcd.printNumF( ((
float
)freq/1000),3 ,CENTER, 8); }
272
if
(freq >=1000000ul ) { lcd.printNumF( ((
float
)freq/1000),2 ,CENTER, 8); }
273
lcd.setFont(SmallFont);
274
switch
(regim){
275
case
0: lcd.print(
"Sinus DDS"
,CENTER, 32);
break
;
276
case
1: lcd.print(
"Triangle DDS"
,CENTER, 32);
break
;
277
case
2: lcd.print(
"Meandr DDS"
,CENTER, 32);
break
;
278
case
3: lcd.print(
"Pila1 DDS"
,CENTER, 32);
break
;
279
case
4: lcd.print(
"Pila2 DDS"
,CENTER, 32);
break
;
280
case
5: lcd.print(
"PWM Mode"
,CENTER,32);
break
;
281
case
6: lcd.print(
"Duty Mode"
, CENTER, 32);
282
}
283
if
(regim==6) lcd.print(dutystr, CENTER, 40);
284
else
lcd.print(stepstr, CENTER, 40);
285
}
//end monitor out
286
287
288
void
set_duty(){
289
//float ocr,delta_ocr;
290
if
(regim==6&&ICR1<100)
return
;
291
if
(regim==5 && ICR1<100){
292
OCR1A=ICR1/2; duty=50;
293
return
;
294
}
295
static
uint16_t ocr;
296
ocr=(uint32_t)ICR1*duty/100;
297
if
(ocr < 1) {
// если значение оср менее 1, то
298
while
(ocr<1) {
// менять скважность пока оср не станет больше ноля
299
(duty>50)? duty-- : duty++ ;
300
ocr=(uint32_t)ICR1*duty/100;
301
}
302
}
303
OCR1A=ocr;
304
}
305
306
void
check_regim(){
//
307
if
(regim <5){
if
(freq > 100000) freq=100000;
308
TCCR1B=0;
//отключить генерацию таймера
309
TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
310
}
// end if regim <5
311
312
if
(regim > 4) {
313
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
314
if
(TCCR1B==17 && ICR1<2800){
315
ICR1=icr; freq= (
float
) 8000000UL/ICR1;
316
set_duty();
317
}
318
else
{ pwm_gen(); }
// запуск функции вывода частоты через таймер
319
}
320
PORTB|= 1<<7;
//флажок для выхода из цикла DDS
321
monitor_out();
//вывод информации
322
}
Ну пока без энкодера. Проба пера. ЦАП и выходные цепи использую с ранее сделаного генератора с Паяльника, который меня не устроил.
Ну и для полной коллекции -последняя версия с более удобной распиновкой дисплея и кнопок для печатной платы. Так же вроде подправил все мелкие недочёты.
Из существенный отличий -использовал 8 ногу для включения/отключения подсветки. Либо можно что-то ещё включать, например сделать управляемое смещение, или переключать выход релюшкой , или ещё что - пригодится в любом случае. Включается комбинацией- нажать кнопку энкодера, + удерживая её нажать кнопку режима. Затем отпустить кнопку энкодера, и последней отпустить кнопку режима. Скетч:
001
/* Генератор с регулируемой частотой и скважностью на Ардуино Уно v 2.3
002
* Распиновка: Энкодер A0,A1 (in)
003
* Кнопка энкодера A3 (in)
004
* Кнопка "режим" A2 (in)
005
* Подсветка 8 (out) (вывод BL дисплея)
006
* Дисплей nokia 5110 - 13,12,11,10 (out)
007
* Выход генератора 0..7 + 9 (out)
008
*/
009
010
011
const
PROGMEM uint8_t sinewave[]=
// массив синуса
012
{
013
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
014
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
015
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
016
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
017
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
018
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
019
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
020
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
021
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
022
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
023
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
024
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
025
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
026
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
027
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
028
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
029
};
030
031
const
PROGMEM uint8_t squarewave[]=
//массив меандра
032
{
033
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
034
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
035
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
036
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
037
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
038
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
039
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
040
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
041
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
042
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
043
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
044
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
045
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
046
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
047
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
048
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
049
};
050
051
const
PROGMEM uint8_t trianglewave[]=
//массив треугольника
052
{
053
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
054
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
055
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
056
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
057
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
058
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
059
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
060
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
061
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
062
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
063
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
064
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
065
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
066
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
067
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
068
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
069
};
070
071
const
PROGMEM uint8_t sawtoothwave[]=
//массив пила1
072
{
073
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
074
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
075
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
076
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
077
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
078
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
079
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
080
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
081
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
082
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
083
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
084
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
085
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
086
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
087
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
088
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
089
};
090
091
const
PROGMEM uint8_t rewsawtoothwave[]=
//массив пила2
092
{
093
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
094
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
095
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
096
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
097
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
098
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
099
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
100
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
101
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
102
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
103
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
104
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
105
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
106
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
107
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
108
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
109
};
110
111
112
const
PROGMEM
char
musor_mass[]=
//массив для подгонки адреса в флэш-памяти
113
{
114
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
115
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
116
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
117
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
118
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
119
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
120
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
121
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
122
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
123
};
124
125
126
volatile int32_t freq=1000;
// частота по умолчанию
127
volatile uint16_t shag=100;
//шаг энкодера по умолчанию
128
volatile uint8_t regim=5;
//режим генератора по умолчанию
129
volatile uint8_t monitor_flag;
// флаг для вывода на дисплей
130
volatile uint8_t ad2,ad1,ad0;
//служебные байты для функции на ассемблере
131
volatile uint8_t duty=50;
//скважность
132
volatile uint32_t icr=0;
//переменная для управления регистром сравнения таймера1
133
134
#include <LCD5110_SSVS.h>
135
extern
uint8_t SmallFont[];
136
extern
uint8_t MediumNumbers[];
137
LCD5110 lcd(10,11,12,13);
138
139
// DDS algorithm
140
static
inline
void
signalOUT(
const
uint8_t *signal){
141
asm volatile(
"eor r18, r18 ;r18<-0"
"\n\t"
142
"eor r19, r19 ;r19<-0"
"\n\t"
143
"1:"
"\n\t"
144
"add r18, %0 ;1 cycle"
"\n\t"
145
"adc r19, %1 ;1 cycle"
"\n\t"
146
"adc %A3, %2 ;1 cycle"
"\n\t"
147
"lpm ;3 cycles"
"\n\t"
148
"out %4, __tmp_reg__ ;1 cycle"
"\n\t"
149
"sbis %5, 7 ;1 cycle if no skip"
"\n\t"
150
"rjmp 1b ;2 cycles. Total 10 cycles"
"\n\t"
151
:
152
:
"r"
(ad0),
"r"
(ad1),
"r"
(ad2),
"e"
(signal),
"I"
(_SFR_IO_ADDR(PORTD)),
"I"
(_SFR_IO_ADDR(PORTB))
153
:
"r18"
,
"r19"
);
154
}
155
156
void
setup
(){
157
lcd.InitLCD();
158
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTD=musor_mass[n]; }
159
PORTC|=(1<<PC2)|(1<<PC3);
//подтяжка кнопок на A2 A3
160
TCCR0B=0; TCCR1A=0; TCCR1B=0;
161
DDRB|=1<<PB0;
// 8 пин -включение подсветки дисплея = выход
162
PCICR=(1<<PCIE1);
//включить прерывание PCINT
163
PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11);
//выбор пинов прерывания (A1,A2,A3)
164
check_regim();
165
}
//end setup
166
167
168
void
up_down(boolean x){
// управление регулировками
169
170
if
(TCCR1B==17 && ICR1<2800 && regim==5){
171
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
172
if
(icr<100 && shag > 10) shag=10;
173
if
(icr<10 && shag > 1) shag=1;
174
icr-=shag;
175
if
(icr<2) icr=2; }
176
else
{
if
(icr > 1800 && shag >100) shag =100;
177
icr+=shag ; }
178
return
;
179
}
180
if
(regim==6){
181
182
if
(ICR1>100){
183
184
if
(x){
if
(duty<100) {duty++; } }
185
186
if
(!x){
if
(duty>0) {duty--; }}
187
}
188
else
{
189
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
190
else
{
if
(OCR1A>0) {OCR1A--; } }
191
if
(OCR1A>ICR1) OCR1A=ICR1-1;
192
duty=(uint32_t)100*OCR1A/ICR1;
193
}
194
return
;
195
}
196
197
x? freq+=shag : freq-=shag ;
198
if
(freq < 1) freq=1;
199
}
200
201
202
203
// ПРЕРЫВАНИЕ от кнопок и энкодера
204
ISR (PCINT1_vect){
205
PCMSK1=0; PCICR=0;
206
PORTD=0;
207
// если нажата кнопка энкодера
208
if
((PINC&(1<<3))==0){
209
while
((PINC&(1<<3))==0);
// подождать до тех пор, когда кнопку отпустят
210
211
//блок вкл/отк подсветки дисплея (8 пин ардуино)
212
if
((PINC&(1<<2))==0){
//если после этого нажата кнопка режимов,
213
PINB|=1<<PB0;
//менять состояние 8 пина
214
while
((PINC&(1<<2))==0);
// теперь подождать пока отпустят кнопку режим
215
PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
return
;
216
}
217
if
(regim==6) { regim=5; check_regim(); PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
return
; }
218
switch
(shag){
219
case
1: shag=10;
break
;
220
case
10: shag=100;
break
;
221
case
100: shag=1000;
break
;
222
case
1000: shag=1;
223
}
224
check_regim();
225
PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
return
;
226
}
227
if
((PINC&(1<<2))==0){
// если нажата кнопка режимов
228
PORTD=0;
229
while
((PINC&(1<<2))==0);
230
regim++;
if
(regim==7) regim=0;
231
check_regim(); PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11);PCICR=(1<<PCIE1);
return
;
232
}
233
234
// если кнопки не нажимались -значит крутили энкодер:
235
up_down( (PINC&1)^((PINC&2)>>1) );
//отправить считанное энкодеров в другую функцию
236
// если при вращении счёт идёт не в нужную сторону, то вставить символ '!' up_down(!
237
check_regim();
238
PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
//включить прерывания
239
240
}
//конец функции обработки прерываний от кнопок PCINT1_vect
241
242
243
void
pwm_gen(){
//настройка таймера1
244
uint16_t divider=1;
245
icr = (16000000ul / freq /2 /divider);
246
byte
shifts[] = {3,3,2,2};
247
for
(
byte
i = 0; i < 4; i++){
248
if
(icr > 65536) {
249
divider <<= shifts[i];
250
icr = 16000000ul / freq /2 /divider;
251
}
252
else
{
253
TCCR1B = (i+1)|(1<<WGM13);
break
;
254
}
255
}
256
ICR1=icr-1;
257
set_duty();
258
}
//end pwm_gen
259
260
261
void
loop
() {
262
if
(monitor_flag) { monitor_flag=0; monitor_out(); }
263
if
(regim <5){ PORTB&= ~(1<<7);
264
DDRD=0xFF;
//set D port as output
265
uint32_t temp=(
float
)freq /0.095367431640625;
266
ad2=temp>>16; ad1=temp>>8; ad0=temp;
267
switch
(regim){
268
case
0: signalOUT(sinewave);
break
;
269
case
1: signalOUT(trianglewave);
break
;
270
case
2: signalOUT(squarewave);
break
;
271
case
3: signalOUT(sawtoothwave);
break
;
272
case
4: signalOUT(rewsawtoothwave);
273
}
//end switch
274
DDRD=0; PORTD=0;
275
276
}
//end if (regim<5)
277
278
}
279
280
281
void
monitor_out(){
282
String dutystr,stepstr;
283
dutystr= String(
"Duty="
+ String(duty)+
"%"
);
284
stepstr=String(
"Step= "
+String(shag));
285
lcd.clrScr();
286
//Вывод первой строчки
287
lcd.setFont(SmallFont);
288
if
(freq <10000) {lcd.print(
"Frequency, Hz"
,LEFT,0 ); }
289
if
(freq >=10000) {lcd.print(
"Frequency, kHz"
,LEFT,0 ); }
290
//Вывод второй строчки
291
lcd.setFont(MediumNumbers);
292
if
(freq <10000) { lcd.printNumI(freq, CENTER, 8); }
293
if
(freq >=10000u && freq < 1000000ul ) { lcd.printNumF( ((
float
)freq/1000),3 ,CENTER, 8); }
294
if
(freq >=1000000ul ) { lcd.printNumF( ((
float
)freq/1000),2 ,CENTER, 8); }
295
//Вывод третьей строчки
296
lcd.setFont(SmallFont);
297
switch
(regim){
298
case
0: lcd.print(
"Sinus DDS"
,CENTER, 32);
break
;
299
case
1: lcd.print(
"Triangle DDS"
,CENTER, 32);
break
;
300
case
2: lcd.print(
"Meandr DDS"
,CENTER, 32);
break
;
301
case
3: lcd.print(
"Pila1 DDS"
,CENTER, 32);
break
;
302
case
4: lcd.print(
"Pila2 DDS"
,CENTER, 32);
break
;
303
case
5: lcd.print(
"PWM Mode"
,CENTER,32);
break
;
304
case
6: lcd.print(
"Duty Mode"
, CENTER, 32);
305
}
306
//Вывод четвёртой строчки
307
if
(regim==6) lcd.print(dutystr, CENTER, 40);
308
else
lcd.print(stepstr, CENTER, 40);
309
}
//end monitor out
310
311
312
void
set_duty(){
313
//float ocr,delta_ocr;
314
if
(regim==6&&ICR1<100)
return
;
315
if
(regim==5 && ICR1<100){
316
OCR1A=ICR1/2; duty=50;
317
return
;
318
}
319
static
uint16_t ocr;
320
ocr=(uint32_t)ICR1*duty/100;
321
OCR1A=ocr;
322
}
323
324
void
check_regim(){
// проверка и установка режимов генератора
325
if
(regim <5){
if
(freq > 100000) freq=100000;
326
TCCR1B=0;
327
TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
328
}
// end if regim <5
329
330
if
(regim > 4) {
331
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
332
if
(TCCR1B==17 && ICR1<2800){
333
ICR1=icr; freq= (
float
) 8000000UL/ICR1;
334
set_duty();
335
}
336
else
{ pwm_gen(); }
337
}
338
PORTB|= 1<<7;
339
monitor_flag=1;
340
}
Как и первый вариант генератора сделал в виде шилда для UNO.
dimax, спасибо, замечательное устройство.
Предполагаемую схему на ОУ для смещения нуля попробовал, но убедился, что LM358 для этих целей не подходит. Заказал LM833, а пока собрал схему в пректически неизменном варианте:
dimax, спасибо
Кстати, не получаеться исправить в новой версии кода сам енкодер. Пробовал вставить исправления не получилось считает по два.
Етот генератор можно использовать как медицынский прибор при частоте 10Кгц и Синусе после усиления до 60В получиться аналог русского миостимулятора Мион-1
ftor, фикс двойного щелчка энкодера
1
void
up_down(boolean x){
// управление регулировками
2
static
boolean n=0;
if
(n=!n){
return
;}
//далее без изменений
Дело было вечером, делать было нечего.. А сделать что-то хотелось. Решил добавить в поделку пару полезных функций:
Генератор V2.4, в код добавлены частотометр и вольтметр.
Частотометр -измеряет методом тактирования первого таймера от источника сигнала. Измеряемый диапазон: 1Гц ... 7,999 МГц*

* при превышении верхнего диапазона частотометр будет выдавать неправильные показания. Точность измерений зависит от точности установленного на плате кварцевого резонатора.
Вход подключать к пину D5, в идеале отключая от этой ноги резистор из схемы ЦАП. Но можно и не отключать, -работает и так.
Вольтметр - Измеряет постоянное напряжение на шине Vcc и на пинах A4, A5. Используется оверсемплинг до псевдо 14 бит разрешения. , Вольтметр самокалибруется по встроенному в М.К. И.О.Н. на 1,1 вольта.
Измеряемое напряжение от 0в до напряжения питания М.К. (Vcc), отображаемое разрешение -1мв.
При загрузке можно кнопками выбрать что запускать, вольтметр или частотометр. Генератор запустится через 5 секунд по умолчанию, если выбор не сделан. Выхода из выбранных режимов не предусмотрено, только перезапуск по питанию или ресетом.
Обновлённая схема:
Скетч:
001
/* Генератор, частотометр, вольтметр на Ардуино Уно v 2.4
002
* Распиновка: Энкодер A0,A1 (in)
003
* Кнопка энкодера A2 (in)
004
* Кнопка "режим" A3 (in)
005
* Подсветка 8 (out) (вывод BL дисплея)
006
* Дисплей nokia 5110 - 13,12,11,10 (out)
007
* Выход генератора 0..7 + 9 (out)
008
* Вход частотометра - 5 (in)
009
* Входы вольтметра -А4,А5 (in)
010
*/
011
012
013
const
PROGMEM uint8_t sinewave[]=
// массив синуса
014
{
015
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
016
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
017
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
018
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
019
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
020
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
021
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
022
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
023
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
024
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
025
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
026
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
027
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
028
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
029
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
030
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
031
};
032
033
const
PROGMEM uint8_t squarewave[]=
//массив меандра
034
{
035
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
036
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
037
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
038
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
039
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
040
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
041
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
042
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
043
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
044
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
045
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
046
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
047
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
048
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
049
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
050
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
051
};
052
053
const
PROGMEM uint8_t trianglewave[]=
//массив треугольника
054
{
055
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
056
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
057
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
058
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
059
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
060
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
061
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
062
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
063
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
064
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
065
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
066
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
067
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
068
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
069
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
070
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
071
};
072
073
const
PROGMEM uint8_t sawtoothwave[]=
//массив пила1
074
{
075
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
076
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
077
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
078
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
079
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
080
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
081
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
082
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
083
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
084
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
085
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
086
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
087
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
088
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
089
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
090
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
091
};
092
093
const
PROGMEM uint8_t rewsawtoothwave[]=
//массив пила2
094
{
095
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
096
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
097
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
098
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
099
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
100
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
101
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
102
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
103
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
104
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
105
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
106
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
107
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
108
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
109
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
110
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
111
};
112
113
114
const
PROGMEM
char
musor_mass[]=
//массив для подгонки адреса в флэш-памяти
115
{
116
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
117
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
118
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
119
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
120
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
121
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
122
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
123
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
124
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
125
};
126
127
128
volatile int32_t freq=1000;
// частота по умолчанию
129
volatile uint32_t icr=0;
//переменная для управления регистром сравнения таймера1
130
volatile uint16_t shag=100;
//шаг энкодера по умолчанию
131
volatile uint16_t int_tic=0;
132
volatile uint8_t regim=5;
//режим генератора по умолчанию
133
volatile uint8_t monitor_flag;
// флаг для вывода на дисплей
134
volatile uint8_t ad2,ad1,ad0;
//служебные байты для функции на ассемблере
135
volatile uint8_t duty=50;
//скважность
136
volatile uint8_t main_flag=0;
//флаг работа в режиме генератора или нет
137
138
#define int_on() PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
139
//включить прерывание PCINT1, выбор пинов прерывания A1,A2,A3
140
#define int_off() PCMSK1=0; PCICR=0; //отключить PCINT1
141
#include <LCD5110_SSVS.h>
142
extern
uint8_t SmallFont[];
143
extern
uint8_t MediumNumbers[];
144
LCD5110 lcd(10,11,12,13);
145
146
// DDS algorithm
147
static
inline
void
signalOUT(
const
uint8_t *signal){
148
asm volatile(
"eor r18, r18 ;r18<-0"
"\n\t"
149
"eor r19, r19 ;r19<-0"
"\n\t"
150
"1:"
"\n\t"
151
"add r18, %0 ;1 cycle"
"\n\t"
152
"adc r19, %1 ;1 cycle"
"\n\t"
153
"adc %A3, %2 ;1 cycle"
"\n\t"
154
"lpm ;3 cycles"
"\n\t"
155
"out %4, __tmp_reg__ ;1 cycle"
"\n\t"
156
"sbis %5, 7 ;1 cycle if no skip"
"\n\t"
157
"rjmp 1b ;2 cycles. Total 10 cycles"
"\n\t"
158
:
159
:
"r"
(ad0),
"r"
(ad1),
"r"
(ad2),
"e"
(signal),
"I"
(_SFR_IO_ADDR(PORTD)),
"I"
(_SFR_IO_ADDR(PORTB))
160
:
"r18"
,
"r19"
);
161
}
162
163
void
setup
(){
164
lcd.InitLCD();
165
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTD=musor_mass[n]; }
166
PORTD=0; DDRD=0; TCCR1A=0; TCCR1B=0; TIMSK1=0;
167
PORTC|=(1<<PC2)|(1<<PC3);
//подтяжка кнопок на A2 A3
168
DDRB|=1<<PB0;
// 8 пин -включение подсветки дисплея = выход
169
main_screen();
//стартовое сообщение и выбор задач
170
//далее загрузка генератора по умолчанию
171
TCCR0B=0; check_regim();
172
int_on();
//включить прерывание PCINT1
173
main_flag=1;
174
}
//end setup
175
176
177
void
up_down(boolean x){
// управление регулировками
178
// static boolean n=0; if (n=!n){return;} // снять ремарку для энкодеров с двойным щелчком
179
if
(TCCR1B==17 && ICR1<2800 && regim==5){
180
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
181
if
(icr<100 && shag > 10) shag=10;
182
if
(icr<10 && shag > 1) shag=1;
183
icr-=shag;
184
if
(icr<2) icr=2; }
185
else
{
if
(icr > 1800 && shag >100) shag =100; icr+=shag ; }
186
return
; }
187
if
(regim==6){
if
(ICR1>100){
188
if
(x){
if
(duty<100) {duty++; } }
189
if
(!x){
if
(duty>0) {duty--; }} }
190
else
{
191
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
192
else
{
if
(OCR1A>0) {OCR1A--; } }
193
if
(OCR1A>ICR1) OCR1A=ICR1-1;
194
duty=(uint32_t)100*OCR1A/ICR1; }
195
return
;
196
}
197
x? freq+=shag : freq-=shag ;
198
if
(freq < 1) freq=1;
199
}
200
201
202
////////////////////////////////////////////////////////////////
203
//****** ПРЕРЫВАНИЕ от кнопок и энкодера*******/////////////////
204
ISR (PCINT1_vect){
205
int_off(); PORTD=0;
206
///блок для обработки событий не в режиме генератора///
207
if
(main_flag==0) {
if
((PINC&(1<<3))==0){
while
((PINC&(1<<3))==0);
//вкл. подсветки
208
if
((PINC&(1<<2))==0){ PINB|=1<<PB0;
while
((PINC&(1<<2))==0);}
209
int_on();
return
;
//выходить если не в режиме генератора
210
}}
211
//далее всё в режиме генаратора
212
// если нажата кнопка энкодера
213
if
((PINC&(1<<3))==0){
214
while
((PINC&(1<<3))==0);
// подождать до тех пор, когда кнопку отпустят
215
//блок вкл/отк подсветки дисплея (8 пин ардуино)
216
if
((PINC&(1<<2))==0){
//если после этого нажата кнопка режимов,
217
PINB|=1<<PB0;
//менять состояние 8 пина
218
while
((PINC&(1<<2))==0);
// теперь подождать пока отпустят кнопку режим
219
int_on();
return
;
220
}
221
if
(regim==6) { regim=5; check_regim(); int_on();
return
; }
222
switch
(shag){
223
case
1: shag=10;
break
;
224
case
10: shag=100;
break
;
225
case
100: shag=1000;
break
;
226
case
1000: shag=1;
break
;
227
} check_regim(); int_on();
return
;
228
}
//конец блока *если нажата кнопка энкодера*
229
if
((PINC&(1<<2))==0){
// если нажата кнопка режимов
230
PORTD=0;
231
while
((PINC&(1<<2))==0);
232
regim++;
if
(regim==7) regim=0;
233
check_regim(); int_on();
return
;
234
}
//конец блока *если нажата кнопка режимов*
235
// если кнопки не нажимались -значит крутили энкодер:
236
up_down( ! (PINC&1)^((PINC&2)>>1) );
//отправить считанное энкодеров в другую функцию
237
// если при вращении счёт идёт не в нужную сторону, то (вставить/убрать) символ '!' up_down(!
238
check_regim(); int_on();
239
}
//конец функции обработки прерываний от кнопок PCINT1_vect
240
////////////////////КОНЕЦ_ПРЕРЫВАНИЕ_от_кнопок_и_энкодера///////////////////////////////
241
242
243
void
pwm_gen(){
//настройка таймера1
244
uint16_t divider=1;
245
icr = (16000000ul / freq /2 /divider);
246
byte
shifts[] = {3,3,2,2};
247
for
(
byte
i = 0; i < 4; i++){
248
if
(icr > 65536) {
249
divider <<= shifts[i];
250
icr = 16000000ul / freq /2 /divider; }
251
else
{ TCCR1B = (i+1)|(1<<WGM13);
break
; } }
252
ICR1=icr-1;
253
set_duty();
254
}
//end pwm_gen
255
256
257
void
loop
() {
258
if
(monitor_flag) { monitor_flag=0; monitor_out(); }
259
if
(regim <5){ PORTB&= ~(1<<7);
260
DDRD=0xFF;
//set D port as output
261
uint32_t temp=(
float
)freq /0.095367431640625;
262
ad2=temp>>16; ad1=temp>>8; ad0=temp;
263
switch
(regim){
264
case
0: signalOUT(sinewave);
break
;
265
case
1: signalOUT(trianglewave);
break
;
266
case
2: signalOUT(squarewave);
break
;
267
case
3: signalOUT(sawtoothwave);
break
;
268
case
4: signalOUT(rewsawtoothwave);
269
}
//end switch
270
DDRD=0; PORTD=0;
271
272
}
//end if (regim<5)
273
}
//end loop
274
275
276
void
monitor_out(){
277
String dutystr,stepstr;
278
dutystr= String(
"Duty="
+ String(duty)+
"%"
);
279
stepstr=String(
"Step= "
+String(shag));
280
lcd.clrScr();
281
//Вывод первой строчки
282
lcd.setFont(SmallFont);
283
if
(freq <10000) {lcd.print(
"Frequency, Hz"
,LEFT,0 ); }
284
if
(freq >=10000) {lcd.print(
"Frequency, kHz"
,LEFT,0 ); }
285
//Вывод второй строчки
286
lcd.setFont(MediumNumbers);
287
if
(freq <10000) { lcd.printNumI(freq, CENTER, 8); }
288
if
(freq >=10000u && freq < 1000000ul ) { lcd.printNumF( ((
float
)freq/1000),3 ,CENTER, 8); }
289
if
(freq >=1000000ul ) { lcd.printNumF( ((
float
)freq/1000),2 ,CENTER, 8); }
290
//Вывод третьей строчки
291
lcd.setFont(SmallFont);
292
switch
(regim){
293
case
0: lcd.print(
"Sinus DDS"
,CENTER, 32);
break
;
294
case
1: lcd.print(
"Triangle DDS"
,CENTER, 32);
break
;
295
case
2: lcd.print(
"Meandr DDS"
,CENTER, 32);
break
;
296
case
3: lcd.print(
"Pila1 DDS"
,CENTER, 32);
break
;
297
case
4: lcd.print(
"Pila2 DDS"
,CENTER, 32);
break
;
298
case
5: lcd.print(
"PWM Mode"
,CENTER,32);
break
;
299
case
6: lcd.print(
"Duty Mode"
, CENTER, 32);
300
}
301
//Вывод четвёртой строчки
302
if
(regim==6) lcd.print(dutystr, CENTER, 40);
303
else
lcd.print(stepstr, CENTER, 40);
304
}
//end monitor out
305
306
307
void
set_duty(){
308
if
(regim==6&&ICR1<100)
return
;
309
if
(regim==5 && ICR1<100){
310
OCR1A=ICR1/2; duty=50;
311
return
; }
312
static
uint16_t ocr;
313
ocr=(uint32_t)ICR1*duty/100;
314
OCR1A=ocr;
315
}
316
317
void
check_regim(){
// проверка и установка режимов генератора
318
if
(regim <5){
if
(freq > 100000) freq=100000;
319
TCCR1B=0;
320
TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
321
}
// end if regim <5
322
if
(regim > 4) {
323
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
324
if
(TCCR1B==17 && ICR1<2800){
325
ICR1=icr; freq= (
float
) 8000000UL/ICR1;
326
set_duty(); }
327
else
{ pwm_gen(); }
328
}
329
PORTB|= 1<<7;
330
monitor_flag=1;
331
}
332
333
334
ISR (TIMER1_OVF_vect){ int_tic++; }
// прерывание частотомера
335
336
void
freq_meter(){
337
lcd.clrScr();
338
lcd.setFont(SmallFont);
339
lcd.print(
"Freq.counter"
,LEFT, 0);
340
int_on();
//включить прерывание PCINT1
341
TIMSK1 = 1<<TOIE1;
// подключить прерывание
342
uint32_t freqm=0;
// Переменная измерения частоты
343
int_tic=0; TCNT1=0; TIFR1=0;
//всё обнулить
344
while
(1){
345
TCCR1B=7;
//тактировани от входа Т1
346
delay(2000); TCCR1B=0;
347
freqm= (((uint32_t)int_tic<<16) | TCNT1)/2;
//сложить что натикало
348
int_tic=0; TCNT1 = 0;
349
lcd.clrScr(); lcd.setFont(SmallFont);
350
lcd.print(
"Freq.counter"
,LEFT, 0);
351
lcd.setFont(MediumNumbers);
352
if
(freqm <10000) { lcd.printNumI(freqm, CENTER, 8); lcd.setFont(SmallFont); lcd.print(
"Herz"
,CENTER, 32 ); }
353
if
(freqm >=10000u && freqm < 1000000ul ) { lcd.printNumF( ((
float
)freqm/1000),3 ,CENTER, 8);lcd.setFont(SmallFont); lcd.print(
"KiloHerz"
,CENTER, 32 ); }
354
if
(freqm >=1000000ul ) { lcd.printNumF( ((
float
)freqm/1000000ul),3 ,CENTER, 8); lcd.setFont(SmallFont); lcd.print(
"MegaHerz"
,CENTER, 32 ); }
355
}
356
}
357
358
//////////////////////////////////////////////////////////////////////
359
void
volt_meter() {
360
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
361
ADCSRB=0; DIDR0=48; int_on();
362
float
ain,vcc; String ainstr,vccstr;
363
lcd.clrScr(); lcd.setFont(SmallFont);
364
lcd.print(
"Volt meter"
,LEFT, 0);
365
while
(1){
366
ADMUX = (1<<REFS0)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1);
//Vcc measure
367
delay(1); vcc=(
float
)(1.1*65472) / analog_func();
368
vccstr=String(
"Vcc= "
+String(vcc,3)+
" v "
);
369
lcd.print(vccstr,LEFT, 40);
370
ADMUX = (1<<REFS0)|(1<<MUX2);
//A4 measure
371
delay(1); ain= analog_func()*vcc /65472 ;
372
ainstr=String(
"Ain4= "
+String(ain,3)+
" v "
);
373
lcd.print(ainstr,LEFT, 16);
374
ADMUX = (1<<REFS0)|(1<<MUX2)|(1<<MUX0);
//A5 measure
375
delay(1); ain= analog_func() *vcc /65472 ;
376
ainstr=String(
"Ain5= "
+String(ain,3)+
" v "
);
377
lcd.print(ainstr,LEFT, 24);
378
};
//end while
379
}
//end volt meter
380
381
uint32_t analog_func(){
382
uint32_t adc_buff=0;
383
for
(
int
n=0; n<=4095; n++ ) {
384
ADCSRA |= (1<<ADSC);
385
while
(bit_is_set(ADCSRA,ADSC));
386
adc_buff += ADC;
387
}
return
adc_buff>>=6;
388
}
389
390
/////////////////////////////////////////////////////////////////
391
void
main_screen(){
392
static
boolean flag_exit =0; uint8_t n=84;
393
lcd.setFont(SmallFont);
394
lcd.print(
"Freq.counter >"
,LEFT, 8 );
395
lcd.print(
"**************"
,LEFT,24);
396
lcd.print(
" Volt meter >"
,LEFT, 40 );
397
while
(flag_exit==0) {
//ждать нажатия кнопки режимов.
398
delay(350); lcd.clrRow(3,n); n-=6;
399
if
(n==0) flag_exit=1;
400
if
((PINC&(1<<2))==0){
// если нажата кнопка режимов
401
while
((PINC&(1<<2))==0);
402
freq_meter(); }
403
if
((PINC&(1<<3))==0){
// если нажата кнопка энкодера
404
while
((PINC&(1<<3))==0);
405
volt_meter(); }
406
}
// end while
407
}
// end main_screen
нет ничего такого. бери исходники димакса, выбери плату уно например и заливай с помощью программатора и все))
кстати правильно что свою. я резисторы цапа слишком близко друг другу расположил, неудобно было паять. у меня на 70% спаянная
+ не тестировал, просто включил. вроде работает, экран показывает, режимы переключаются
Доброго времени суток dimax,
воспользовался кодом из поста #6 и появилось 2 вопроса:
1. Даю генератору частоту 0Hz, результат - если сразу после запуска, то не генерит, если до этого была дана частота, генерит предыдущую заданную. Как заставить остановиться по 0?
2. Есть ли какая возможность перевесить выход с D9 на D3?
"Терминальная" версия генератора. Запустить терминалку, послать требуемую частоту в герцах. В ответ напишется та частота, которую смог сделать МК.
01
void
setup
() {
02
Serial
.begin(9600);
03
pinMode (9,OUTPUT);
// выход генератора
04
TCCR1A=0;TCCR1B=0;
05
}
06
void
loop
() {
07
static
uint32_t reqfreq=0;
//переменная запроса частоты
08
uint32_t ocr=OCR1A; uint16_t divider=1;
float
freq;
09
if
(
Serial
.available() > 0){ reqfreq =
Serial
.parseInt();
10
if
(reqfreq==0 || reqfreq>F_CPU/2) {
return
;}
11
ocr = (F_CPU / reqfreq /2 /divider);
12
byte
shifts[] = {3,3,2,2};
13
for
(
byte
i = 0; i < 4; i++){
14
if
(ocr > 65536) { divider <<= shifts[i];
15
ocr = F_CPU / reqfreq /2 /divider; }
16
else
{ TCCR1B = (i+1)|(1<<WGM12);
break
; } }
//Mode4 (CTC)
17
OCR1A=ocr-1; TCCR1A=1<<COM1A0;
18
freq= (
float
) F_CPU/2 / (OCR1A+1) /divider;
19
if
(freq <10000) {
Serial
.print(freq,1);
Serial
.println(
" Hz "
); }
20
if
(freq >=10000) {
Serial
.print(freq/1000,3);
Serial
.println(
" kHz"
);}
21
}
22
}
Сделал новую версию генератора полностью на новом железе. (Все картинки кликабельны).
На написание ушёл месяц -учитывая что было освоение доселе неизвестного МК, а программа вышла под 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) уже есть в составе аддона. Вторую нужно поставить дополнительно, брать тут.
001
/*Генератор с регулируемой частотой v3.0 (C)Dimax */
002
#define pwm2_polar 0 //полярность выхода PWM2
003
#define paper 0x000000 // цвет фона экрана
004
#include <Adafruit_GFX_AS.h> // Core graphics library
005
#include <Adafruit_ST7735.h> // Hardware-specific library
006
#include <SPI.h>
007
Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10);
008
volatile
int
enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
009
volatile
int
encstep=1;
//шаг изменения частоты по умолчанию
010
volatile
float
freq=1000;
//частота по умолчанию
011
volatile
float
duty_out;
// переменная счёта скважности
012
float
t_hi, t_low;
//переменные счёта длины импульсов
013
uint8_t wave[512];
//массив для DDS синтеза
014
byte
sine_logo[]={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
015
44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
016
46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
017
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,
018
1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
019
020
void
setup
() {
021
SPI.setModule(2);
// выбор SPI2
022
tft.initR(INITR_BLACKTAB);
023
tft.setRotation(3);
//дисплей горизонтально, контакты слева
024
tft.fillScreen(paper);
//залить цветом по умолчанию
025
tft.setTextWrap(0);
//не переосить строки
026
nvic_irq_disable_all();
//отключить все прерывания
027
systick_disable();
// отключить системный таймер
028
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0);
//включить тактирование tim-2,3,4
029
RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);
////включить тактирование port-a-b-c,tim1
030
AFIO_BASE->MAPR=(1<<8)|(1<<6);
//tim 1 && tim 2 Partial remap
031
pinMode(PB0,PWM);
//buzzer
032
pinMode(PB6,INPUT_PULLUP);
//enc
033
pinMode(PB7,INPUT_PULLUP);
//enc
034
pinMode(PB8,INPUT_PULLUP);
//key enc
035
mytone(1000,50);
//сигнал после старта
036
///////// Таймер4 -обработчик энкодера
037
TIMER4_BASE->CR1=(1<<2)|(1<<9);
//CKD10 URS
038
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );
//cc1s, cc2s input mapped TI1/TI2
039
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);
//Capture/Compare 1,2 output polarity
040
TIMER4_BASE->SMCR=(1<<1)|(1<<0);
//Encoder mode3(SMS bit)стр.407
041
TIMER4_BASE->CNT=0;
042
TIMER4_BASE->ARR=1;
//ограничение счёта ( =3 для двухимпульсного энкодера)
043
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1;
044
timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
045
TIMER4_BASE->CR1|=(1<<0);
//запуск
046
//прерывание кнопки энкодера
047
attachInterrupt(PB8, key_enc_int, RISING);
048
timer_set();
049
}
050
051
052
053
void
loop
() {
054
static
int
old_mode_loop=-1;
055
if
(freq > 8485) {tim_mode=1;}
else
{tim_mode=0;}
// переключать режимы таймера
056
if
(mode!=old_mode_loop) {
057
tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
058
}
//чистить полностью экран только при смене режимов
059
if
(mode==7) {mon_out(); freq_meter(); }
060
if
(mode >1 && mode<7) { mon_out(); dds_set(); }
// запуск DDS режимов
061
if
(mon_flag) { mon_out();}
//в остальных ситуациях при наличии флага вывода на дисплей
062
}
063
064
065
void
freq_meter(){
066
/////////////////////счётчик импульсов\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
067
pinMode(PA15,INPUT_PULLDOWN);
// вход частотометра
068
uint32_t imp_long,imp_hi;
//переменные измерения длины такта
069
__asm volatile(
"cpsid i"
);
070
/// Timer2 счёт младших 16 бит
071
TIMER2_BASE->CR1=0;
//стоп таймер
072
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
073
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
074
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
075
TIMER2_BASE->CCMR2=0;
076
TIMER2_BASE->CR2=1<<5;
//MMS:010 управление подчинённым в режиме "Update"
077
TIMER2_BASE->SMCR= (1<<14);
// ECE & TS:000 режим 2 внешнего тактирования & разрешение работы от таймера1
078
TIMER2_BASE->ARR=65535;
//считать до максимума
079
TIMER2_BASE->EGR=1;
//перечитать регистры.
080
TIMER2_BASE->CR1|=(1<<0);
//start timer2
081
/// Timer3 счёт старших 16 бит
082
TIMER3_BASE->CR1=1<<0;
//стоп таймер
083
TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0;
084
TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0;
085
TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;
086
TIMER3_BASE->CCMR1=0;
087
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);
//SMS:111 && TS:001 такт брать от 2-го таймера
088
TIMER3_BASE->ARR=65535;
//считать до
089
TIMER3_BASE->EGR=1;
//перечитать регистры.
090
TIMER3_BASE->CR1|=(1<<0);
//start timer3
091
/// настройка времени разрешения на таймере1 для таймера2
092
TIMER1_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прерываний
093
TIMER1_BASE->CNT=0;
094
TIMER1_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
095
TIMER1_BASE->CCER=0;
// отключить выходы таймера на физ ноги
096
TIMER1_BASE->PSC=1999;
// 72000000/20000= 36000кГц тактовая таймера
097
TIMER1_BASE->ARR=35999;
//считать до 36000 (1секунда)
098
TIMER1_BASE->EGR=1;
//перечитать регистры.
099
TIMER1_BASE->CR1|=(1<<0);
100
__asm volatile(
"cpsie i"
);
101
while
(TIMER1_BASE->CR1&1) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
102
freq= TIMER3_BASE->CNT<<16 | TIMER2_BASE->CNT ;
103
if
(freq>1E6){ t_low=0;t_hi=0; duty_out=50;
return
;}
//выйти если freq больше мегагерца
104
// Перенастройка таймера 2 в режии измерения длительности импульса и скважности
105
divider=1;
106
while
((72E6/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
107
__asm volatile(
"cpsid i"
);
108
TIMER2_BASE->CR1=0;
//стоп таймер
109
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
110
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
111
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
112
TIMER2_BASE->CCMR2=0;
113
TIMER2_BASE->CR2=0;
114
TIMER2_BASE->PSC= divider-1;
115
TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);
// TS:101 SMS:100 вход TI1FP1 , Режим сброса
116
TIMER2_BASE->CCMR1=(1<<0)|(1<<9);
//CC1 input,mapped on TI1, CC2 input,mapped on TI1
117
TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);
//cc1-Hi,cc2-lo
118
TIMER2_BASE->EGR=1;
//перечитать регистры.
119
TIMER2_BASE->CR1=(1<<0);
120
__asm volatile(
"cpsie i"
);
121
while
( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
122
TIMER2_BASE->CR1=0;
// стоп таймер
123
imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
124
imp_hi=(uint32_t) ((TIMER2_BASE->CCR2)*divider);
125
if
(freq <1000){ freq=72E6 /imp_long;}
//если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
126
duty_out= (
float
) imp_hi / (imp_long / 100.0) ;
127
t_low= (imp_long-imp_hi) /72.0 ;
128
t_hi= imp_hi /72.0;
129
mon_flag=1;
130
}
//END freq meter
131
132
133
///////////////////////////////////////////////////////////////////////////
134
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
135
///////////////////////////////////////////////////////////////////////////
136
void
mon_out(){
137
char
mybuf[10];
float
freq_out;
138
//************** Вывод первой строчки*****************************
139
tft.setCursor(0, 0);
// вперёд, вниз
140
tft.setTextColor(ST7735_GREEN, paper);
141
tft.setTextSize(2);
142
switch
(mode){
143
case
0: tft.print(
" PWM Mode "
);
break
;
144
case
1: tft.print(
" Duty Mode "
);
break
;
145
case
2: tft.print(
" Sinus DDS "
);
break
;
146
case
3: tft.print(
" Triangle DDS"
);
break
;
147
case
4: tft.print(
" Pila1 DDS "
);
break
;
148
case
5: tft.print(
" Pila2 DDS "
);
break
;
149
case
6: tft.print(
" Meandr DDS "
);
break
;
150
case
7: tft.print(
" Freq. meter "
);
break
;
151
}
152
//*****************Вывод второй строчки*****************************
153
tft.setTextColor(ST7735_WHITE, paper);
154
tft.setTextSize(3); tft.setCursor(0, 19);
155
if
(freq >=1E6) { freq_out=freq/1E6;}
156
else
if
(freq>=1E4) { freq_out=freq/1000;}
157
else
{ freq_out=freq;}
158
if
(mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print(
" "
); }
159
else
{dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}
160
//********************Вывод третьей строчки*****************************
161
tft.setCursor(50, 43);
// вперёд, вниз
162
tft.setTextColor(ST7735_RED,paper);
163
if
(freq>=1E6){tft.print(
"MHz"
);}
164
else
if
(freq>=1E4){tft.print(
"kHz"
);}
165
else
{tft.print(
" Hz"
);}
166
//********************* "осциллограммы"******************************
167
tft.fillRect(5,90, 100,38,paper);
//вправо, вниз, ширина вправо, длина вниз
168
tft.drawRect(0,67, 160,61,ST7735_MAGENTA);
//вправо, вниз, ширина вправо, длина вниз
169
if
(mode<2 || mode==7){
170
tft.drawFastVLine(5, 90, 30, ST7735_CYAN);
// восход фронта статическая вер линия
171
tft.drawFastHLine(5, 91, (
int
)duty_out, ST7735_YELLOW);
//длина единицы
172
tft.drawFastHLine(5, 90, (
int
)duty_out, ST7735_YELLOW);
//паралельная линия для выделения
173
tft.drawFastVLine((
int
)duty_out+5, 91, 30, ST7735_YELLOW);
// спад
174
tft.drawFastVLine((
int
)duty_out+4, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
175
tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);
//спад конец такта статическая вер. линия
176
tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
177
tft.drawFastHLine((
int
)duty_out+5, 120, (100-(
int
)duty_out), ST7735_YELLOW);
//линия единицы 2-го такта
178
tft.drawFastHLine((
int
)duty_out+5, 119, (100-(
int
)duty_out), ST7735_YELLOW);
//паралельная линия для выделения
179
}
180
if
(mode==2){
// логотип синуса
181
for
(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
182
}
//END for
183
}
// END if (mode==2)
184
else
if
(mode==3){
// логотип треугольника
185
tft.drawLine(5,98,30,73,ST7735_YELLOW);
186
tft.drawLine(30,73,80,123,ST7735_YELLOW);
187
tft.drawLine(80,123,105,98,ST7735_YELLOW);
188
}
//END mode==3
189
else
if
(mode==4){
//логотип пилы1
190
tft.drawLine(5,123,105,73,ST7735_YELLOW);
191
tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);
//спад конец такта статическая вер. линия
192
}
//END if (mode==3)
193
else
if
(mode==5){
//логотип пилы2
194
tft.drawFastVLine(5, 73, 50, ST7735_YELLOW);
// восход фронта статическая вер линия
195
tft.drawLine(5,73,105,123,ST7735_YELLOW);
196
}
// END if (mode==4)
197
else
if
(mode==6){
//логотип меандра
198
tft.drawFastVLine(5,73,25,ST7735_YELLOW);
199
tft.drawFastHLine(5,73,50,ST7735_YELLOW);
200
tft.drawFastVLine(55,73,50,ST7735_YELLOW);
201
tft.drawFastHLine(55,123,50,ST7735_YELLOW);
202
tft.drawFastVLine(105,98,25,ST7735_YELLOW);
203
}
204
//*********************** характеристики сигнала****************************************
205
tft.setCursor(5, 70);
// вперёд, вниз
206
tft.setTextColor(ST7735_WHITE, paper);
207
tft.setTextSize(1);
208
if
(mode < 2 || mode==7){
209
tft.print(
"+Width="
);
if
(t_hi<1E3) {tft.print(t_hi); tft.print(
" uS "
);}
else
{tft.print(t_hi/1000); tft.print(
" mS "
);}
210
tft.setCursor(5, 80);
// вперёд, вниз
211
tft.print(
"-Width="
);
if
(t_low<1E3) {tft.print(t_low); tft.print(
" uS "
);}
else
{tft.print(t_low/1000); tft.print(
" mS "
);}
212
tft.setCursor(114, 70); tft.print(
"Duty="
);
213
tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(
" % "
);
214
}
//END if (mode < 2 || mode==7)
215
if
(mode!=7){
// выводить шаг кроме частотометра
216
tft.setCursor(114, 95); tft.print(
"Step="
);
217
tft.setCursor(114, 105);
218
switch
(encstep) {
219
case
1: tft.print(
" 1"
);
break
;
220
case
10: tft.print(
" 10"
);
break
;
221
case
100: tft.print(
" 100"
);
break
;
222
case
1000: tft.print(
"1000"
);
break
;
223
}
// END switch case
224
}
// END if (mode!=7)
225
if
( freq < 1) {tft.fillRect(1,68, 158,59,paper); }
226
mon_flag=0;
227
}
//END mon_out
228
229
230
231
232
//обработчик прерываний энкодера
233
void
enc_int(){
234
if
((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);}
235
else
{enc_mode(-1);}
236
}
// END VOID
237
238
239
// ФУНКЦИЯ конфигурации режимов
240
void
enc_mode(
int
in
){
241
modebit= (GPIOB_BASE->IDR&0x100);
//состояние кнопки. 0-нажата
242
if
(!modebit) {
243
mytone(880,30);
//звук переключения режимов
244
mode+=
in
;
245
if
(mode>7){mode=7;}
246
if
(mode<0){mode=0;}
247
if
(mode<2){timer_set();}
248
if
(mode==7) {freq=0;}
249
mon_flag=1;
250
return
;
251
}
252
mytone(4400,10);
//звук изменения частоты
253
switch
(mode){
254
case
0:
if
(tim_mode==0) { freq+=(encstep*
in
); timer_set(); }
else
{ timer_hi_set(
in
); }
break
;
255
case
1: duty_in+=
in
;
if
(duty_in>99){duty_in=99;}
if
(duty_in<1){duty_in=1;} set_duty(); calc_freq();
break
;
256
case
7:
break
;
// в частотометре не реагировать на вращение энкодера
257
default
: freq+=(encstep*
in
); mon_flag=1;
//wave режимы
258
// проверка на корректность запроса частоты
259
if
(freq<0){freq=0;}
if
(freq>2E5) {freq=2E5;}
260
}
//end switch case
261
}
//end enc_mode
262
263
264
// обработчик кнопки энкодера
265
void
key_enc_int(){
266
if
(!modebit){modebit=1; mon_flag=1;
return
;}
// если менялся режим -выйти
267
if
(mode==1) { mytone(880,30); mode=0; mon_flag=1; ;
return
;}
//сменить режим и выйти если были в duty mode
268
mytone(220,75);
//звук переключения шага
269
switch
(encstep){
270
case
1: encstep=10;
break
;
271
case
10: encstep=100;
break
;
272
case
100: encstep=1000;
break
;
273
case
1000: encstep=1;
break
;
274
}
275
mon_flag=1;
//флаг вывода на дисплей
276
}
//end
277
278
void
set_duty(){
279
if
(mode==0 && TIMER1_BASE->ARR<100){
280
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ;
281
duty_in=50;
return
;
282
}
283
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
284
}
285
286
//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
287
void
timer_set(){
288
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
289
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
290
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
291
TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1; TIMER1_BASE->CR1=1;
292
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
293
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
294
if
(freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1;
return
;}
295
if
(freq>36E6) {freq=36E6;}
296
divider=1;
297
int
tim_arr = 72E6/freq;
298
while
( (tim_arr/divider) > 65535) {divider++;}
299
TIMER1_BASE->PSC=divider-1;
300
TIMER1_BASE->ARR=(tim_arr/divider)-1;
301
set_duty();
302
calc_freq();
303
}
304
305
//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
306
void
timer_hi_set(
int
arr){
307
__asm volatile(
"cpsid i"
);
308
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
309
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
310
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
311
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
312
//насильно уменьшить шаг с ростом частоты
313
if
(TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100;
314
if
(TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
315
if
(TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;
316
arr*=encstep;
317
int
icr= TIMER1_BASE->ARR-=arr;
318
if
(icr<1) {icr=1;}
if
(icr>65535) {icr=65535;}
319
TIMER1_BASE->ARR=icr;
320
__asm volatile(
"cpsie i"
);
321
set_duty();
322
calc_freq();
323
}
324
325
// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
326
void
calc_freq(){
327
uint32_t imp_long, imp_hi;
328
freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
329
duty_out= (
float
) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
330
duty_out= floorf(duty_out);
331
imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider);
332
imp_hi=(uint32_t) ((TIMER1_BASE->CCR1)*divider);
333
t_low= (imp_long-imp_hi) /72.0 ;
334
t_hi= imp_hi /72.0;
335
mon_flag=1;
336
}
337
338
339
// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
340
void
dds_set(){
341
static
uint32_t akkum;
342
static
byte
oldmode=255;
343
TIMER1_BASE->CCER=0;
//timer output pins disable && перечитать регистры.
344
GPIOA_BASE->CRL = 0x33333333;
// pa0-pa7 выход
345
GPIOA_BASE->CRH =0x44444444;
//pa8-pa15 вход
346
if
(oldmode !=mode) {
347
if
(mode==2) {
for
(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*
float
(n)/512)+1)/2 ;}}
// синус
348
else
if
(mode==3){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=n;}
else
{wave[n]=(511-n);}}}
//треугол
349
else
if
(mode==4){
for
(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}
//пила1
350
else
if
(mode==5){
for
(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}
//пила2
351
else
if
(mode==6){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=0;}
else
{wave[n]=255;}}}
//меандр
352
oldmode=mode; }
353
//для пересчёта коэффициента вручную: частоту на экране * текущий множитель и разделить на фактически измеренную частоту
354
uint32_t dds_shag= (
double
)freq * 1670.1100841768461768461768461768;
// шаг= частота*коэффициент
355
while
(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];}
//генерация DDS
356
GPIOA_BASE->CRL=0x44444444;
// все пины в Z
357
}
//END DDS set()
358
//
359
360
void
mytone(
int
frq,
int
ms ){
361
if
(mode==7){
return
;}
//таймер в режиме частотометра занят
362
uint16_t psc=1; uint32_t tim_arr;
363
// настройка генератора звука на таймере3
364
tim_arr = 36E6/frq;
365
while
( (tim_arr/psc) > 65535) {psc++;}
366
__asm volatile(
"cpsid i"
);
367
TIMER2_BASE->SMCR=0;
368
TIMER3_BASE->CCR3=0;
//обнулить регистр соответсвующий используемому выходу
369
TIMER3_BASE->PSC=psc-1;
370
TIMER3_BASE->ARR=(tim_arr/psc)-1;
371
TIMER3_BASE->CCMR2=(1<<5)|(1<<4);
// OC3M:011
372
TIMER3_BASE->CCER=1<<8;
//cc3e подключить аппаратную ногу
373
TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);
//SMS:101 && TS:001 строб от 2-го таймера
374
TIMER3_BASE->EGR=1;
//перечитать регистры.
375
TIMER3_BASE->CR1=1;
376
/// настройка выдержки времени на таймере2
377
psc=1;
378
tim_arr = 72E3 * ms;
379
while
( (tim_arr/psc) > 65536) {psc++;}
380
TIMER2_BASE->CCMR2=0;
381
TIMER2_BASE->CR2=0;
382
TIMER2_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прероываний
383
TIMER2_BASE->CNT=0;
384
TIMER2_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
385
TIMER2_BASE->PSC=psc-1;
386
TIMER2_BASE->ARR=(tim_arr/psc)-1;
387
TIMER2_BASE->EGR=1;
//перечитать регистры.
388
TIMER2_BASE->CR1|=(1<<0);
389
__asm volatile(
"cpsie i"
);
390
}
Убедительная просьба не использовать кнопку "Цитировать" под этим постом, т.к. у меня пропадёт возможность редактировать ошибки, да и просто засирать тему повторной информацией не стоит.
V3_128
Переделал под 128х128 экран (st7735 ) , цвета синий с красным попутаны. Но оно и не мешает.
001
[code]
002
003
004
/*Генератор с регулируемой частотой v3.0 (C)Dimax */
005
#define pwm2_polar 0 //полярность выхода PWM2
006
#define paper 0x000000 // цвет фона экрана
007
#include <Adafruit_GFX_AS.h> // Core graphics library
008
#include <Adafruit_ST7735.h> // Hardware-specific library
009
#include <SPI.h>
010
Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10);
011
volatile
int
enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
012
volatile
int
encstep=1;
//шаг изменения частоты по умолчанию
013
volatile
float
freq=1000;
//частота по умолчанию
014
volatile
float
duty_out;
// переменная счёта скважности
015
float
t_hi, t_low;
//переменные счёта длины импульсов
016
uint8_t wave[512];
//массив для DDS синтеза
017
byte
sine_logo[]={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
018
44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
019
46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
020
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,
021
1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
022
023
void
setup
() {
024
SPI.setModule(2);
// выбор SPI2
025
tft.initR(INITR_BLACKTAB);
026
tft.setRotation(3);
//дисплей горизонтально, контакты слева
027
tft.fillScreen(paper);
//залить цветом по умолчанию
028
tft.setTextWrap(0);
//не переосить строки
029
nvic_irq_disable_all();
//отключить все прерывания
030
systick_disable();
// отключить системный таймер
031
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0);
//включить тактирование tim-2,3,4
032
RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);
////включить тактирование port-a-b-c,tim1
033
AFIO_BASE->MAPR=(1<<8)|(1<<6);
//tim 1 && tim 2 Partial remap
034
pinMode(PB0,PWM);
//buzzer
035
pinMode(PB6,INPUT_PULLUP);
//enc
036
pinMode(PB7,INPUT_PULLUP);
//enc
037
pinMode(PB8,INPUT_PULLUP);
//key enc
038
mytone(1000,50);
//сигнал после старта
039
///////// Таймер4 -обработчик энкодера
040
TIMER4_BASE->CR1=(1<<2)|(1<<9);
//CKD10 URS
041
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );
//cc1s, cc2s input mapped TI1/TI2
042
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);
//Capture/Compare 1,2 output polarity
043
TIMER4_BASE->SMCR=(1<<1)|(1<<0);
//Encoder mode3(SMS bit)стр.407
044
TIMER4_BASE->CNT=0;
045
TIMER4_BASE->ARR=1;
//ограничение счёта ( =3 для двухимпульсного энкодера)
046
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1;
047
timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
048
TIMER4_BASE->CR1|=(1<<0);
//запуск
049
//прерывание кнопки энкодера
050
attachInterrupt(PB8, key_enc_int, RISING);
051
timer_set();
052
}
053
054
055
056
void
loop
() {
057
static
int
old_mode_loop=-1;
058
if
(freq > 8485) {tim_mode=1;}
else
{tim_mode=0;}
// переключать режимы таймера
059
if
(mode!=old_mode_loop) {
060
tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
061
}
//чистить полностью экран только при смене режимов
062
if
(mode==7) {mon_out(); freq_meter(); }
063
if
(mode >1 && mode<7) { mon_out(); dds_set(); }
// запуск DDS режимов
064
if
(mon_flag) { mon_out();}
//в остальных ситуациях при наличии флага вывода на дисплей
065
}
066
067
068
void
freq_meter(){
069
/////////////////////счётчик импульсов\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
070
pinMode(PA15,INPUT_PULLDOWN);
// вход частотометра
071
uint32_t imp_long,imp_hi;
//переменные измерения длины такта
072
__asm volatile(
"cpsid i"
);
073
/// Timer2 счёт младших 16 бит
074
TIMER2_BASE->CR1=0;
//стоп таймер
075
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
076
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
077
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
078
TIMER2_BASE->CCMR2=0;
079
TIMER2_BASE->CR2=1<<5;
//MMS:010 управление подчинённым в режиме "Update"
080
TIMER2_BASE->SMCR= (1<<14);
// ECE & TS:000 режим 2 внешнего тактирования & разрешение работы от таймера1
081
TIMER2_BASE->ARR=65535;
//считать до максимума
082
TIMER2_BASE->EGR=1;
//перечитать регистры.
083
TIMER2_BASE->CR1|=(1<<0);
//start timer2
084
/// Timer3 счёт старших 16 бит
085
TIMER3_BASE->CR1=1<<0;
//стоп таймер
086
TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0;
087
TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0;
088
TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;
089
TIMER3_BASE->CCMR1=0;
090
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);
//SMS:111 && TS:001 такт брать от 2-го таймера
091
TIMER3_BASE->ARR=65535;
//считать до
092
TIMER3_BASE->EGR=1;
//перечитать регистры.
093
TIMER3_BASE->CR1|=(1<<0);
//start timer3
094
/// настройка времени разрешения на таймере1 для таймера2
095
TIMER1_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прерываний
096
TIMER1_BASE->CNT=0;
097
TIMER1_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
098
TIMER1_BASE->CCER=0;
// отключить выходы таймера на физ ноги
099
TIMER1_BASE->PSC=1999;
// 72000000/20000= 36000кГц тактовая таймера
100
TIMER1_BASE->ARR=35999;
//считать до 36000 (1секунда)
101
TIMER1_BASE->EGR=1;
//перечитать регистры.
102
TIMER1_BASE->CR1|=(1<<0);
103
__asm volatile(
"cpsie i"
);
104
while
(TIMER1_BASE->CR1&1) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
105
freq= TIMER3_BASE->CNT<<16 | TIMER2_BASE->CNT ;
106
if
(freq>1E6){ t_low=0;t_hi=0; duty_out=50;
return
;}
//выйти если freq больше мегагерца
107
// Перенастройка таймера 2 в режии измерения длительности импульса и скважности
108
divider=1;
109
while
((72E6/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
110
__asm volatile(
"cpsid i"
);
111
TIMER2_BASE->CR1=0;
//стоп таймер
112
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
113
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
114
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
115
TIMER2_BASE->CCMR2=0;
116
TIMER2_BASE->CR2=0;
117
TIMER2_BASE->PSC= divider-1;
118
TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);
// TS:101 SMS:100 вход TI1FP1 , Режим сброса
119
TIMER2_BASE->CCMR1=(1<<0)|(1<<9);
//CC1 input,mapped on TI1, CC2 input,mapped on TI1
120
TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);
//cc1-Hi,cc2-lo
121
TIMER2_BASE->EGR=1;
//перечитать регистры.
122
TIMER2_BASE->CR1=(1<<0);
123
__asm volatile(
"cpsie i"
);
124
while
( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
125
TIMER2_BASE->CR1=0;
// стоп таймер
126
imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
127
imp_hi=(uint32_t) ((TIMER2_BASE->CCR2)*divider);
128
if
(freq <1000){ freq=72E6 /imp_long;}
//если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
129
duty_out= (
float
) imp_hi / (imp_long / 100.0) ;
130
t_low= (imp_long-imp_hi) /72.0 ;
131
t_hi= imp_hi /72.0;
132
mon_flag=1;
133
}
//END freq meter
134
135
136
///////////////////////////////////////////////////////////////////////////
137
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
138
///////////////////////////////////////////////////////////////////////////
139
void
mon_out(){
140
char
mybuf[10];
float
freq_out;
141
//************** Вывод первой строчки*****************************
142
tft.setCursor(0, 0);
// вперёд, вниз
143
tft.setTextColor(ST7735_GREEN, paper);
144
tft.setTextSize(2);
145
switch
(mode){
146
case
0: tft.print(
" PWM Mode "
);
break
;
147
case
1: tft.print(
"Duty Mode "
);
break
;
148
case
2: tft.print(
"Sinus DDS "
);
break
;
149
case
3: tft.print(
"Trian DDS"
);
break
;
150
case
4: tft.print(
"Pila1 DDS "
);
break
;
151
case
5: tft.print(
"Pila2 DDS "
);
break
;
152
case
6: tft.print(
"Meandr DDS "
);
break
;
153
case
7: tft.print(
"Freq meter "
);
break
;
154
}
155
//*****************Вывод второй строчки*****************************
156
tft.setTextColor(ST7735_WHITE, paper);
157
tft.setTextSize(2); tft.setCursor(0, 19);
158
if
(freq >=1E6) { freq_out=freq/1E6;}
159
else
if
(freq>=1E4) { freq_out=freq/1000;}
160
else
{ freq_out=freq;}
161
if
(mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print(
" "
); }
162
else
{dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}
163
//********************Вывод третьей строчки*****************************
164
tft.setCursor(30, 43);
// вперёд, вниз
165
tft.setTextColor(ST7735_RED,paper);
166
if
(freq>=1E6){tft.print(
"MHz"
);}
167
else
if
(freq>=1E4){tft.print(
"kHz"
);}
168
else
{tft.print(
" Hz"
);}
169
//********************* "осциллограммы"******************************
170
tft.fillRect(5,90, 100,38,paper);
//вправо, вниз, ширина вправо, длина вниз
171
tft.drawRect(0,67, 128,61,ST7735_MAGENTA);
//вправо, вниз, ширина вправо, длина вниз
172
if
(mode<2 || mode==7){
173
tft.drawFastVLine(5, 90, 30, ST7735_CYAN);
// восход фронта статическая вер линия
174
tft.drawFastHLine(5, 91, (
int
)duty_out, ST7735_YELLOW);
//длина единицы
175
tft.drawFastHLine(5, 90, (
int
)duty_out, ST7735_YELLOW);
//паралельная линия для выделения
176
tft.drawFastVLine((
int
)duty_out+5, 91, 30, ST7735_YELLOW);
// спад
177
tft.drawFastVLine((
int
)duty_out+4, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
178
tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);
//спад конец такта статическая вер. линия
179
tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
180
tft.drawFastHLine((
int
)duty_out+5, 120, (100-(
int
)duty_out), ST7735_YELLOW);
//линия единицы 2-го такта
181
tft.drawFastHLine((
int
)duty_out+5, 119, (100-(
int
)duty_out), ST7735_YELLOW);
//паралельная линия для выделения
182
}
183
if
(mode==2){
// логотип синуса
184
for
(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
185
}
//END for
186
}
// END if (mode==2)
187
else
if
(mode==3){
// логотип треугольника
188
tft.drawLine(5,98,30,73,ST7735_YELLOW);
189
tft.drawLine(30,73,80,123,ST7735_YELLOW);
190
tft.drawLine(80,123,105,98,ST7735_YELLOW);
191
}
//END mode==3
192
else
if
(mode==4){
//логотип пилы1
193
tft.drawLine(5,123,105,73,ST7735_YELLOW);
194
tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);
//спад конец такта статическая вер. линия
195
}
//END if (mode==3)
196
else
if
(mode==5){
//логотип пилы2
197
tft.drawFastVLine(5, 73, 50, ST7735_YELLOW);
// восход фронта статическая вер линия
198
tft.drawLine(5,73,105,123,ST7735_YELLOW);
199
}
// END if (mode==4)
200
else
if
(mode==6){
//логотип меандра
201
tft.drawFastVLine(5,73,25,ST7735_YELLOW);
202
tft.drawFastHLine(5,73,50,ST7735_YELLOW);
203
tft.drawFastVLine(55,73,50,ST7735_YELLOW);
204
tft.drawFastHLine(55,123,50,ST7735_YELLOW);
205
tft.drawFastVLine(105,98,25,ST7735_YELLOW);
206
}
207
//*********************** характеристики сигнала****************************************
208
tft.setCursor(5, 70);
// вперёд, вниз
209
tft.setTextColor(ST7735_WHITE, paper);
210
tft.setTextSize(1);
211
if
(mode < 2 || mode==7){
212
tft.print(
"+Width="
);
if
(t_hi<1E3) {tft.print(t_hi); tft.print(
" uS "
);}
else
{tft.print(t_hi/1000); tft.print(
" mS "
);}
213
tft.setCursor(5, 80);
// вперёд, вниз
214
tft.print(
"-Width="
);
if
(t_low<1E3) {tft.print(t_low); tft.print(
" uS "
);}
else
{tft.print(t_low/1000); tft.print(
" mS "
);}
215
tft.setCursor(60, 95); tft.print(
"Duty = "
);
216
// tft.setCursor(60, 105);
217
tft.print(duty_out,0);tft.print(
"%"
);
218
}
//END if (mode < 2 || mode==7)
219
if
(mode!=7){
// выводить шаг кроме частотометра
220
tft.setCursor(14, 95); tft.print(
"Step="
);
221
tft.setCursor(14, 105);
222
switch
(encstep) {
223
case
1: tft.print(
" 1"
);
break
;
224
case
10: tft.print(
" 10"
);
break
;
225
case
100: tft.print(
" 100"
);
break
;
226
case
1000: tft.print(
"1000"
);
break
;
227
}
// END switch case
228
}
// END if (mode!=7)
229
if
( freq < 1) {tft.fillRect(1,68, 158,59,paper); }
230
mon_flag=0;
231
}
//END mon_out
232
233
234
235
236
//обработчик прерываний энкодера
237
void
enc_int(){
238
if
((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);}
239
else
{enc_mode(-1);}
240
}
// END VOID
241
242
243
// ФУНКЦИЯ конфигурации режимов
244
void
enc_mode(
int
in
){
245
modebit= (GPIOB_BASE->IDR&0x100);
//состояние кнопки. 0-нажата
246
if
(!modebit) {
247
mytone(880,30);
//звук переключения режимов
248
mode+=
in
;
249
if
(mode>7){mode=7;}
250
if
(mode<0){mode=0;}
251
if
(mode<2){timer_set();}
252
if
(mode==7) {freq=0;}
253
mon_flag=1;
254
return
;
255
}
256
mytone(4400,10);
//звук изменения частоты
257
switch
(mode){
258
case
0:
if
(tim_mode==0) { freq+=(encstep*
in
); timer_set(); }
else
{ timer_hi_set(
in
); }
break
;
259
case
1: duty_in+=
in
;
if
(duty_in>99){duty_in=99;}
if
(duty_in<1){duty_in=1;} set_duty(); calc_freq();
break
;
260
case
7:
break
;
// в частотометре не реагировать на вращение энкодера
261
default
: freq+=(encstep*
in
); mon_flag=1;
//wave режимы
262
// проверка на корректность запроса частоты
263
if
(freq<0){freq=0;}
if
(freq>2E5) {freq=2E5;}
264
}
//end switch case
265
}
//end enc_mode
266
267
268
// обработчик кнопки энкодера
269
void
key_enc_int(){
270
if
(!modebit){modebit=1; mon_flag=1;
return
;}
// если менялся режим -выйти
271
if
(mode==1) { mytone(880,30); mode=0; mon_flag=1; ;
return
;}
//сменить режим и выйти если были в duty mode
272
mytone(220,75);
//звук переключения шага
273
switch
(encstep){
274
case
1: encstep=10;
break
;
275
case
10: encstep=100;
break
;
276
case
100: encstep=1000;
break
;
277
case
1000: encstep=1;
break
;
278
}
279
mon_flag=1;
//флаг вывода на дисплей
280
}
//end
281
282
void
set_duty(){
283
if
(mode==0 && TIMER1_BASE->ARR<100){
284
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ;
285
duty_in=50;
return
;
286
}
287
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
288
}
289
290
//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
291
void
timer_set(){
292
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
293
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
294
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
295
TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1; TIMER1_BASE->CR1=1;
296
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
297
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
298
if
(freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1;
return
;}
299
if
(freq>36E6) {freq=36E6;}
300
divider=1;
301
int
tim_arr = 72E6/freq;
302
while
( (tim_arr/divider) > 65535) {divider++;}
303
TIMER1_BASE->PSC=divider-1;
304
TIMER1_BASE->ARR=(tim_arr/divider)-1;
305
set_duty();
306
calc_freq();
307
}
308
309
//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
310
void
timer_hi_set(
int
arr){
311
__asm volatile(
"cpsid i"
);
312
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
313
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
314
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
315
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
316
//насильно уменьшить шаг с ростом частоты
317
if
(TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100;
318
if
(TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
319
if
(TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;
320
arr*=encstep;
321
int
icr= TIMER1_BASE->ARR-=arr;
322
if
(icr<1) {icr=1;}
if
(icr>65535) {icr=65535;}
323
TIMER1_BASE->ARR=icr;
324
__asm volatile(
"cpsie i"
);
325
set_duty();
326
calc_freq();
327
}
328
329
// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
330
void
calc_freq(){
331
uint32_t imp_long, imp_hi;
332
freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
333
duty_out= (
float
) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
334
duty_out= floorf(duty_out);
335
imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider);
336
imp_hi=(uint32_t) ((TIMER1_BASE->CCR1)*divider);
337
t_low= (imp_long-imp_hi) /72.0 ;
338
t_hi= imp_hi /72.0;
339
mon_flag=1;
340
}
341
342
343
// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
344
void
dds_set(){
345
static
uint32_t akkum;
346
static
byte
oldmode=255;
347
TIMER1_BASE->CCER=0;
//timer output pins disable && перечитать регистры.
348
GPIOA_BASE->CRL = 0x33333333;
// pa0-pa7 выход
349
GPIOA_BASE->CRH =0x44444444;
//pa8-pa15 вход
350
if
(oldmode !=mode) {
351
if
(mode==2) {
for
(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*
float
(n)/512)+1)/2 ;}}
// синус
352
else
if
(mode==3){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=n;}
else
{wave[n]=(511-n);}}}
//треугол
353
else
if
(mode==4){
for
(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}
//пила1
354
else
if
(mode==5){
for
(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}
//пила2
355
else
if
(mode==6){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=0;}
else
{wave[n]=255;}}}
//меандр
356
oldmode=mode; }
357
//для пересчёта коэффициента вручную: частоту на экране * текущий множитель и разделить на фактически измеренную частоту
358
uint32_t dds_shag= (
double
)freq * 1670.1100841768461768461768461768;
// шаг= частота*коэффициент
359
while
(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];}
//генерация DDS
360
GPIOA_BASE->CRL=0x44444444;
// все пины в Z
361
}
//END DDS set()
362
//
363
364
void
mytone(
int
frq,
int
ms ){
365
if
(mode==7){
return
;}
//таймер в режиме частотометра занят
366
uint16_t psc=1; uint32_t tim_arr;
367
// настройка генератора звука на таймере3
368
tim_arr = 36E6/frq;
369
while
( (tim_arr/psc) > 65535) {psc++;}
370
__asm volatile(
"cpsid i"
);
371
TIMER2_BASE->SMCR=0;
372
TIMER3_BASE->CCR3=0;
//обнулить регистр соответсвующий используемому выходу
373
TIMER3_BASE->PSC=psc-1;
374
TIMER3_BASE->ARR=(tim_arr/psc)-1;
375
TIMER3_BASE->CCMR2=(1<<5)|(1<<4);
// OC3M:011
376
TIMER3_BASE->CCER=1<<8;
//cc3e подключить аппаратную ногу
377
TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);
//SMS:101 && TS:001 строб от 2-го таймера
378
TIMER3_BASE->EGR=1;
//перечитать регистры.
379
TIMER3_BASE->CR1=1;
380
/// настройка выдержки времени на таймере2
381
psc=1;
382
tim_arr = 72E3 * ms;
383
while
( (tim_arr/psc) > 65536) {psc++;}
384
TIMER2_BASE->CCMR2=0;
385
TIMER2_BASE->CR2=0;
386
TIMER2_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прероываний
387
TIMER2_BASE->CNT=0;
388
TIMER2_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
389
TIMER2_BASE->PSC=psc-1;
390
TIMER2_BASE->ARR=(tim_arr/psc)-1;
391
TIMER2_BASE->EGR=1;
//перечитать регистры.
392
TIMER2_BASE->CR1|=(1<<0);
393
__asm volatile(
"cpsie i"
);
394
}
395
396
397
398
[/code]
DIMAX СПАСИБО !
Т.К. энкодер у меня не тот , прилепил кнопки.
001
[code]
002
003
004
/*Генератор с регулируемой частотой v3.0 (C)Dimax
005
IVL (240265) Добавил кнопки. Экран 128х128 ST7735 */
006
#define pwm2_polar 0 //полярность выхода PWM2
007
#define paper 0x000000 // цвет фона экрана
008
#include <Adafruit_GFX_AS.h> // Core graphics library
009
#include <Adafruit_ST7735.h> // Hardware-specific library
010
#include <SPI.h>
011
Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10);
012
volatile
int
enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0, mode=0;
013
volatile
int
encstep=1;
//шаг изменения частоты по умолчанию
014
volatile
float
freq=1000;
//частота по умолчанию
015
volatile
float
duty_out;
// переменная счёта скважности
016
float
t_hi, t_low;
//переменные счёта длины импульсов
017
uint8_t wave[512];
//массив для DDS синтеза
018
byte
sine_logo[]={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
019
44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
020
46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
021
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,
022
1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
023
024
void
setup
() {
025
SPI.setModule(2);
// выбор SPI2
026
tft.initR(INITR_BLACKTAB);
027
tft.setRotation(3);
//дисплей горизонтально, контакты слева
028
tft.fillScreen(paper);
//залить цветом по умолчанию
029
tft.setTextWrap(0);
//не переосить строки
030
nvic_irq_disable_all();
//отключить все прерывания
031
systick_disable();
// отключить системный таймер
032
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0);
//включить тактирование tim-2,3,4
033
RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);
////включить тактирование port-a-b-c,tim1
034
AFIO_BASE->MAPR=(1<<8)|(1<<6);
//tim 1 && tim 2 Partial remap
035
pinMode(PB0,PWM);
//buzzer
036
pinMode(PB4,INPUT_PULLUP);
//key+
037
pinMode(PB5,INPUT_PULLUP);
//key-
038
pinMode(PB6,INPUT_PULLUP);
//enc
039
pinMode(PB7,INPUT_PULLUP);
//enc
040
pinMode(PB8,INPUT_PULLUP);
//key enc
041
mytone(1000,50);
//сигнал после старта
042
///////// Таймер4 -обработчик энкодера
043
TIMER4_BASE->CR1=(1<<2)|(1<<9);
//CKD10 URS
044
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );
//cc1s, cc2s input mapped TI1/TI2
045
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);
//Capture/Compare 1,2 output polarity
046
TIMER4_BASE->SMCR=(1<<1)|(1<<0);
//Encoder mode3(SMS bit)стр.407
047
TIMER4_BASE->CNT=0;
048
TIMER4_BASE->ARR=1;
//ограничение счёта ( =3 для двухимпульсного энкодера)
049
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1;
050
timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
051
TIMER4_BASE->CR1|=(1<<0);
//запуск
052
//прерывание кнопки энкодера
053
attachInterrupt(PB8, key_enc_int, RISING);
054
attachInterrupt(PB4, key_plus_int, RISING);
055
attachInterrupt(PB5, key_min_int, RISING);
056
timer_set();
057
}
058
059
060
061
void
loop
() {
062
static
int
old_mode_loop=-1;
063
if
(freq > 8485) {tim_mode=1;}
else
{tim_mode=0;}
// переключать режимы таймера
064
if
(mode!=old_mode_loop) {
065
tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
066
}
//чистить полностью экран только при смене режимов
067
if
(mode==7) {mon_out(); freq_meter(); }
068
if
(mode >1 && mode<7) { mon_out(); dds_set(); }
// запуск DDS режимов
069
if
(mon_flag) { mon_out();}
//в остальных ситуациях при наличии флага вывода на дисплей
070
}
071
072
073
void
freq_meter(){
074
/////////////////////счётчик импульсов\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
075
pinMode(PA15,INPUT_PULLDOWN);
// вход частотометра
076
uint32_t imp_long,imp_hi;
//переменные измерения длины такта
077
__asm volatile(
"cpsid i"
);
078
/// Timer2 счёт младших 16 бит
079
TIMER2_BASE->CR1=0;
//стоп таймер
080
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
081
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
082
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
083
TIMER2_BASE->CCMR2=0;
084
TIMER2_BASE->CR2=1<<5;
//MMS:010 управление подчинённым в режиме "Update"
085
TIMER2_BASE->SMCR= (1<<14);
// ECE & TS:000 режим 2 внешнего тактирования & разрешение работы от таймера1
086
TIMER2_BASE->ARR=65535;
//считать до максимума
087
TIMER2_BASE->EGR=1;
//перечитать регистры.
088
TIMER2_BASE->CR1|=(1<<0);
//start timer2
089
/// Timer3 счёт старших 16 бит
090
TIMER3_BASE->CR1=1<<0;
//стоп таймер
091
TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0;
092
TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0;
093
TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;
094
TIMER3_BASE->CCMR1=0;
095
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);
//SMS:111 && TS:001 такт брать от 2-го таймера
096
TIMER3_BASE->ARR=65535;
//считать до
097
TIMER3_BASE->EGR=1;
//перечитать регистры.
098
TIMER3_BASE->CR1|=(1<<0);
//start timer3
099
/// настройка времени разрешения на таймере1 для таймера2
100
TIMER1_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прерываний
101
TIMER1_BASE->CNT=0;
102
TIMER1_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
103
TIMER1_BASE->CCER=0;
// отключить выходы таймера на физ ноги
104
TIMER1_BASE->PSC=1999;
// 72000000/20000= 36000кГц тактовая таймера
105
TIMER1_BASE->ARR=35999;
//считать до 36000 (1секунда)
106
TIMER1_BASE->EGR=1;
//перечитать регистры.
107
TIMER1_BASE->CR1|=(1<<0);
108
__asm volatile(
"cpsie i"
);
109
while
(TIMER1_BASE->CR1&1) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
110
freq= TIMER3_BASE->CNT<<16 | TIMER2_BASE->CNT ;
111
if
(freq>1E6){ t_low=0;t_hi=0; duty_out=50;
return
;}
//выйти если freq больше мегагерца
112
// Перенастройка таймера 2 в режии измерения длительности импульса и скважности
113
divider=1;
114
while
((72E6/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
115
__asm volatile(
"cpsid i"
);
116
TIMER2_BASE->CR1=0;
//стоп таймер
117
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
118
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
119
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
120
TIMER2_BASE->CCMR2=0;
121
TIMER2_BASE->CR2=0;
122
TIMER2_BASE->PSC= divider-1;
123
TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);
// TS:101 SMS:100 вход TI1FP1 , Режим сброса
124
TIMER2_BASE->CCMR1=(1<<0)|(1<<9);
//CC1 input,mapped on TI1, CC2 input,mapped on TI1
125
TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);
//cc1-Hi,cc2-lo
126
TIMER2_BASE->EGR=1;
//перечитать регистры.
127
TIMER2_BASE->CR1=(1<<0);
128
__asm volatile(
"cpsie i"
);
129
while
( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
130
TIMER2_BASE->CR1=0;
// стоп таймер
131
imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
132
imp_hi=(uint32_t) ((TIMER2_BASE->CCR2)*divider);
133
if
(freq <1000){ freq=72E6 /imp_long;}
//если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
134
duty_out= (
float
) imp_hi / (imp_long / 100.0) ;
135
t_low= (imp_long-imp_hi) /72.0 ;
136
t_hi= imp_hi /72.0;
137
mon_flag=1;
138
}
//END freq meter
139
140
141
///////////////////////////////////////////////////////////////////////////
142
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
143
///////////////////////////////////////////////////////////////////////////
144
void
mon_out(){
145
char
mybuf[10];
float
freq_out;
146
//************** Вывод первой строчки*****************************
147
tft.setCursor(0, 0);
// вперёд, вниз
148
tft.setTextColor(ST7735_GREEN, paper);
149
tft.setTextSize(2);
150
switch
(mode){
151
case
0: tft.print(
" PWM Mode "
);
break
;
152
case
1: tft.print(
"Duty Mode "
);
break
;
153
case
2: tft.print(
"Sinus DDS "
);
break
;
154
case
3: tft.print(
"Trian DDS"
);
break
;
155
case
4: tft.print(
"Pila1 DDS "
);
break
;
156
case
5: tft.print(
"Pila2 DDS "
);
break
;
157
case
6: tft.print(
"Meandr DDS "
);
break
;
158
case
7: tft.print(
"Freq meter "
);
break
;
159
}
160
//*****************Вывод второй строчки*****************************
161
tft.setTextColor(ST7735_WHITE, paper);
162
tft.setTextSize(2); tft.setCursor(0, 19);
163
if
(freq >=1E6) { freq_out=freq/1E6;}
164
else
if
(freq>=1E4) { freq_out=freq/1000;}
165
else
{ freq_out=freq;}
166
if
(mode!=7 && freq <10000) { dtostrf(freq_out, 6, 0, mybuf); tft.print(mybuf); tft.print(
" "
); }
167
else
{dtostrf(freq_out, 8, 3, mybuf); tft.print(mybuf);}
168
//********************Вывод третьей строчки*****************************
169
tft.setCursor(30, 43);
// вперёд, вниз
170
tft.setTextColor(ST7735_RED,paper);
171
if
(freq>=1E6){tft.print(
"MHz"
);}
172
else
if
(freq>=1E4){tft.print(
"kHz"
);}
173
else
{tft.print(
" Hz"
);}
174
//********************* "осциллограммы"******************************
175
tft.fillRect(5,90, 100,38,paper);
//вправо, вниз, ширина вправо, длина вниз
176
tft.drawRect(0,67, 128,61,ST7735_MAGENTA);
//вправо, вниз, ширина вправо, длина вниз
177
if
(mode<2 || mode==7){
178
tft.drawFastVLine(5, 90, 30, ST7735_CYAN);
// восход фронта статическая вер линия
179
tft.drawFastHLine(5, 91, (
int
)duty_out, ST7735_YELLOW);
//длина единицы
180
tft.drawFastHLine(5, 90, (
int
)duty_out, ST7735_YELLOW);
//паралельная линия для выделения
181
tft.drawFastVLine((
int
)duty_out+5, 91, 30, ST7735_YELLOW);
// спад
182
tft.drawFastVLine((
int
)duty_out+4, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
183
tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);
//спад конец такта статическая вер. линия
184
tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
185
tft.drawFastHLine((
int
)duty_out+5, 120, (100-(
int
)duty_out), ST7735_YELLOW);
//линия единицы 2-го такта
186
tft.drawFastHLine((
int
)duty_out+5, 119, (100-(
int
)duty_out), ST7735_YELLOW);
//паралельная линия для выделения
187
}
188
if
(mode==2){
// логотип синуса
189
for
(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+sine_logo[n],ST7735_YELLOW);
190
}
//END for
191
}
// END if (mode==2)
192
else
if
(mode==3){
// логотип треугольника
193
tft.drawLine(5,98,30,73,ST7735_YELLOW);
194
tft.drawLine(30,73,80,123,ST7735_YELLOW);
195
tft.drawLine(80,123,105,98,ST7735_YELLOW);
196
}
//END mode==3
197
else
if
(mode==4){
//логотип пилы1
198
tft.drawLine(5,123,105,73,ST7735_YELLOW);
199
tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);
//спад конец такта статическая вер. линия
200
}
//END if (mode==3)
201
else
if
(mode==5){
//логотип пилы2
202
tft.drawFastVLine(5, 73, 50, ST7735_YELLOW);
// восход фронта статическая вер линия
203
tft.drawLine(5,73,105,123,ST7735_YELLOW);
204
}
// END if (mode==4)
205
else
if
(mode==6){
//логотип меандра
206
tft.drawFastVLine(5,73,25,ST7735_YELLOW);
207
tft.drawFastHLine(5,73,50,ST7735_YELLOW);
208
tft.drawFastVLine(55,73,50,ST7735_YELLOW);
209
tft.drawFastHLine(55,123,50,ST7735_YELLOW);
210
tft.drawFastVLine(105,98,25,ST7735_YELLOW);
211
}
212
//*********************** характеристики сигнала****************************************
213
tft.setCursor(5, 70);
// вперёд, вниз
214
tft.setTextColor(ST7735_WHITE, paper);
215
tft.setTextSize(1);
216
if
(mode < 2 || mode==7){
217
tft.print(
"+Width="
);
if
(t_hi<1E3) {tft.print(t_hi); tft.print(
" uS "
);}
else
{tft.print(t_hi/1000); tft.print(
" mS "
);}
218
tft.setCursor(5, 80);
// вперёд, вниз
219
tft.print(
"-Width="
);
if
(t_low<1E3) {tft.print(t_low); tft.print(
" uS "
);}
else
{tft.print(t_low/1000); tft.print(
" mS "
);}
220
tft.setCursor(60, 95); tft.print(
"Duty = "
);
221
// tft.setCursor(60, 105);
222
tft.print(duty_out,0);tft.print(
"%"
);
223
}
//END if (mode < 2 || mode==7)
224
if
(mode!=7){
// выводить шаг кроме частотометра
225
tft.setCursor(14, 95); tft.print(
"Step="
);
226
tft.setCursor(14, 105);
227
switch
(encstep) {
228
case
1: tft.print(
" 1"
);
break
;
229
case
10: tft.print(
" 10"
);
break
;
230
case
100: tft.print(
" 100"
);
break
;
231
case
1000: tft.print(
"1000"
);
break
;
232
}
// END switch case
233
}
// END if (mode!=7)
234
if
( freq < 1) {tft.fillRect(1,68, 158,59,paper); }
235
mon_flag=0;
236
}
//END mon_out
237
238
239
240
241
//обработчик прерываний энкодера
242
void
enc_int(){
243
if
((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);}
244
else
{enc_mode(-1);}
245
}
// END VOID
246
247
// обработчик кнопки +
248
void
key_plus_int(){
249
// delay(1);
250
enc_mode(1);
251
}
252
253
// обработчик кнопки -
254
void
key_min_int(){
255
// delay(1);
256
enc_mode(-1);
257
}
258
259
// ФУНКЦИЯ конфигурации режимов
260
void
enc_mode(
int
in
){
261
modebit= (GPIOB_BASE->IDR&0x100);
//состояние кнопки. 0-нажата
262
if
(!modebit) {
263
mytone(880,30);
//звук переключения режимов
264
mode+=
in
;
265
if
(mode>7){mode=7;}
266
if
(mode<0){mode=0;}
267
if
(mode<2){timer_set();}
268
if
(mode==7) {freq=0;}
269
mon_flag=1;
270
return
;
271
}
272
mytone(4400,10);
//звук изменения частоты
273
switch
(mode){
274
case
0:
if
(tim_mode==0) { freq+=(encstep*
in
); timer_set(); }
else
{ timer_hi_set(
in
); }
break
;
275
case
1: duty_in+=
in
;
if
(duty_in>99){duty_in=99;}
if
(duty_in<1){duty_in=1;} set_duty(); calc_freq();
break
;
276
case
7:
break
;
// в частотометре не реагировать на вращение энкодера
277
default
: freq+=(encstep*
in
); mon_flag=1;
//wave режимы
278
// проверка на корректность запроса частоты
279
if
(freq<0){freq=0;}
if
(freq>2E5) {freq=2E5;}
280
}
//end switch case
281
}
//end enc_mode
282
283
284
// обработчик кнопки энкодера
285
void
key_enc_int(){
286
//delay(10);
287
if
(!modebit){modebit=1; mon_flag=1;
return
;}
// если менялся режим -выйти
288
if
(mode==1) { mytone(880,30); mode=0; mon_flag=1; ;
return
;}
//сменить режим и выйти если были в duty mode
289
mytone(220,75);
//звук переключения шага
290
switch
(encstep){
291
case
1: encstep=10;
break
;
292
case
10: encstep=100;
break
;
293
case
100: encstep=1000;
break
;
294
case
1000: encstep=1;
break
;
295
}
296
mon_flag=1;
//флаг вывода на дисплей
297
}
//end
298
299
void
set_duty(){
300
if
(mode==0 && TIMER1_BASE->ARR<100){
301
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ;
302
duty_in=50;
return
;
303
}
304
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
305
}
306
307
//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
308
void
timer_set(){
309
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
310
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
311
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
312
TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1; TIMER1_BASE->CR1=1;
313
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
314
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
315
if
(freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1;
return
;}
316
if
(freq>36E6) {freq=36E6;}
317
divider=1;
318
int
tim_arr = 72E6/freq;
319
while
( (tim_arr/divider) > 65535) {divider++;}
320
TIMER1_BASE->PSC=divider-1;
321
TIMER1_BASE->ARR=(tim_arr/divider)-1;
322
set_duty();
323
calc_freq();
324
}
325
326
//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
327
void
timer_hi_set(
int
arr){
328
__asm volatile(
"cpsid i"
);
329
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
330
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
331
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
332
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
333
//насильно уменьшить шаг с ростом частоты
334
if
(TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100;
335
if
(TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
336
if
(TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;
337
arr*=encstep;
338
int
icr= TIMER1_BASE->ARR-=arr;
339
if
(icr<1) {icr=1;}
if
(icr>65535) {icr=65535;}
340
TIMER1_BASE->ARR=icr;
341
__asm volatile(
"cpsie i"
);
342
set_duty();
343
calc_freq();
344
}
345
346
// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
347
void
calc_freq(){
348
uint32_t imp_long, imp_hi;
349
freq= 72E6/((TIMER1_BASE->ARR+1)*divider);
350
duty_out= (
float
) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
351
duty_out= floorf(duty_out);
352
imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider);
353
imp_hi=(uint32_t) ((TIMER1_BASE->CCR1)*divider);
354
t_low= (imp_long-imp_hi) /72.0 ;
355
t_hi= imp_hi /72.0;
356
mon_flag=1;
357
}
358
359
360
// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
361
void
dds_set(){
362
static
uint32_t akkum;
363
static
byte
oldmode=255;
364
TIMER1_BASE->CCER=0;
//timer output pins disable && перечитать регистры.
365
GPIOA_BASE->CRL = 0x33333333;
// pa0-pa7 выход
366
GPIOA_BASE->CRH =0x44444444;
//pa8-pa15 вход
367
if
(oldmode !=mode) {
368
if
(mode==2) {
for
(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*
float
(n)/512)+1)/2 ;}}
// синус
369
else
if
(mode==3){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=n;}
else
{wave[n]=(511-n);}}}
//треугол
370
else
if
(mode==4){
for
(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}
//пила1
371
else
if
(mode==5){
for
(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}
//пила2
372
else
if
(mode==6){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=0;}
else
{wave[n]=255;}}}
//меандр
373
oldmode=mode; }
374
//для пересчёта коэффициента вручную: частоту на экране * текущий множитель и разделить на фактически измеренную частоту
375
uint32_t dds_shag= (
double
)freq * 1670.1100841768461768461768461768;
// шаг= частота*коэффициент
376
while
(!mon_flag){GPIOA_BASE->ODR=wave[(akkum+=dds_shag)>>23];}
//генерация DDS
377
GPIOA_BASE->CRL=0x44444444;
// все пины в Z
378
}
//END DDS set()
379
//
380
381
void
mytone(
int
frq,
int
ms ){
382
if
(mode==7){
return
;}
//таймер в режиме частотометра занят
383
uint16_t psc=1; uint32_t tim_arr;
384
// настройка генератора звука на таймере3
385
tim_arr = 36E6/frq;
386
while
( (tim_arr/psc) > 65535) {psc++;}
387
__asm volatile(
"cpsid i"
);
388
TIMER2_BASE->SMCR=0;
389
TIMER3_BASE->CCR3=0;
//обнулить регистр соответсвующий используемому выходу
390
TIMER3_BASE->PSC=psc-1;
391
TIMER3_BASE->ARR=(tim_arr/psc)-1;
392
TIMER3_BASE->CCMR2=(1<<5)|(1<<4);
// OC3M:011
393
TIMER3_BASE->CCER=1<<8;
//cc3e подключить аппаратную ногу
394
TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);
//SMS:101 && TS:001 строб от 2-го таймера
395
TIMER3_BASE->EGR=1;
//перечитать регистры.
396
TIMER3_BASE->CR1=1;
397
/// настройка выдержки времени на таймере2
398
psc=1;
399
tim_arr = 72E3 * ms;
400
while
( (tim_arr/psc) > 65536) {psc++;}
401
TIMER2_BASE->CCMR2=0;
402
TIMER2_BASE->CR2=0;
403
TIMER2_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прероываний
404
TIMER2_BASE->CNT=0;
405
TIMER2_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
406
TIMER2_BASE->PSC=psc-1;
407
TIMER2_BASE->ARR=(tim_arr/psc)-1;
408
TIMER2_BASE->EGR=1;
//перечитать регистры.
409
TIMER2_BASE->CR1|=(1<<0);
410
__asm volatile(
"cpsie i"
);
411
}
412
413
414
415
[/code]
Дребезг убрал аппаратно.
Тед, в начале изучения stm32 делал что-то похожее, проверять в лом:
01
//Выход таймера PA8 (T1C1)
02
03
#define GPIOA_CRH (*(volatile unsigned int*)0x40010804)
04
#define RCC_APB2ENR (*(volatile unsigned int*)0x40021018)
05
#define TIM1_CR1 (*(volatile unsigned int*)0x40012c00)
06
#define TIM1_CCMR1 (*(volatile unsigned int*)0x40012c18)
07
#define TIM1_CCER (*(volatile unsigned int*)0x40012c20)
08
#define TIM1_PSC (*(volatile unsigned int*)0x40012c28)
09
#define TIM1_ARR (*(volatile unsigned int*)0x40012c2c)
10
#define TIM1_CCR1 (*(volatile unsigned int*)0x40012c34)
11
12
13
void
setup
(){
14
GPIOA_CRH |= (1<<1)|(1<<0)|(1<<3);
15
GPIOA_CRH &= ~(1<<2);
16
RCC_APB2ENR|= 1<<11;
//включить тактирование tim1
17
TIM1_CCER|=1<<0;
//cc1e enable
18
TIM1_CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
19
TIM1_PSC=0;
//prescaler
20
TIM1_CCR1= 0;
// скважность по умолчанию в тактах
21
TIM1_ARR= 1;
// частота по умолчанию в тактах
22
TIM1_CR1=1;
23
}
24
25
void
loop
() {
26
if
(
Serial
.available()) {
27
int
divider=1;
28
int
freq = 72E6/
Serial
.parseInt();
29
int
duty =
Serial
.parseInt();
30
while
( (freq/divider) > 65535) {divider++;}
31
TIM1_PSC=divider-1;
32
TIM1_ARR=(freq/divider)-1;
33
if
(!duty){ TIM1_CCR1= freq/divider/2;}
34
else
{ TIM1_CCR1=(freq/divider)*duty/100; }
35
freq= 72E6/((TIM1_ARR+1)*divider);
36
Serial
.print(freq);
Serial
.print(
"Hz"
);
37
float
duty2= (
float
) TIM1_CCR1 / ((TIM1_ARR+1) / 100.0) ;
38
Serial
.print(
" Duty="
);
Serial
.println(duty2,0);
39
}
40
41
}
терминал принимает два числа через пробел. Частоту и дьюти в процентах. Если дьюти не задать, то он по умолчанию 50%
Спасибо автору за проделанную работу и великолепно поданный материал!
Развел в Sprint Layout 6.0 для односторонки. Первый опыт для данной программы... но если кому полезно могу сбросить... не понял пока как прикрепить к сообщению.
Для "OLED I2C" - Необходимо заменить библиотеку и инициализацию, а также добавить update в конце отрабатки процедуры вывода.
На повторение, версии - 2.3, ушло 5 часов.
Вот так выглядит первое включение осциллографа с генератором V2.4. До 25 кГц работает. Хорошая игрушка.
Конечно можно смотреть и более высокие частоты, но с 25 кГц завал частотной характеристики. Экран неплохой. Применения не вижу. К приборам это отношения не имеет. Я планирую использовать как макет. Поиграться с выводом на экран, попробовать АЦП .
Информация на всякий пожарный:
Загрузил скетч на чистую 328 . Чтобы запустить приходится неско раз жать ресет. Заменил дисплей все Ок!
С другими библиотеками дисплей работает без проблем.
Решил тоже собратьгенератор, но не заморачиваться с печаткой. Взял голую 328, приложил к плате дисплея, исходя из наиболее выгодного расположения подкорректировал скетч и дальше вспомнил радиолюбительскую молодость и начал лепиь навесухой. В принципе на все ушло около четырех часов. Работает неплохо. Правда пила немного не идеальна, но я еще немного грешу на матрицу резисторов. В наличии были только 1,2 и 2,2 кОм. На них и собрал. Конструкция получилась достаточно компактная и жесткая. Максимальная высота монтажа не превышает толщину энкодера. Осталось корпус, но это уже не сегодня.
Вид спереди и сзади.
Осциллограма
есть у кого осцилограммы с данного генератора 1МГц-4МГц
нужна визуальная привязка для настройки входного уся цыфрового осцилографа
зараннее благодарен!
apeks1, да не жалко, вот вам 4МГц, с моего генератора версии 2.3.
ADMUX = (1<<REFS0)|(1<<MUX2)
|(1<<MUX1)
;//ADC6 measure
ADMUX = (1<<REFS0)|(1<<MUX2)
|(1<<MUX1)
|(1<<MUX0)
;//ADC7 measure
Спасибо! Вроде всё работает.
Версия 2.4 под экранчик ssd1306 128х32 I2C, вдруг кому надо :) На A4 и A5 вешается экран, A6 и A7 входы вольтометра. Библиотеку ставил через стандартный менеджер библиотек ардуино IDE, на всякий случай в коде указал ссылку на гитхаб либы. Думаю экранчик ssd1306 128х64 I2C тоже заведётся, нужно будет только поменять координаты строк ну и увеличить шрифт где нужно. А, ещё тут указать что 64 пиксела высота: ssd1306_128x64_i2c_init();
001
/* Генератор, частотометр, вольтметр на Ардуино Уно v 2.4
002
* Распиновка: Энкодер A0,A1 (in)
003
* Кнопка энкодера A2 (in)
004
* Кнопка "режим" A3 (in)
005
* Подсветка 8 (out) (вывод BL дисплея)
006
* Дисплей ssd1306 128х32 I2C - A4, A5
007
* Выход генератора 0..7 + 9 (out)
008
* Вход частотометра - 5 (in)
009
* Входы вольтметра -А6,А7 (in)
010
*/
011
012
013
const
PROGMEM uint8_t sinewave[]=
// массив синуса
014
{
015
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
016
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
017
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
018
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
019
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
020
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
021
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
022
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
023
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
024
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
025
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
026
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
027
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
028
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
029
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
030
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
031
};
032
033
const
PROGMEM uint8_t squarewave[]=
//массив меандра
034
{
035
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
036
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
037
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
038
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
039
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
040
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
041
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
042
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
043
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
044
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
045
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
046
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
047
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
048
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
049
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
050
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
051
};
052
053
const
PROGMEM uint8_t trianglewave[]=
//массив треугольника
054
{
055
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
056
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
057
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
058
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
059
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
060
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
061
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
062
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
063
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
064
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
065
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
066
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
067
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
068
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
069
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
070
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
071
};
072
073
const
PROGMEM uint8_t sawtoothwave[]=
//массив пила1
074
{
075
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
076
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
077
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
078
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
079
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
080
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
081
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
082
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
083
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
084
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
085
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
086
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
087
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
088
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
089
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
090
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
091
};
092
093
const
PROGMEM uint8_t rewsawtoothwave[]=
//массив пила2
094
{
095
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
096
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
097
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
098
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
099
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
100
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
101
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
102
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
103
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
104
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
105
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
106
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
107
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
108
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
109
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
110
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
111
};
112
113
114
const
PROGMEM
char
musor_mass[]=
//массив для подгонки адреса в флэш-памяти
115
{
116
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
117
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
118
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
119
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
120
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
121
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
122
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
123
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
124
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
125
};
126
127
128
volatile int32_t freq=1000;
// частота по умолчанию
129
volatile uint32_t icr=0;
//переменная для управления регистром сравнения таймера1
130
volatile uint16_t shag=100;
//шаг энкодера по умолчанию
131
volatile uint16_t int_tic=0;
132
volatile uint8_t regim=5;
//режим генератора по умолчанию
133
volatile uint8_t monitor_flag;
// флаг для вывода на дисплей
134
volatile uint8_t ad2,ad1,ad0;
//служебные байты для функции на ассемблере
135
volatile uint8_t duty=50;
//скважность
136
volatile uint8_t main_flag=0;
//флаг работа в режиме генератора или нет
137
138
#define int_on() PCMSK1=(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11); PCICR=(1<<PCIE1);
139
//включить прерывание PCINT1, выбор пинов прерывания A1,A2,A3
140
#define int_off() PCMSK1=0; PCICR=0; //отключить PCINT1
141
#include "ssd1306.h" // <a href="https://github.com/lexus2k/ssd1306" rel="nofollow">https://github.com/lexus2k/ssd1306</a> Можно установить через штатный менеджер библиотек
142
143
// DDS algorithm
144
static
inline
void
signalOUT(
const
uint8_t *signal){
145
asm volatile(
"eor r18, r18 ;r18<-0"
"\n\t"
146
"eor r19, r19 ;r19<-0"
"\n\t"
147
"1:"
"\n\t"
148
"add r18, %0 ;1 cycle"
"\n\t"
149
"adc r19, %1 ;1 cycle"
"\n\t"
150
"adc %A3, %2 ;1 cycle"
"\n\t"
151
"lpm ;3 cycles"
"\n\t"
152
"out %4, __tmp_reg__ ;1 cycle"
"\n\t"
153
"sbis %5, 7 ;1 cycle if no skip"
"\n\t"
154
"rjmp 1b ;2 cycles. Total 10 cycles"
"\n\t"
155
:
156
:
"r"
(ad0),
"r"
(ad1),
"r"
(ad2),
"e"
(signal),
"I"
(_SFR_IO_ADDR(PORTD)),
"I"
(_SFR_IO_ADDR(PORTB))
157
:
"r18"
,
"r19"
);
158
}
159
160
void
setup
(){
161
ssd1306_setFixedFont(ssd1306xled_font6x8);
162
ssd1306_128x32_i2c_init();
163
ssd1306_setContrast(255);
164
ssd1306_fillScreen( 0x00 );
165
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTD=musor_mass[n]; }
166
PORTD=0; DDRD=0; TCCR1A=0; TCCR1B=0; TIMSK1=0;
167
PORTC|=(1<<PC2)|(1<<PC3);
//подтяжка кнопок на A2 A3
168
DDRB|=1<<PB0;
// 8 пин -включение подсветки дисплея = выход
169
main_screen();
//стартовое сообщение и выбор задач
170
//далее загрузка генератора по умолчанию
171
TCCR0B=0; check_regim();
172
int_on();
//включить прерывание PCINT1
173
main_flag=1;
174
}
//end setup
175
176
177
void
up_down(boolean x){
// управление регулировками
178
// static boolean n=0; if (n=!n){return;} // снять ремарку для энкодеров с двойным щелчком
179
if
(TCCR1B==17 && ICR1<2800 && regim==5){
180
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
181
if
(icr<100 && shag > 10) shag=10;
182
if
(icr<10 && shag > 1) shag=1;
183
icr-=shag;
184
if
(icr<2) icr=2; }
185
else
{
if
(icr > 1800 && shag >100) shag =100; icr+=shag ; }
186
return
; }
187
if
(regim==6){
if
(ICR1>100){
188
if
(x){
if
(duty<100) {duty++; } }
189
if
(!x){
if
(duty>0) {duty--; }} }
190
else
{
191
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
192
else
{
if
(OCR1A>0) {OCR1A--; } }
193
if
(OCR1A>ICR1) OCR1A=ICR1-1;
194
duty=(uint32_t)100*OCR1A/ICR1; }
195
return
;
196
}
197
x? freq+=shag : freq-=shag ;
198
if
(freq < 1) freq=1;
199
}
200
201
202
////////////////////////////////////////////////////////////////
203
//****** ПРЕРЫВАНИЕ от кнопок и энкодера*******/////////////////
204
ISR (PCINT1_vect){
205
int_off(); PORTD=0;
206
///блок для обработки событий не в режиме генератора///
207
if
(main_flag==0) {
if
((PINC&(1<<3))==0){
while
((PINC&(1<<3))==0);
//вкл. подсветки
208
if
((PINC&(1<<2))==0){ PINB|=1<<PB0;
while
((PINC&(1<<2))==0);}
209
int_on();
return
;
//выходить если не в режиме генератора
210
}}
211
//далее всё в режиме генаратора
212
// если нажата кнопка энкодера
213
if
((PINC&(1<<3))==0){
214
while
((PINC&(1<<3))==0);
// подождать до тех пор, когда кнопку отпустят
215
//блок вкл/отк подсветки дисплея (8 пин ардуино)
216
if
((PINC&(1<<2))==0){
//если после этого нажата кнопка режимов,
217
PINB|=1<<PB0;
//менять состояние 8 пина
218
while
((PINC&(1<<2))==0);
// теперь подождать пока отпустят кнопку режим
219
int_on();
return
;
220
}
221
if
(regim==6) { regim=5; check_regim(); int_on();
return
; }
222
switch
(shag){
223
case
1: shag=10;
break
;
224
case
10: shag=100;
break
;
225
case
100: shag=1000;
break
;
226
case
1000: shag=1;
break
;
227
} check_regim(); int_on();
return
;
228
}
//конец блока *если нажата кнопка энкодера*
229
if
((PINC&(1<<2))==0){
// если нажата кнопка режимов
230
PORTD=0;
231
while
((PINC&(1<<2))==0);
232
regim++;
if
(regim==7) regim=0;
233
check_regim(); int_on();
return
;
234
}
//конец блока *если нажата кнопка режимов*
235
// если кнопки не нажимались -значит крутили энкодер:
236
up_down( ! (PINC&1)^((PINC&2)>>1) );
//отправить считанное энкодеров в другую функцию
237
// если при вращении счёт идёт не в нужную сторону, то (вставить/убрать) символ '!' up_down(!
238
check_regim(); int_on();
239
}
//конец функции обработки прерываний от кнопок PCINT1_vect
240
////////////////////КОНЕЦ_ПРЕРЫВАНИЕ_от_кнопок_и_энкодера///////////////////////////////
241
242
243
void
pwm_gen(){
//настройка таймера1
244
uint16_t divider=1;
245
icr = (16000000ul / freq /2 /divider);
246
byte
shifts[] = {3,3,2,2};
247
for
(
byte
i = 0; i < 4; i++){
248
if
(icr > 65536) {
249
divider <<= shifts[i];
250
icr = 16000000ul / freq /2 /divider; }
251
else
{ TCCR1B = (i+1)|(1<<WGM13);
break
; } }
252
ICR1=icr-1;
253
set_duty();
254
}
//end pwm_gen
255
256
257
void
loop
() {
258
if
(monitor_flag) { monitor_flag=0; monitor_out(); }
259
if
(regim <5){ PORTB&= ~(1<<7);
260
DDRD=0xFF;
//set D port as output
261
uint32_t temp=(
float
)freq /0.095367431640625;
262
ad2=temp>>16; ad1=temp>>8; ad0=temp;
263
switch
(regim){
264
case
0: signalOUT(sinewave);
break
;
265
case
1: signalOUT(trianglewave);
break
;
266
case
2: signalOUT(squarewave);
break
;
267
case
3: signalOUT(sawtoothwave);
break
;
268
case
4: signalOUT(rewsawtoothwave);
269
}
//end switch
270
DDRD=0; PORTD=0;
271
272
}
//end if (regim<5)
273
}
//end loop
274
275
276
void
monitor_out(){
277
String dutystr,stepstr;
278
dutystr= String(
"Duty:"
+ String(duty)+
"%"
);
279
stepstr=String(
"Step:"
+String(shag));
280
ssd1306_clearScreen();
281
//Вывод первой строчки
282
//ssd1306_setFixedFont(ssd1306xled_font6x8);
283
if
(freq <10000) {ssd1306_printFixed(0, 0,
"Frequency, Hz"
, STYLE_NORMAL); }
284
if
(freq >=10000) {ssd1306_printFixed(0, 0,
"Frequency, kHz"
, STYLE_NORMAL); }
285
//Вывод второй строчки
286
if
(freq <10000) {
287
String sfreq((
float
)freq, 0);
288
sfreq.trim();
289
const
char
* szfreq = sfreq.c_str();
290
ssd1306_printFixedN(0, 8,szfreq, STYLE_NORMAL, FONT_SIZE_2X);
291
}
292
if
(freq >=10000u && freq < 1000000ul ) {
293
String sfreq((
float
)freq/1000, 3);
294
const
char
* szfreq = sfreq.c_str();
295
ssd1306_printFixedN(0, 8, szfreq, STYLE_NORMAL, FONT_SIZE_2X);
296
}
297
if
(freq >=1000000ul ) {
298
String sfreq((
float
)freq/1000, 2);
299
const
char
* szfreq = sfreq.c_str();
300
ssd1306_printFixedN(0, 8, szfreq, STYLE_NORMAL, FONT_SIZE_2X);
301
}
302
//Вывод третьей строчки
303
switch
(regim){
304
case
0: ssd1306_printFixed(0, 24,
"Sinus.DDS"
, STYLE_NORMAL);
break
;
305
case
1: ssd1306_printFixed(0, 24,
"Triangl.DDS"
, STYLE_NORMAL);
break
;
306
case
2: ssd1306_printFixed(0, 24,
"Meandr.DDS"
, STYLE_NORMAL);
break
;
307
case
3: ssd1306_printFixed(0, 24,
"Pila1.DDS"
, STYLE_NORMAL);
break
;
308
case
4: ssd1306_printFixed(0, 24,
"Pila2.DDS"
, STYLE_NORMAL);
break
;
309
case
5: ssd1306_printFixed(0, 24,
"PWM Mode"
, STYLE_NORMAL);
break
;
310
case
6: ssd1306_printFixed(0, 24,
"Duty Mode"
, STYLE_NORMAL);
311
}
312
//Вывод четвёртой строчки
313
if
(regim==6) ssd1306_printFixed(68, 24, dutystr.c_str(), STYLE_NORMAL);
314
else
ssd1306_printFixed(68, 24, stepstr.c_str(), STYLE_NORMAL);
315
}
//end monitor out
316
317
318
void
set_duty(){
319
if
(regim==6&&ICR1<100)
return
;
320
if
(regim==5 && ICR1<100){
321
OCR1A=ICR1/2; duty=50;
322
return
; }
323
static
uint16_t ocr;
324
ocr=(uint32_t)ICR1*duty/100;
325
OCR1A=ocr;
326
}
327
328
void
check_regim(){
// проверка и установка режимов генератора
329
if
(regim <5){
if
(freq > 100000) freq=100000;
330
TCCR1B=0;
331
TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
332
}
// end if regim <5
333
if
(regim > 4) {
334
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
335
if
(TCCR1B==17 && ICR1<2800){
336
ICR1=icr; freq= (
float
) 8000000UL/ICR1;
337
set_duty(); }
338
else
{ pwm_gen(); }
339
}
340
PORTB|= 1<<7;
341
monitor_flag=1;
342
}
343
344
ISR (TIMER1_OVF_vect){ int_tic++; }
// прерывание частотомера
345
346
void
freq_meter(){
347
//ssd1306_setFixedFont(ssd1306xled_font6x8);
348
ssd1306_clearScreen();
349
ssd1306_printFixed(0, 0,
"Freq.counter"
, STYLE_NORMAL);
350
int_on();
//включить прерывание PCINT1
351
TIMSK1 = 1<<TOIE1;
// подключить прерывание
352
uint32_t freqm=0;
// Переменная измерения частоты
353
int_tic=0; TCNT1=0; TIFR1=0;
//всё обнулить
354
while
(1){
355
TCCR1B=7;
//тактировани от входа Т1
356
delay(2000); TCCR1B=0;
357
freqm= (((uint32_t)int_tic<<16) | TCNT1)/2;
//сложить что натикало
358
int_tic=0; TCNT1 = 0;
359
//ssd1306_setFixedFont(ssd1306xled_font6x8);
360
ssd1306_clearScreen();
361
ssd1306_printFixed(0, 0,
"Freq.counter"
, STYLE_NORMAL);
362
if
(freqm <10000) {
363
String sfreqm((
float
)freqm, 0);
364
sfreqm.trim();
365
const
char
* szfreqm = sfreqm.c_str();
366
ssd1306_printFixedN(0, 8, szfreqm, STYLE_NORMAL, FONT_SIZE_2X);
367
ssd1306_printFixed(0, 24,
"Herz"
, STYLE_NORMAL);
368
}
369
if
(freqm >=10000u && freqm < 1000000ul ) {
370
String sfreqm((
float
)freqm/1000, 3);
371
const
char
* szfreqm = sfreqm.c_str();
372
ssd1306_printFixedN(0, 8, szfreqm, STYLE_NORMAL, FONT_SIZE_2X);
373
ssd1306_printFixed(0, 24,
"KiloHerz"
, STYLE_NORMAL);
374
}
375
if
(freqm >=1000000ul ) {
376
String sfreqm((
float
)freqm/1000000ul, 3);
377
const
char
* szfreqm = sfreqm.c_str();
378
ssd1306_printFixedN(0, 8, szfreqm, STYLE_NORMAL, FONT_SIZE_2X);
379
ssd1306_printFixed(0, 24,
"MegaHerz"
, STYLE_NORMAL);
380
}
381
}
382
}
383
384
//////////////////////////////////////////////////////////////////////
385
void
volt_meter() {
386
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
387
ADCSRB=0; DIDR0=48; int_on();
388
float
ain,vcc; String ainstr,vccstr;
389
//ssd1306_setFixedFont(ssd1306xled_font6x8);
390
ssd1306_clearScreen();
391
ssd1306_printFixed(0, 0,
"Volt meter"
, STYLE_NORMAL);
392
while
(1){
393
ADMUX = (1<<REFS0)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1);
//Vcc measure
394
delay(1); vcc=(
float
)(1.1*65472) / analog_func();
395
vccstr=String(
"Vcc= "
+String(vcc,3)+
" v "
);
396
ssd1306_printFixed(0, 24, vccstr.c_str(), STYLE_NORMAL);
397
ADMUX = (1<<REFS0)|(1<<MUX2)|(1<<MUX1);
//ADC6 measure
398
delay(1); ain= analog_func()*vcc /65472 ;
399
ainstr=String(
"Ain6= "
+String(ain,3)+
" v "
);
400
ssd1306_printFixed(0, 8, ainstr.c_str(), STYLE_NORMAL);
401
ADMUX = (1<<REFS0)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0);
//ADC7 measure
402
delay(1); ain= analog_func() *vcc /65472 ;
403
ainstr=String(
"Ain7= "
+String(ain,3)+
" v "
);
404
ssd1306_printFixed(0, 16, ainstr.c_str(), STYLE_NORMAL);
405
};
//end while
406
}
//end volt meter
407
408
uint32_t analog_func(){
409
uint32_t adc_buff=0;
410
for
(
int
n=0; n<=4095; n++ ) {
411
ADCSRA |= (1<<ADSC);
412
while
(bit_is_set(ADCSRA,ADSC));
413
adc_buff += ADC;
414
}
return
adc_buff>>=6;
415
}
416
417
/////////////////////////////////////////////////////////////////
418
void
main_screen(){
419
static
boolean flag_exit =0; uint8_t n=84;
420
//ssd1306_setFixedFont(ssd1306xled_font6x8);
421
ssd1306_printFixed(0, 0,
"Freq.counter >"
, STYLE_NORMAL);
422
ssd1306_printFixed(0, 8,
"**************"
, STYLE_NORMAL);
423
ssd1306_printFixed(0, 16,
" Volt meter >"
, STYLE_NORMAL);
424
while
(flag_exit==0) {
//ждать нажатия кнопки режимов.
425
delay(350); ssd1306_printFixed(n-12, 8,
" "
, STYLE_NORMAL); n-=6;
426
if
(n==0) flag_exit=1;
427
if
((PINC&(1<<2))==0){
// если нажата кнопка режимов
428
while
((PINC&(1<<2))==0);
429
freq_meter(); }
430
if
((PINC&(1<<3))==0){
// если нажата кнопка энкодера
431
while
((PINC&(1<<3))==0);
432
volt_meter(); }
433
}
// end while
434
}
// end main_screen
Очевидно, что вместо 2E5 какое-то другое число :)
Мне было не очевидно :) Я делал перистальтический насос на ардуине там было ограничение на кол-во оборотов простой цифрой, вот там мне было очевидно, а тут мой проц завис :) Оказалось что 2E5 это число)))) Погуглил, хоть и не понял что это за числа, методом научного тыка написал то что мне надо. Спасибо за помощь.
Генератор на ардуине собирал на макетной плате проводами 1,5 дня, замучился. Решил что надо осваивать изготовление печатных плат. Выбрал KiCad, на осваивание программы и создание платы ушло 3 дня. Травил час, собирал около 3. Вот что получилось:
Не без косяков конечно :) Считал считал, но почему то у энкодера посадочные места по длине растянулись, но он все равно встал, не сделал переходные отверстия для питания экрана и энкодера, решил просто, припаял кусок провода. Не стал лудить, думал покрою лаком, но пока паял все заляпал. Придется теперь лудить. Ну вот вроде бы все.
Файлы KiCad https://yadi.sk/d/9IRSuFxi3X578E посадочные места под энкодер поправил, а вот с переходным отверстия так и не разобрался, наверно надо посадочные отверстия поставить, и не нашел у себя пищалку, соответственно ее не мерил и посадочные места под не из библиотеки.
dimax, еще раз спасибо за проект и поддержку :)
Респект автору за генератор, работает стабильно, правда частоту врёт на доли Герца, но это уж придирки)) Не могу понять чем отличается PWM и Duty режимы, в обои регулируется и частота и длительность, зачем их дублировать было?) Кстати у stm32f103 очень крутые фронты выходных сигналов, около 5 нс.

Добрый вечер! Сделал платку под голую Atmega328 TQFP32 по схеме v2.4, спаял, плата как бы запускается, шим норм идёт, а вот синус получил такой:
Проверил схему на 2 раза, вроде всё верно, помыл от канифоли - не помогает. сейчас заметил, что в версии 2.0 нижний резистор в делителе, который идёт на массу, номиналом 1к, а в версиях 2.3, 2.4 номинал 2к, не думаю что это так сильно бы изуродовало синус, но может я просто не знаю физику процесса? Голую атмегу первый раз шил (до этого только тиньки), фьюзы никакие не выставлял, просто загрузчик сперва записал, потом скеч (говорят загрузчик ставить фьюзы как надо) шил USBASP через Arduino IDE.
Может картинка кому то подскажет чего я накосячил? :)
Плата в формате Sprint Layout 5: https://yadi.sk/d/K0Lq2Inu3YVVkQ
Фотовид (картинка кликабельна):
Focus, Похоже на сдвиг массива, почитайте внимательнее параграф "Особенности" в описании версии 2.0
Поперемещал архив туда-сюда,даже адрес начала у него всегда больше сотни, потом добавил в него нулей х2, синус почти стал походить на синус. Убрал по чуть чуть пару строк нулей - теперь синус как синус. Но адрес помоему получился не кратный 100... Массив пила2 начинается со 100500
Все остальные формы так же пришли в форму :) .
А резистор нижний в делителе всётаки 2к должен быть? или 1к?
Повторил конструкцию.
2 выход через полевой транзистор дл управления нагрузкой
https://mega.nz/#!zR9T3Rib!G8u6PgksZBSvkkWdyijTcffjNB3y4wmQEGyeMM3EKgs
Спасибо.
Версия 2.5 написана под микроконтроллер LGT8F328P, который обозревался недавно тут. Функционально представляет из себя немного окультуренную версию 2.4. Так-же может потребовать подгонки массивов (подробно описано в 2.0) Отличия от 2.4 : убрана дополнительная кнопка, всё управление производится одним энкодером. Вращение энкодера не нажимая его кнопки -меняет частоту. Вращение энкодера с нажатой кнопкой -меняет режимы. Однократное нажатие/отпускание кнопки меняет шаг. Вольтметр и частотометр перенесены в общее меню. Частотометр нормально измеряет примерно до 12МГц (при тактовой МК 32МГц). Вольтметр улучшен, умеет автоматически выбирать нужный референс. (1,024/2,048/4,096 или Vcc) Адаптирована под этот МК функция DDS-синтеза (линк на автора метода). Скорострельность DDS синтеза достигла очень высокого значения -4MS/sec, (в версии 2.4 -1,6MS/Sec) это даже существенно быстрее чем в версии 3.0 на stm32 (2,6MS/Sec) Однако же вкусить плоды такой скорости не представляется возможным, т.к. ЦАП на этом МК сделали с высокоомным выходом, поэтому на высоких частотах он начинает образовывать фильтр, который заметно ухудшает характеристики. Это надо смотреть визуально, что бы понять. На осциллограмме слева -синус на 100кГц с этого ЦАПа сильно перекошён , но не изломанный как был на версии 2.0 (осцилограмма справа).
В связи с этим желательно поставить на выход ЦАПа опер. усилитель, что б ещё сильнее не ухудшить сигнал при подключении нагрузки. Уровень сигнала с ЦАП можно сделать фиксированным с референсом от одного из внутренних опорных напряжений, либо регулируемым, для этого на вход AREF нужно подать напряжение от 0 до Vcc например с переменного резистора.
Схема:
Скетч:
001
/* Генератор с регулируемой частотой и скважностью v 2.5
002
* предназначен только для МК lgt8f328p
003
* Распиновка: Энкодер пин A0,A1 (PC0,PC1)
004
* Кнопка энкодера пин 2 (PD2)
005
* Дисплей nokia 5110 - 13,12,11,10 (PB5,PB4,PB3,PB2)
006
* Выход генератора DDS пин 4 (PD4)
007
* Выход генератора PWM пин 9 (PB1)
008
* Вход вольтметра пин A3 (ADC3)
009
* Вход частотометра пин 5 (PD5)
010
*/
011
012
013
014
const
PROGMEM uint8_t sinewave[]=
// массив синуса
015
{
016
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
017
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
018
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
019
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
020
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
021
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
022
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
023
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
024
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
025
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
026
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
027
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
028
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
029
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
030
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
031
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
032
};
033
034
const
PROGMEM uint8_t squarewave[]=
//массив меандра
035
{
036
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
037
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
038
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
039
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
040
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
041
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
042
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
043
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
044
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
045
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
046
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
047
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
048
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
049
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
050
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
051
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
052
};
053
054
const
PROGMEM uint8_t trianglewave[]=
//массив треугольника
055
{
056
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
057
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
058
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
059
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
060
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
061
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
062
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
063
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
064
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
065
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
066
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
067
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
068
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
069
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
070
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
071
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01
072
};
073
074
const
PROGMEM uint8_t sawtoothwave[]=
//массив пила1
075
{
076
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
077
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
078
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
079
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
080
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
081
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
082
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
083
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
084
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
085
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
086
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
087
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
088
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
089
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
090
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
091
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
092
};
093
094
const
PROGMEM uint8_t rewsawtoothwave[]=
//массив пила2
095
{
096
0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0,
097
0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,
098
0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,
099
0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0,
100
0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,
101
0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0,
102
0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90,
103
0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,
104
0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,
105
0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,
106
0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,
107
0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,
108
0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,
109
0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,
110
0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,
111
0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,
112
};
113
114
115
const
PROGMEM
char
musor_mass[]=
//массив для подгонки адреса в флэш-памяти
116
{
117
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
118
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
119
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
120
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
121
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
122
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
123
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
124
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
125
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
126
};
127
128
// пользовательские настройки переменных по умолчанию
129
//режимы 0-синус dds, 1-треугольник dds, 2-меандр dds, 3 пила1 dds, 4 пила2 dds
130
//5- гегулировка частоты pwm, 6- Duty pwm, 7-частотометр, 8 -вольтметр
131
volatile uint8_t regim=5;
//выбор режима генератора по умолчанию
132
volatile int32_t freq=1000;
// частота по умолчанию
133
volatile uint16_t shag=100;
//шаг энкодера по умолчанию
134
#define dac_trim 5 // настройка выходного уровня ЦАП
135
// 0 -переменный резистор на пине aref, 1 -1.024V; 2-2.048v; 4-4.096V; 5-Vcc
136
#define F_CPU 32E6 //нужно указать фактическую частоту МК
137
138
139
volatile uint16_t int_tic=0;
//переменная частотометра (переполнение таймера)
140
uint16_t Vbg, involt;
//переменные вольтметра
141
volatile uint32_t icr=0;
//переменная для управления регистром сравнения таймера1
142
volatile uint8_t monitor_flag;
// флаг для вывода на дисплей
143
volatile uint8_t duty=50;
//скважность по умолчанию
144
//volatile uint8_t main_flag=0;//флаг работа в режиме генератора или нет
145
volatile uint8_t modebit=1;
//флаг смены режимов.(для анализа действия энкодера)
146
#include "lgtx8p.h"
147
#define int_on() PCMSK1=(1<<PCINT9); PCICR=(1<<PCIE1); EIMSK=1<<INT0; //прерывание на А1
148
//включить прерывание PCINT1, выбор пинов прерывания A1,
149
#define int_off() PCMSK1=0; PCICR=0; EIMSK=0; ///отключить прерывания энкодера и кнопки
150
#include <LCD5110_SSVS.h>
151
#include <avr/delay.h>
152
extern
uint8_t SmallFont[];
153
extern
uint8_t MediumNumbers[];
154
LCD5110 lcd(10,11,12,13);
155
156
// DDS algorithm
157
static
inline
void
signalOUT(uint16_t ad01,uint8_t ad2,
const
uint8_t *wvt){
158
asm volatile(
159
"ldi r28,0xA1; "
"\n\t"
160
"clr r29;"
"\n\t"
161
"eor r18, r18;"
"\n\t"
162
"eor r19, r19;"
"\n\t"
163
"1:"
"\n\t"
164
"add r18, %A0;"
"\n\t"
165
"adc r19, %B0;"
"\n\t"
166
"adc %A2, %1;"
"\n\t"
167
"lpm ;"
"\n\t"
168
"st Y, __tmp_reg__ ;"
"\n\t"
169
"sbis %3, 7 ;"
"\n\t"
170
"rjmp 1b;"
"\n\t"
171
::
"r"
(ad01),
"r"
(ad2),
"e"
(wvt),
"I"
(_SFR_IO_ADDR(PORTF))
172
:
"r18"
,
"r19"
,
"r28"
,
"r29"
);
173
}
174
175
void
setup
(){
176
lcd.InitLCD();
177
for
(
int
n=0; n <
sizeof
(musor_mass); n++ ) {PORTF=musor_mass[n]&0xFC; }
178
pinMode(A0,INPUT_PULLUP);
//encoder
179
pinMode(A1,INPUT_PULLUP);
// encoder
180
pinMode(2,INPUT_PULLUP);
//encoder key
181
pinMode(3,OUTPUT);
// buzzer
182
TCCR1A=0; TCCR1B=0; TIMSK1=0;
183
EICRA=3;
//INT0 -RISING+FALLING
184
EIFR==EIFR;
//flag clear
185
TCCR0B=0; check_regim();
186
//разгон до 32 МГц от внутреннего RC генератора
187
CLKPR = 1<<PMCE;
//разрешить изменение
188
CLKPR = 0;
//делитель =1
189
}
//end setup
190
191
192
193
/////////////// ПРЕРЫВАНИЕ от кн0пки//////////////////////////
194
ISR (INT0_vect){
// прерывание от кнопки энкодера
195
int_off();
196
if
( (TIFR3&(1<<0))==0 ){TIFR3|=1<<0;}
// флаг выходы из режима частотометра
197
if
(!modebit) { modebit=1; int_on();
return
;}
//меняли режим, просто выйти
198
if
(regim==6) { regim=5; check_regim();
return
; }
199
switch
(shag){
200
case
1: shag=10;
break
;
201
case
10: shag=100;
break
;
202
case
100: shag=1000;
break
;
203
case
1000: shag=1;
break
;
204
} check_regim();
return
;
205
}
//конец прерывания *если нажата кнопка энкодера*
206
////////////////////////////////////////////////////////////
207
208
209
ISR (PCINT1_vect){
//прерывание при вращении энкодера
210
// static boolean n=0; if (n=!n){return;} // снять ремарку для энкодеров с двойным щелчком
211
int_off();
212
bool
x = (PINC&1)^((PINC&2)>>1);
213
if
( (TIFR3&(1<<0))==0 ){TIFR3|=1<<0;}
// флаг выхода из режима частотометра
214
//////////////////// переключение режимов при нажатой кнопке//////////////////////
215
modebit= (PIND&(1<<2));
//0 -кнопка нажата. 1 -не нажата
216
if
(!modebit) {
//если кнопка нажата
217
//флаг что переключаю режимы
218
if
(x) { regim++;
if
(regim>8) {regim=0;} }
219
else
{regim-- ;
if
(regim>8) {regim=8;}}
220
check_regim();
return
;
221
}
222
////////////////// переключение частоты/////////////////////////////////////////
223
if
(TCCR1B==17 && ICR1<2800 && regim==5){
//если > 2800 герц
224
if
(x) {
if
(icr<1000 && shag > 100) shag=100;
225
if
(icr<100 && shag > 10) shag=10;
226
if
(icr<10 && shag > 1) shag=1;
227
icr-=shag;
228
if
(icr<2) icr=2; }
//ограничитель
229
else
{
if
(icr > 1800 && shag >100) shag =100; icr+=shag ; }
230
check_regim();
return
; }
231
///////////////////////////переключение Duty//////////////////////////////////
232
if
(regim==6){
if
(ICR1>100){
233
if
(x){
if
(duty<100) {duty++; } }
234
if
(!x){
if
(duty>0) {duty--; }} }
235
else
{
236
if
(x){
if
(OCR1A<ICR1) {OCR1A++; } }
237
else
{
if
(OCR1A>0) {OCR1A--; } }
238
if
(OCR1A>ICR1) OCR1A=ICR1-1;
239
240
duty=(uint32_t)100*OCR1A/ICR1; }
241
check_regim();
return
;
242
}
// end перекл. duty
243
x? freq+=shag : freq-=shag ;
//изменение частоты менее 2800 Герц
244
if
(freq < 1) freq=1; check_regim();
245
}
// end isr pcint
246
247
248
249
void
pwm_gen(){
//настройка таймера1
250
uint16_t divider=1;
251
icr = (uint32_t)(F_CPU / freq /2 /divider);
252
byte
shifts[] = {3,3,2,2};
253
for
(
byte
i = 0; i < 4; i++){
254
if
(icr > 65536) {
255
divider <<= shifts[i];
256
icr = (uint32_t)F_CPU / freq /2 /divider; }
257
else
{ TCCR1B = (i+1)|(1<<WGM13);
break
; } }
258
ICR1=icr-1;
259
set_duty();
260
}
//end pwm_gen
261
262
263
void
loop
() {
264
if
(regim <5){ dds_set();}
265
if
(regim==7) {freq_meter();}
266
if
(regim==8) {volt_meter();}
267
if
(monitor_flag) { monitor_flag=0; monitor_out(); }
268
}
//end loop
269
270
271
void
monitor_out(){
272
char
mystr[14];
//массив для вывода строк на дисплей
273
lcd.clrScr();
274
//Вывод первой строчки
275
lcd.setFont(SmallFont);
276
if
(regim<8){
277
if
(freq <1E4) {lcd.print(
"Frequency, Hz"
,CENTER,0 ); }
278
if
(freq >=1E4) {lcd.print(
"Frequency, kHz"
,CENTER,0 ); }
279
}
if
(regim==8){lcd.print(
"Voltage, Volt"
,CENTER,0 ); }
280
//Вывод второй строчки
281
lcd.setFont(MediumNumbers);
282
if
(regim<8){
283
if
(freq <10000) { lcd.printNumI(freq, CENTER, 8); }
284
if
(freq >=10000u && freq < 1000000ul ) { lcd.printNumF( ((
float
)freq/1000),3 ,CENTER, 8); }
285
if
(freq >=1000000ul ) { lcd.printNumF( ((
float
)freq/1000),2 ,CENTER, 8); }
286
}
if
(regim==8){ lcd.printNumF( ((
float
)involt/1000),3 ,CENTER, 8); }
287
//Вывод третьей строчки
288
lcd.setFont(SmallFont);
289
switch
(regim){
290
case
0: lcd.print(
"Sinus DDS"
,CENTER, 32);
break
;
291
case
1: lcd.print(
"Triangle DDS"
,CENTER, 32);
break
;
292
case
2: lcd.print(
"Meandr DDS"
,CENTER, 32);
break
;
293
case
3: lcd.print(
"Pila1 DDS"
,CENTER, 32);
break
;
294
case
4: lcd.print(
"Pila2 DDS"
,CENTER, 32);
break
;
295
case
5: lcd.print(
"PWM Mode"
,CENTER,32);
break
;
296
case
6: lcd.print(
"Duty Mode"
, CENTER, 32);
break
;
297
case
7: lcd.print(
"Freq counter"
, CENTER, 32);
break
;
298
case
8: lcd.print(
"Volt meter"
, CENTER, 32);
299
}
300
//Вывод четвёртой строчки
301
if
(regim==6) { sprintf(mystr,
"Duty=%d %%"
, duty);
302
lcd.print(mystr, CENTER, 40);}
303
else
if
(regim < 6) {sprintf(mystr,
"Step=%d"
, shag);
304
lcd.print(mystr, CENTER, 40);}
305
if
(regim==8){
306
sprintf(mystr,
"Vcc= %d.%02d v"
, (
int
)(Vbg/1000), (
int
)Vbg%1000 );
307
lcd.print(mystr, CENTER, 40);
308
}
//end if regim 8
309
}
310
//end monitor out
311
312
313
void
set_duty(){
314
if
(regim==6&&ICR1<100)
return
;
315
if
(regim==5 && ICR1<100){
316
OCR1A=ICR1/2; duty=50;
317
return
; }
318
static
uint16_t ocr;
319
ocr=(uint32_t)ICR1*duty/100;
320
OCR1A=ocr;
321
}
322
323
void
check_regim(){
// проверка и установка режимов работы
324
if
(regim <5){
// если DDS режимы:
325
if
(freq > 100000) { freq=100000;}
326
TCCR1B=0; TCCR1A=0; DDRB&=~(1<<DDB1);
// 9 pin arduino set Z-mode
327
}
// end if regim <5
328
if
(regim==5 || regim==6) {
// если PWM или Duty
329
TCCR1A=1<<COM1A1; DDRB|=1<<DDB1;
// 9 pin set output (pwm out)
330
if
(TCCR1B==17 && ICR1<2800){
331
ICR1=icr; freq= F_CPU/2/ICR1;
332
set_duty(); }
333
else
{ pwm_gen(); }
334
}
335
PORTF|= 1<<7;
//поставить флаг что-б выйти из цикла DDS, если были в цикле
336
if
(regim==7){ freq=0;}
//если частотометр то обнулить частоту
337
monitor_flag=1;
338
int_on()
339
340
}
341
342
343
ISR (TIMER1_OVF_vect){ int_tic++; }
// прерывание частотомера
344
345
void
freq_meter(){
346
//подготовка таймера1
347
TCCR1B=0; TCCR1A=0; TIMSK1 = 1<<TOIE1;
// подключить прерывание
348
int_tic=0; TCNT1=0; TIFR1=TIFR1;
//всё обнулить
349
//подготовка таймера3
350
TCCR3B=0;TCCR3A=0; TIFR3=TIFR3;
351
//все 3 регистра стравнения что б удобнее смотреть регистр флагов
352
uint16_t ocr = ( (uint32_t)F_CPU/1024) -1;
353
OCR3AH=ocr>>8; OCR3AL=ocr;
// -2 sec
354
OCR3BH=ocr>>8; OCR3BL=ocr;
// -2 sec
355
OCR3CH=ocr>>8; OCR3CL=ocr;
// -2 sec
356
TCCR3B=5<<CS30;
//старт счёта 2х секунд
357
TCCR1B=7<<CS10;
//старт счёта таймера1 от тактового входа
358
while
(TIFR3==0);
// крутится пока не будет флагов конца счёта
359
TCCR1B=0;
// стоп Тimer1
360
TIMSK1=0;
361
if
( (TIFR3&(1<<0))==0){
// если не было переполнения, то считаем
362
freq= ((uint32_t)int_tic<<16) | TCNT1;
//сложить что натикало
363
int_tic=0; TCNT1 = 0;
364
} monitor_flag=1;
365
}
366
367
368
//////////////////////ВОЛЬТМЕТР/////////////////////////////////
369
void
volt_meter() {
370
Vbg=Vbg_read();
//измерить напряжение на Vcc
371
involt=auto_range_read(3);
//измерить на входе А3
372
monitor_flag=1;
373
}
//end volt meter
374
375
376
// функция для измерения напряжения с автоматическим переключением референса
377
uint16_t auto_range_read(uint8_t
in
){
378
uint16_t an_in;
379
an_in= _adc(
in
,3);
//вкючение референса 1024
380
if
(an_in <=(800*4)) {
return
(an_in/4);}
381
an_in= _adc(
in
,2);
//вкючение референса 2048
382
if
(an_in <=(1800*2)) {
return
(an_in/2); }
383
an_in=_adc(
in
,4);
//вкючение референса 4096
384
if
(an_in <3800) {
return
an_in; }
385
an_in= _adc(
in
,1);
386
an_in= (uint32_t)an_in * Vbg / 4096;
387
return
an_in;
388
}
389
390
391
////// analog Read (input, reference)
392
uint16_t _adc( uint8_t
in
, uint8_t refs){
393
uint16_t adc; ADCSRA=1<<ADEN | 1<<ADPS2 |0<<ADPS1 |1<<ADPS0;
// clk /32
394
ADCSRB=0; ADCSRC=0; DIDR0=1<<
in
;
395
switch
(refs) {
//0 -ext, 1-Vcc, 2-2048, 3-1024, 4-4096
396
case
0: ADCSRD=0;
break
;
// AREF ничего не меняется, REFS=0
397
case
1: ADCSRD=0;
break
;
// VCC ничего не меняется, REFS=1
398
case
2: ADCSRD=0; VCAL=VCAL2;
break
;
// 2v048
399
case
3: ADCSRD=0; VCAL=VCAL1;
break
;
// 1v1024
400
case
4: refs=0; ADCSRD=1<<REFS2; VCAL=VCAL3;
break
;
// 4v096
401
}
402
ADMUX=
in
&0xF | refs<<6;
403
uint32_t akkum=0;
404
for
(
int
n=0x0; n<=0xff; n++ ) {
405
ADCSRA |= (1<<ADSC);
406
while
(ADCSRA&(1<<ADSC));
407
akkum += ADC; }
408
akkum>>=8;
409
return
(uint16_t)akkum;
410
411
}
412
//функция для измерения напряжения на Vcc
413
uint16_t Vbg_read(){
414
ADCSRB=0; ADCSRC=0;
415
ADCSRA=1<<ADEN | 1<<ADPS2 |0<<ADPS1 |1<<ADPS0;
// adc enable , clk/32
416
ADMUX=1<<REFS0 | 1<<3 | 1<<2 | 1<<0;
//ref=avcc, input= ivref
417
ADCSRD=1<<BGEN ;
//ivref=1v024
418
VCAL=VCAL1;
//load 1v024 calibrate byte
419
uint32_t bgaread=0;
420
//собрать 256 семплов для усреднения
421
for
(
int
n=0x0; n<=0xff; n++ ) {
422
ADCSRA |= (1<<ADSC);
423
while
(bit_is_set(ADCSRA,ADSC));
424
bgaread += ADC;
425
}
426
bgaread>>=8;
427
bgaread= ((uint32_t)1024<<12) / bgaread;
428
return
(uint16_t)bgaread;
429
}
430
431
void
dds_set(){
432
PORTF&= ~(1<<7);
//флаг DDS
433
switch
(dac_trim) {
434
case
0: DACON=1<<DACEN|1<<DAOE|1<<DAVS0;
break
;
435
case
1: DACON=1<<DACEN|1<<DAOE|1<<DAVS1; ADCSRD= 1<BGEN;
break
;
436
case
2: DACON=1<<DACEN|1<<DAOE|1<<DAVS1; ADCSRD= 1<BGEN|1<<IVSEL0;
break
;
437
case
4: DACON=1<<DACEN|1<<DAOE|1<<DAVS1; ADCSRD= 1<BGEN|1<<IVSEL1;
break
;
438
case
5: DACON= 1<<DACEN | 1<<DAOE;
break
;
439
} uint32_t temp= (
float
)0x1000000ul / (F_CPU/8ul) * freq;
440
switch
(regim){
441
case
0: signalOUT(temp, temp>>16,sinewave);
break
;
442
case
1: signalOUT(temp, temp>>16,trianglewave);
break
;
443
case
2: signalOUT(temp, temp>>16,squarewave);
break
;
444
case
3: signalOUT(temp, temp>>16,sawtoothwave);
break
;
445
case
4: signalOUT(temp, temp>>16,rewsawtoothwave);
446
}
//end switch
447
}
Генератор V3.1 (для МК STM32F103C8T6)
-добавил в программу поддержку тактовой частоты 128МГц, которая появилась в последних аддонах.
-программный DDS синтезатор переписал на ассемблере, скорость работы в сравнении с версией 3.0 выросла примерно в 2 раза, и стала 5 и 9 MSamples/Sec соответственно для тактовых 72 и 128 МГц. Это существенно расширило качественный диапазон. Теперь синус 500кГц вполне прилично выглядит. Пила на той же частоте правда уже не очень.
-добавил поддержку чипа- тактового синтезатора Si5351. С ним возможно устанавливать частоты с любым шагом от 4кГц до 225МГц. Из 3х выходов на si5351 использовал только один, с двух других можно вывести например фиксированные частоты. Пример есть в скетче.
-подключил все 3 выхода и вход через мультиплексор ADG704. (удобно, что-бы всё вывести на один разъём)
Все аппаратные доработки уместились в прежнем корпусе. Модуль si5351 припаял вторым этажом над R2R, а мультиплексор втиснулся рядом с разъёмом.
Схема: Минимальный набор для сборки по прежнему -плата с МК, дисплей и энкодер. Микросхемы мультиплексора и тактового синтезатора ставить не обязательно, синтезатор даже не отобразится в меню, если при старте программа не обнаружила его подключенным. В схеме есть небольшое отличие от версии 3.0 Нужно было освободить пин B8 для шины I2C, и кнопку энкодера я перекинул на PB1 В остальном распиновка совместима с 3.0
Библиотека для si5351 примечание: в этой библиотеке в файлике si5351.cpp нужно найти строчку Wire.requestFrom(i2c_bus_addr, (uint8_t)1, (uint8_t)false); и удалить выделенный операнд. Это нужно для совместимости с библиотекой wire.h. Библиотека для дисплея st7735. Библиотека Adafruit_GFX, и на всякий случай готовая прошивка.
Скетч:
001
/*Генератор с регулируемой частотой v3.1 (C)Dimax */
002
#define pwm2_polar 0 //полярность выхода PWM2
003
#define paper 0x000000 // цвет фона экрана
004
#define DDSMAX 1E6 //максимальная частота для генеартора DDS
005
#define dds_mpl_72 835.05327478167234049174700635502 //множитель DDS для частоты F_CPU 72МГц
006
//для пересчёта множителя необходимо: частоту на экране * текущий множитель и разделить на фактически измеренную частоту
007
#define dds_mpl_128 469.7191655978919104715512499704 //множитель DDS для частоты F_CPU 128Mhz
008
#define enc_on() timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
009
#define enc_off() timer_detach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT);
010
#include <Adafruit_ST7735.h> // Hardware-specific library
011
#include <SPI.h>
012
#include "si5351.h"
013
#include <Wire.h>
014
Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10);
015
Si5351 si5351;
016
boolean si5351_found=0;
017
volatile
int
enc_tic=0, duty_in=50, divider, mon_flag, modebit=1, tim_mode=0;
018
volatile
int
mode=1;
// 0- GEN_si5351, 1-PWM, 2-Duty, 3..7 DDS, 8-Freqmeter
019
volatile
int
encstep=10;
//шаг изменения частоты по умолчанию
020
volatile int32_t freq=1000;
//частота по умолчанию
021
volatile
float
duty_out;
// переменная счёта скважности
022
float
t_hi, t_low;
//переменные счёта длины импульсов
023
uint8_t wave[512];
//массив для DDS синтеза
024
uint8_t sine_logo[] __FLASH__ ={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
025
44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
026
46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
027
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,
028
1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
029
030
void
setup
() {
031
delay(100);
// пауза для дисплея
032
SPI.setModule(2);
// выбор SPI2
033
tft.initR(INITR_BLACKTAB);
034
tft.setRotation(3);
//дисплей горизонтально, контакты слева
035
tft.fillScreen(paper);
//залить цветом по умолчанию
036
tft.setTextWrap(0);
//не переносить строки
037
nvic_irq_disable_all();
//отключить все прерывания
038
systick_disable();
// отключить системный таймер
039
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0);
//включить тактирование tim-2,3,4
040
RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);
////включить тактирование port-a-b-c,tim1
041
AFIO_BASE->MAPR|=(1<<8)|(1<<6);
//tim 1 && tim 2 Partial remap
042
i2c_master_enable(I2C1, I2C_REMAP);
//SDA PB9, SCL PB8
043
Wire.begin();
044
si5351_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
//если нашёлся si5351 поднимается флажок
045
// if (si5351_found) { si5351.set_freq(16E6*SI5351_FREQ_MULT, SI5351_CLK1);// фиксированная частота с выхода 1
046
// si5351.set_freq(20E6*SI5351_FREQ_MULT, SI5351_CLK2); }//фиксированная частота с выхода 2
047
pinMode(PB0,PWM);
//buzzer
048
pinMode(PB1,INPUT_PULLUP);
//key encoder
049
pinMode(PB3,OUTPUT);
//муль типлексор
050
pinMode(PB4,OUTPUT);
//мультиплексор
051
pinMode(PB6,INPUT_PULLUP);
//enc
052
pinMode(PB7,INPUT_PULLUP);
//enc
053
mytone(1000,50);
//сигнал после старта
054
///////// Таймер4 -обработчик энкодера
055
TIMER4_BASE->CR1=(1<<2)|(1<<9);
//CKD10 URS
056
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );
//cc1s, cc2s input mapped TI1/TI2
057
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);
//Capture/Compare 1,2 output polarity
058
TIMER4_BASE->SMCR=(1<<1)|(1<<0);
//Encoder mode3(SMS bit)стр.407
059
TIMER4_BASE->CNT=0;
060
TIMER4_BASE->ARR=1;
//ограничение счёта ( =3 для двухимпульсного энкодера)
061
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1;
062
enc_on();
063
TIMER4_BASE->CR1|=(1<<0);
//запуск
064
//прерывание кнопки энкодера
065
attachInterrupt(PB1, key_enc_int, RISING);
066
timer_set();
067
}
068
069
070
071
void
loop
() {
072
static
int
old_mode_loop=-1;
073
if
(freq > 8485) {tim_mode=1;}
else
{tim_mode=0;}
// переключать режимы таймера
074
if
(mode!=old_mode_loop) {
075
tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
076
}
//чистить полностью экран только при смене режимов
077
comm();
//коммутация выходов мультиплексором
078
if
(mode==8) {mon_out(); freq_meter(); }
079
if
(mode >2 && mode<8) { mon_out(); dds_set(); }
// запуск DDS режимов
080
if
(mon_flag) { mon_out();}
//в остальных ситуациях при наличии флага вывода на дисплей
081
}
082
083
084
void
freq_meter(){
085
/////////////////////счётчик импульсов
086
pinMode(PA15,INPUT_PULLDOWN);
// вход частотометра
087
uint32_t imp_long,imp_hi;
//переменные измерения длины такта
088
__asm volatile(
"cpsid i"
);
089
/// Timer2 счёт младших 16 бит
090
TIMER2_BASE->CR1=0;
//стоп таймер
091
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
092
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
093
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
094
TIMER2_BASE->CCMR2=0;
095
TIMER2_BASE->CR2=1<<5;
//MMS:010 управление подчинённым в режиме "Update"
096
TIMER2_BASE->SMCR= (1<<14);
// ECE & TS:000 режим 2 внешнего тактирования & разрешение работы от таймера1
097
TIMER2_BASE->ARR=65535;
//считать до максимума
098
TIMER2_BASE->EGR=1;
//перечитать регистры.
099
TIMER2_BASE->CR1|=(1<<0);
//start timer2
100
/// Timer3 счёт старших 16 бит
101
TIMER3_BASE->CR1=1<<0;
//стоп таймер
102
TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0;
103
TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0;
104
TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;
105
TIMER3_BASE->CCMR1=0;
106
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);
//SMS:111 && TS:001 такт брать от 2-го таймера
107
TIMER3_BASE->ARR=65535;
//считать до
108
TIMER3_BASE->EGR=1;
//перечитать регистры.
109
TIMER3_BASE->CR1|=(1<<0);
//start timer3
110
/// настройка времени разрешения на таймере1 для таймера2
111
TIMER1_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прерываний
112
TIMER1_BASE->CNT=0;
113
TIMER1_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
114
TIMER1_BASE->CCER=0;
// отключить выходы таймера на физ ноги
115
TIMER1_BASE->PSC=F_CPU/36000 -1;
// 1999; // 72000000/2000= 36000кГц тактовая таймера
116
TIMER1_BASE->ARR=35999;
//считать до 36000 (1секунда)
117
TIMER1_BASE->EGR=1;
//перечитать регистры.
118
TIMER1_BASE->CR1|=(1<<0);
119
__asm volatile(
"cpsie i"
);
120
while
(TIMER1_BASE->CR1&1) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
121
freq= TIMER3_BASE->CNT<<16 | TIMER2_BASE->CNT ;
122
if
(freq>1E6){ t_low=0;t_hi=0; duty_out=50; mon_flag=1;
return
;}
//выйти если freq больше мегагерца
123
// Перенастройка таймера 2 в режии измерения длительности импульса и скважности
124
divider=1;
125
while
((F_CPU/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
126
__asm volatile(
"cpsid i"
);
127
TIMER2_BASE->CR1=0;
//стоп таймер
128
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
129
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
130
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
131
TIMER2_BASE->CCMR2=0;
132
TIMER2_BASE->CR2=0;
133
TIMER2_BASE->PSC= divider-1;
134
TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);
// TS:101 SMS:100 вход TI1FP1 , Режим сброса
135
TIMER2_BASE->CCMR1=(1<<0)|(1<<9);
//CC1 input,mapped on TI1, CC2 input,mapped on TI1
136
TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);
//cc1-Hi,cc2-lo
137
TIMER2_BASE->EGR=1;
//перечитать регистры.
138
TIMER2_BASE->CR1=(1<<0);
139
__asm volatile(
"cpsie i"
);
140
while
( (TIMER2_BASE->SR&0x65F)!=0x65F) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
141
TIMER2_BASE->CR1=0;
// стоп таймер
142
imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
143
imp_hi=(uint32_t) ((TIMER2_BASE->CCR2)*divider);
144
if
(freq <1000){ freq=F_CPU /imp_long;}
//если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты
145
duty_out= (
float
) imp_hi / (imp_long / 100.0) ;
146
t_low= (imp_long-imp_hi) / (F_CPU/1E6) ;
147
t_hi= imp_hi /(F_CPU/1E6);
148
mon_flag=1;
149
}
//END freq meter
150
151
152
///////////////////////////////////////////////////////////////////////////
153
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
154
///////////////////////////////////////////////////////////////////////////
155
void
mon_out(){
156
//************** Вывод первой строчки*****************************
157
tft.setCursor(0, 0);
// вперёд, вниз
158
tft.setTextColor(ST7735_GREEN, paper);
159
tft.setTextSize(2);
160
switch
(mode){
161
case
0: tft.print(
" Clock Gen "
);
break
;
162
case
1: tft.print(
" PWM Mode "
);
break
;
163
case
2: tft.print(
" Duty Mode "
);
break
;
164
case
3: tft.print(
" Sinus DDS "
);
break
;
165
case
4: tft.print(
" Triangle DDS"
);
break
;
166
case
5: tft.print(
" Pila1 DDS "
);
break
;
167
case
6: tft.print(
" Pila2 DDS "
);
break
;
168
case
7: tft.print(
" Meandr DDS "
);
break
;
169
case
8: tft.print(
" Freq. meter "
);
break
;
170
}
171
//*****************Вывод второй строчки*****************************
172
tft.setTextColor(ST7735_WHITE, paper);
173
tft.setTextSize(3); tft.setCursor(0, 19);
174
tft.print(
" "
);
//стереть строку
175
if
(freq>=1E8){tft.setCursor(0, 19);}
//переставление курсора
176
else
if
(freq>=1E5){tft.setCursor(20, 19);}
//переставление курсора
177
else
if
(freq>=1E3){tft.setCursor(40, 19);}
//переставление курсора
178
else
if
(freq>=100){tft.setCursor(60, 19);}
//переставление курсора
179
else
{tft.setCursor(80, 19);}
//переставление курсора
180
tft.print(freq);
//вывод частоты
181
//********************Вывод третьей строчки*****************************
182
tft.setCursor(50, 43);
// вперёд, вниз
183
tft.setTextColor(ST7735_RED,paper);
184
tft.print(
"Herz"
);
185
//********************* "осциллограммы"******************************
186
tft.fillRect(5,90, 100,38,paper);
//вправо, вниз, ширина вправо, длина вниз
187
tft.drawRect(0,67, 160,61,ST7735_MAGENTA);
//вправо, вниз, ширина вправо, длина вниз
188
if
(mode==1 ||mode==2 || mode==8){
189
tft.drawFastVLine(5, 90, 30, ST7735_CYAN);
// восход фронта статическая вер линия
190
tft.drawFastHLine(5, 91, (
int
)duty_out, ST7735_YELLOW);
//длина единицы
191
tft.drawFastHLine(5, 90, (
int
)duty_out, ST7735_YELLOW);
//паралельная линия для выделения
192
tft.drawFastVLine((
int
)duty_out+5, 91, 30, ST7735_YELLOW);
// спад
193
tft.drawFastVLine((
int
)duty_out+4, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
194
tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);
//спад конец такта статическая вер. линия
195
tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
196
tft.drawFastHLine((
int
)duty_out+5, 120, (100-(
int
)duty_out), ST7735_YELLOW);
//линия единицы 2-го такта
197
tft.drawFastHLine((
int
)duty_out+5, 119, (100-(
int
)duty_out), ST7735_YELLOW);
//паралельная линия для выделения
198
}
199
if
(mode==3){
// логотип синуса
200
for
(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+ sine_logo[n],ST7735_YELLOW);
201
}
//END for
202
}
// END if (mode==3)
203
else
if
(mode==4){
// логотип треугольника
204
tft.drawLine(5,98,30,73,ST7735_YELLOW);
205
tft.drawLine(30,73,80,123,ST7735_YELLOW);
206
tft.drawLine(80,123,105,98,ST7735_YELLOW);
207
}
//END mode==4
208
else
if
(mode==5){
//логотип пилы1
209
tft.drawLine(5,123,105,73,ST7735_YELLOW);
210
tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);
//спад конец такта статическая вер. линия
211
}
//END if (mode==5)
212
else
if
(mode==6){
//логотип пилы2
213
tft.drawFastVLine(5, 73, 50, ST7735_YELLOW);
// восход фронта статическая вер линия
214
tft.drawLine(5,73,105,123,ST7735_YELLOW);
215
}
// END if (mode==6)
216
else
if
(mode==7 || mode==0){
//логотип меандра
217
tft.drawFastVLine(5,73,25,ST7735_YELLOW);
218
tft.drawFastHLine(5,73,50,ST7735_YELLOW);
219
tft.drawFastVLine(55,73,50,ST7735_YELLOW);
220
tft.drawFastHLine(55,123,50,ST7735_YELLOW);
221
tft.drawFastVLine(105,98,25,ST7735_YELLOW);
222
}
223
//*********************** характеристики сигнала****************************************
224
tft.setCursor(5, 70);
// вперёд, вниз
225
tft.setTextColor(ST7735_WHITE, paper);
226
tft.setTextSize(1);
227
if
(mode==1 ||mode==2 || mode==8){
228
tft.print(
"+Width="
);
if
(t_hi<1E3) {tft.print(t_hi); tft.print(
" uS "
);}
else
{tft.print(t_hi/1000); tft.print(
" mS "
);}
229
tft.setCursor(5, 80);
// вперёд, вниз
230
tft.print(
"-Width="
);
if
(t_low<1E3) {tft.print(t_low); tft.print(
" uS "
);}
else
{tft.print(t_low/1000); tft.print(
" mS "
);}
231
tft.setCursor(114, 70); tft.print(
"Duty="
);
232
tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(
" % "
);
233
}
//END if (mode < 2 || mode==8)
234
if
(mode!=8){
// выводить шаг кроме частотометра
235
tft.setCursor(114, 95); tft.print(
"Step="
);
236
tft.setCursor(114, 105);
237
switch
(encstep) {
238
case
1: tft.print(
" 1"
);
break
;
239
case
10: tft.print(
" 10"
);
break
;
240
case
100: tft.print(
" 100"
);
break
;
241
case
1E3: tft.print(
" 1E3"
);
break
;
242
case
1E4: tft.print(
" 1E4"
);
break
;
243
case
1E5: tft.print(
" 1E5"
);
break
;
244
case
1E6: tft.print(
" 1E6"
);
break
;
245
246
}
// END switch case
247
}
// END if (mode!=8)
248
if
( freq < 1) {tft.fillRect(1,68, 158,59,paper); }
249
mon_flag=0;
250
enc_on();
251
}
//END mon_out
252
253
254
255
256
//обработчик прерываний энкодера
257
void
enc_int(){
258
if
((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);}
259
else
{enc_mode(-1);}
260
enc_off();
//выключить энкодер
261
}
// END VOID
262
263
264
// ФУНКЦИЯ конфигурации режимов
265
void
enc_mode(
int
in
){
266
modebit= (GPIOB_BASE->IDR&2);
//состояние кнопки PB1. 0-нажата
267
if
(!modebit) {
// если сейчас идёт переключение режимов
268
mytone(880,30);
//звук переключения режимов
269
mode+=
in
;
270
if
(mode>8){mode=8;}
271
if
(si5351_found) {
if
(mode<0){mode=0;} }
else
{
if
(mode<1){mode=1;} }
272
if
(mode==1 || mode==2 ){timer_set();}
273
if
(mode==0){clock_gen();}
274
if
(mode==8) {freq=0;}
275
if
(mode >2 && mode !=8) {
if
(freq>DDSMAX) {freq=DDSMAX;} }
276
mon_flag=1;
277
return
;
278
}
279
mytone(4400,10);
//звук изменения частоты
280
switch
(mode){
281
case
0: freq+=(encstep*
in
); clock_gen();
break
;
282
case
1:
if
(tim_mode==0) { freq+=(encstep*
in
); timer_set(); }
else
{ timer_hi_set(
in
); }
break
;
283
case
2: duty_in+=
in
;
if
(duty_in>99){duty_in=99;}
if
(duty_in<1){duty_in=1;} set_duty(); calc_freq();
break
;
284
case
8:
break
;
// в частотометре не реагировать на вращение энкодера
285
default
: freq+=(encstep*
in
);
if
(freq>DDSMAX) {freq=DDSMAX;} mon_flag=1;
//wave режимы
286
}
//end switch case
287
if
(freq<0){freq=0;}
288
}
//end enc_mode
289
290
291
// обработчик кнопки энкодера
292
void
key_enc_int(){
293
if
(!modebit){modebit=1; enc_step_control() ;
return
;}
// если менялся режим -выйти
294
if
(mode==2) { mytone(880,30); mode=1; enc_step_control() ;
return
;}
//сменить режим и выйти если были в duty mode
295
mytone(220,75);
//звук переключения шага
296
encstep*=10;
297
enc_step_control();
298
}
//end
299
300
void
enc_step_control(){
301
if
(mode!=0 && encstep >1E4) {encstep=1;}
302
if
(mode==0 && encstep >1E6) {encstep=1;}
303
mon_flag=1;
//флаг вывода на дисплей
304
}
305
306
void
set_duty(){
307
if
(mode==1 && TIMER1_BASE->ARR<100){
308
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ;
309
duty_in=50;
return
;
310
}
311
TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)*duty_in/100;
312
}
313
314
//ОБЩАЯ НАСТРОЙКА ТАЙМЕРА
315
void
timer_set(){
316
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
317
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
318
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
319
TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0; TIMER1_BASE->ARR=1; TIMER1_BASE->CR1=1;
320
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
321
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
322
if
(freq<1){freq=0; TIMER1_BASE->CCR1=0;TIMER1_BASE->ARR= 1; mon_flag=1;
return
;}
323
if
(freq>(F_CPU/2)) {freq=F_CPU/2;}
324
divider=1;
325
int
tim_arr = F_CPU/freq;
326
while
( (tim_arr/divider) > 65535) {divider++;}
327
TIMER1_BASE->PSC=divider-1;
328
TIMER1_BASE->ARR=(tim_arr/divider)-1;
329
set_duty();
330
calc_freq();
331
}
332
333
//НАСТРОЙКА ТАЙМЕРА НА ЧАСТОТАХ ВЫШЕ 8КГЦ
334
void
timer_hi_set(
int
arr){
335
__asm volatile(
"cpsid i"
);
336
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
337
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
338
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
339
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
340
//насильно уменьшить шаг с ростом частоты
341
if
(TIMER1_BASE->ARR<1000 && encstep > 100) encstep=100;
342
if
(TIMER1_BASE->ARR<100 && encstep > 10) encstep=10;
343
if
(TIMER1_BASE->ARR<10 && encstep > 1) encstep=1;
344
arr*=encstep;
345
int
icr= TIMER1_BASE->ARR-=arr;
346
if
(icr<1) {icr=1;}
if
(icr>65535) {icr=65535;}
347
TIMER1_BASE->ARR=icr;
348
__asm volatile(
"cpsie i"
);
349
set_duty();
350
calc_freq();
351
}
352
353
// РАСЧЁТ ЧАСТОТЫ И ТАЙМИНГОВ В РЕЖИМАХ PWM
354
void
calc_freq(){
355
uint32_t imp_long, imp_hi;
356
freq= F_CPU /((TIMER1_BASE->ARR+1)*divider);
357
duty_out= (
float
) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
358
duty_out= floorf(duty_out);
359
imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider);
360
imp_hi=(uint32_t) ((TIMER1_BASE->CCR1)*divider);
361
t_low= (imp_long-imp_hi) /(F_CPU/1E6) ;
362
t_hi= imp_hi /(F_CPU/1E6);
363
mon_flag=1;
364
}
365
366
367
// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
368
void
dds_set(){
369
static
uint32_t akkum;
370
static
byte
oldmode=255;
371
TIMER1_BASE->CCER=0;
//timer output pins disable
372
GPIOA_BASE->CRL = 0x33333333;
// pa0-pa7 выход
373
if
(oldmode !=mode) {
374
if
(mode==3) {
for
(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*
float
(n)/512)+1)/2 ;}}
// синус
375
else
if
(mode==4){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=n;}
else
{wave[n]=(511-n);}}}
//треугол
376
else
if
(mode==5){
for
(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}
//пила1
377
else
if
(mode==6){
for
(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}
//пила2
378
else
if
(mode==7){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=0;}
else
{wave[n]=255;}}}
//меандр
379
oldmode=mode; }
380
uint32_t dds_shag= (
double
)freq * ((F_CPU==72E6)? dds_mpl_72 : dds_mpl_128) ;
// шаг= частота*коэффициент
381
asm volatile (
382
"mov R9, %[port];"
"\n\t"
// записать в r9 адресс порта "A"-ODR
383
"mov R8, %[wave];"
"\n\t"
//адресс массива положить в r8
384
"mov R7, %[shag];"
"\n\t"
// значение шага в r7
385
"dds_loop:"
"\n\t"
386
"add R6, r7;"
"\n\t"
//(1)добавить к аккумулятору шаг
387
"lsrs r2, r6, #23;"
"\n\t"
//(1) положить в R2 сдвинутый на 23 бита аккумулятор
388
"ldrb r2, [r8, r2];"
"\n\t"
//(2)загрузить в R2 выбранный байт из массива
389
"strb r2, [r9];"
"\n\t"
//(2) запиcать этот байт в PORTA-ODR
390
"ldr R2, [%[flag]];"
"\n\t"
//(2) подгрузить в R2 флаг
391
"cmp r2, 1;"
"\n\t"
//(1) сравнить
392
"bne dds_loop;"
"\n\t"
//(1) перейти в цикл
393
: : [wave]
"r"
(&wave),[shag]
"r"
(dds_shag),[port]
"r"
(&GPIOA_BASE->ODR),[flag]
"r"
(&mon_flag)
394
:
"r9"
,
"r8"
,
"r7"
,
"r6"
,
"r2"
395
);
396
GPIOA_BASE->CRL=0x44444444;
// все пины в Z
397
}
//END DDS set()
398
//
399
400
401
void
mytone(
int
frq,
int
ms ){
402
if
(mode==8){
return
;}
//таймер в режиме частотометра занят
403
uint16_t psc=1; uint32_t tim_arr;
404
// настройка генератора звука на таймере3
405
tim_arr = (F_CPU/2)/frq;
406
while
( (tim_arr/psc) > 65535) {psc++;}
407
__asm volatile(
"cpsid i"
);
408
TIMER2_BASE->SMCR=0;
409
TIMER3_BASE->CCR3=0;
//обнулить регистр соответсвующий используемому выходу
410
TIMER3_BASE->PSC=psc-1;
411
TIMER3_BASE->ARR=(tim_arr/psc)-1;
412
TIMER3_BASE->CCMR2=(1<<5)|(1<<4);
// OC3M:011
413
TIMER3_BASE->CCER=1<<8;
//cc3e подключить аппаратную ногу
414
TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);
//SMS:101 && TS:001 строб от 2-го таймера
415
TIMER3_BASE->EGR=1;
//перечитать регистры.
416
TIMER3_BASE->CR1=1;
417
/// настройка выдержки времени на таймере2
418
psc=1;
419
tim_arr = (F_CPU/1E3) * ms;
420
while
( (tim_arr/psc) > 65536) {psc++;}
421
TIMER2_BASE->CCMR2=0;
422
TIMER2_BASE->CR2=0;
423
TIMER2_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прероываний
424
TIMER2_BASE->CNT=0;
425
TIMER2_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
426
TIMER2_BASE->PSC=psc-1;
427
TIMER2_BASE->ARR=(tim_arr/psc)-1;
428
TIMER2_BASE->EGR=1;
//перечитать регистры.
429
TIMER2_BASE->CR1|=(1<<0);
430
__asm volatile(
"cpsie i"
);
431
}
432
433
void
clock_gen(){
//функция работа с синтезатором si5351
434
if
(!si5351_found) {
return
;}
// на всякий случай
435
if
(freq <4000){freq=4000;}
if
(freq>225E6){ freq=225E6;}
//допустимые рамки частот
436
si5351.set_freq(freq*SI5351_FREQ_MULT, SI5351_CLK0);
437
mon_flag=1;
438
}
439
440
void
comm(){
//коммутация выходов через мультиплексор
441
if
(mode==1 || mode==2 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,LOW);}
442
else
if
(mode>2 && mode<8 ) {digitalWrite(PB3,HIGH); digitalWrite(PB4,LOW);}
443
else
if
(mode==0 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,HIGH);}
444
else
if
(mode==8) {digitalWrite(PB3,HIGH); digitalWrite(PB4,HIGH);}
445
}
Доделал свой DDS v3.0, зделал корпус.
Автору большая благодарность.
Правда есть проблемы,для контроля выходного сигнала,проходится использовать осцилограф.
Линейных перемеников для печатных плат ненашол,применил что было а они нелинейны.
Нужно переделывать шкалы резисторов.
Генератор v3.2
-изменён вывод на дисплей, каждые три знака в значении частоты разделяются пробелом.
-добавлен шаг 0,1 Гц для частот до 1кГц. Минимальная частота тоже стала 0,1 Герц
-устранены мелкие недочёты в частотометре.
Схема как в версии 3.1 , без изменений.
Готовая прошивка
Скетч:
001
/*Генератор с регулируемой частотой v3.2 (C)Dimax */
002
#define pwm2_polar 0 //полярность выхода PWM2
003
#define paper 0x000000 // цвет фона экрана
004
#define DDSMAX 1E7 //максимальная частота для генератора DDS (удесятерённая)
005
#define dds_mpl_72 835.05327478167234049174700635502 //множитель DDS для частоты F_CPU 72МГц
006
//для пересчёта множителя необходимо: частоту на экране * текущий множитель и разделить на фактически измеренную частоту
007
#define dds_mpl_128 469.7191655978919104715512499704 //множитель DDS для частоты F_CPU 128Mhz
008
#define enc_on() timer_attach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT, enc_int);
009
#define enc_off() timer_detach_interrupt(TIMER4, TIMER_UPDATE_INTERRUPT);
010
#include <Adafruit_ST7735.h> // Hardware-specific library
011
#include <SPI.h>
012
#include "si5351.h"
013
#include <Wire.h>
014
Adafruit_ST7735 tft = Adafruit_ST7735(PB12, PB11,PB10);
015
Si5351 si5351;
016
boolean si5351_found=0;
017
volatile
int
enc_tic=0, duty_in=50, mon_flag, divider, modebit=1;
018
volatile
int
mode=1;
// 0- GEN_si5351, 1-PWM, 2-Duty, 3..7 DDS, 8-Freqmeter
019
volatile
int
encstep=10;
//шаг изменения частоты по умолчанию (желаемый *10)
020
volatile int32_t freq=10000;
//частота по умолчанию (желаемая *10)
021
volatile
float
duty_out;
// переменная счёта скважности
022
float
t_hi, t_low;
//переменные счёта длины импульсов
023
uint8_t wave[512];
//массив для DDS синтеза
024
uint8_t sine_logo[] __FLASH__ ={25,27,28,30,31,33,34,36,37,38,40,41,42,43,
025
44,45,46,47,48,48,49,49,50,50,50,50,50,50,50,49,49,48,48,47,
026
46,45,44,43,42,41,40,38,37,36,34,33,31,30,28,27,25,23,22,20,
027
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,
028
1,2,2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,23};
029
030
void
setup
() {
031
delay(100);
// пауза для дисплея
032
SPI.setModule(2);
// выбор SPI2
033
tft.initR(INITR_BLACKTAB);
034
tft.setRotation(3);
//дисплей горизонтально, контакты слева
035
tft.fillScreen(paper);
//залить цветом по умолчанию
036
tft.setTextWrap(0);
//не переносить строки
037
Serial
.end();
// дефолтовый сериал не нужен
038
nvic_irq_disable_all();
//отключить все прерывания
039
systick_disable();
// отключить системный таймер
040
RCC_BASE->APB1ENR|= (1<<2)|(1<<1)|(1<<0);
//включить тактирование tim-2,3,4
041
RCC_BASE->APB2ENR|= (1<<3)|(1<<11)|(1<<2)|(1<<0)|(1<<4);
////включить тактирование port-a-b-c,tim1
042
AFIO_BASE->MAPR|=(1<<8)|(1<<6);
//tim 1 && tim 2 Partial remap
043
i2c_master_enable(I2C1, I2C_REMAP);
//SDA PB9, SCL PB8
044
Wire.begin();
045
si5351_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
//если нашёлся si5351 поднимается флажок
046
// if (si5351_found) { si5351.set_freq(16E6*SI5351_FREQ_MULT, SI5351_CLK1);// фиксированная частота с выхода 1
047
// si5351.set_freq(20E6*SI5351_FREQ_MULT, SI5351_CLK2); }//фиксированная частота с выхода 2
048
pinMode(PB0,PWM);
//buzzer
049
pinMode(PB1,INPUT_PULLUP);
//key encoder
050
pinMode(PB3,OUTPUT);
//мультиплексор
051
pinMode(PB4,OUTPUT);
//мультиплексор
052
pinMode(PB6,INPUT_PULLUP);
//encoder
053
pinMode(PB7,INPUT_PULLUP);
//encoder
054
mytone(1000,50);
//сигнал после старта
055
///////// Таймер4 -обработчик энкодера
056
TIMER4_BASE->CR1=(1<<2)|(1<<9);
//CKD10 URS
057
TIMER4_BASE->CCMR1=0xf1f1; (1<<0)|(1<<8 );
//cc1s, cc2s input mapped TI1/TI2
058
TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);
//Capture/Compare 1,2 output polarity
059
TIMER4_BASE->SMCR=(1<<1)|(1<<0);
//Encoder mode3(SMS bit)стр.407
060
TIMER4_BASE->CNT=0;
061
TIMER4_BASE->ARR=1;
//ограничение счёта ( =3 для двухимпульсного энкодера)
062
TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1;
063
enc_on();
064
TIMER4_BASE->CR1|=(1<<0);
//запуск
065
//прерывание кнопки энкодера
066
attachInterrupt(PB1, key_enc_int, RISING);
067
if
(mode==1){ timer_set(0); }
068
}
069
070
071
072
void
loop
() {
073
static
int
old_mode_loop=-1;
074
if
(mode!=old_mode_loop) {
075
tft.fillScreen(paper); old_mode_loop=mode; mon_flag=1;
076
}
//чистить полностью экран только при смене режимов
077
comm();
//коммутация выходов мультиплексором
078
if
(mode==8) {mon_out(); freq_meter(); }
079
if
(mode >2 && mode<8) { mon_out(); dds_set(); }
// запуск DDS режимов
080
if
(mon_flag) { mon_out();}
//в остальных ситуациях при наличии флага вывода на дисплей
081
}
082
083
084
void
freq_meter(){
085
/////////////////////счётчик импульсов
086
pinMode(PA15,INPUT_PULLDOWN);
// вход частотометра
087
uint32_t imp_long,imp_hi;
//переменные измерения длины такта
088
__asm volatile(
"cpsid i"
);
089
/// Timer2 счёт младших 16 бит
090
TIMER2_BASE->CR1=0;
//стоп таймер
091
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
092
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
093
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
094
TIMER2_BASE->CCMR2=0;
095
TIMER2_BASE->CR2=1<<5;
//MMS:010 управление подчинённым в режиме "Update"
096
TIMER2_BASE->SMCR= (1<<14);
// ECE & TS:000 режим 2 внешнего тактирования & разрешение работы от таймера1
097
TIMER2_BASE->ARR=65535;
//считать до максимума
098
TIMER2_BASE->EGR=1;
//перечитать регистры.
099
TIMER2_BASE->CR1|=(1<<0);
//start timer2
100
/// Timer3 счёт старших 16 бит
101
TIMER3_BASE->CR1=1<<0;
//стоп таймер
102
TIMER3_BASE->CCER=0; TIMER3_BASE->PSC=0; TIMER3_BASE->CNT=0;
103
TIMER3_BASE->CCR1=0; TIMER3_BASE->CCR2=0; TIMER3_BASE->CCR3=0;
104
TIMER3_BASE->CCR4=0;TIMER3_BASE->PSC=0;TIMER3_BASE->SR=0;TIMER3_BASE->CR2=0;
105
TIMER3_BASE->CCMR1=0;
106
TIMER3_BASE->SMCR=(1<<2)|(1<<1)|(1<<0)|(1<<4);
//SMS:111 && TS:001 такт брать от 2-го таймера
107
TIMER3_BASE->ARR=65535;
//считать до
108
TIMER3_BASE->EGR=1;
//перечитать регистры.
109
TIMER3_BASE->CR1|=(1<<0);
//start timer3
110
/// настройка времени разрешения на таймере1 для таймера2
111
TIMER1_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прерываний
112
TIMER1_BASE->CNT=0;
113
TIMER1_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
114
TIMER1_BASE->CCER=0;
// отключить выходы таймера на физ ноги
115
TIMER1_BASE->PSC=F_CPU/36000 -1;
// 1999; // 72000000/2000= 36000кГц тактовая таймера
116
TIMER1_BASE->ARR=35999;
//считать до 36000 (1секунда)
117
TIMER1_BASE->EGR=1;
//перечитать регистры.
118
TIMER1_BASE->CR1|=(1<<0);
119
__asm volatile(
"cpsie i"
);
120
while
(TIMER1_BASE->CR1&1) {asm volatile(
"nop"
);
if
(mon_flag) {
return
;} }
121
freq= TIMER3_BASE->CNT<<16 | TIMER2_BASE->CNT ;
//частота не удесятерённая
122
if
(freq>1E5){freq*=10; t_low=0;t_hi=0; duty_out=0; mon_flag=1;
return
;}
//выйти если freq больше 100кГц
123
// Перенастройка таймера 2 в режии измерения длительности импульса и скважности для частот менее 100 кГц
124
divider=1;
125
while
((F_CPU/divider/((freq>0)? freq : 1 )) > 65000) {divider++;}
126
__asm volatile(
"cpsid i"
);
127
TIMER2_BASE->CR1=0;
//стоп таймер
128
TIMER2_BASE->CCER=0; TIMER2_BASE->PSC=0; TIMER2_BASE->CNT=0;
129
TIMER2_BASE->CCR1=0; TIMER2_BASE->CCR2=0; TIMER2_BASE->CCR3=0;
130
TIMER2_BASE->CCR4=0;TIMER2_BASE->PSC=0;TIMER2_BASE->SR=0;
131
TIMER2_BASE->CCMR2=0;
132
TIMER2_BASE->CR2=0;
133
TIMER2_BASE->PSC= divider-1;
134
TIMER2_BASE->SMCR=(1<<4)|(1<<6)|(1<<2);
// TS:101 SMS:100 вход TI1FP1 , Режим сброса
135
TIMER2_BASE->CCMR1=(1<<0)|(1<<9);
//CC1 input,mapped on TI1, CC2 input,mapped on TI1
136
TIMER2_BASE->CCER=(1<<5)|(1<<0)|(1<<4);
//cc1-Hi,cc2-lo
137
TIMER2_BASE->EGR=1;
//перечитать регистры.
138
/// настройка таймера1 для счёта тайм-аута при измерения PWM
139
TIMER1_BASE->CR1=(1<<3);
//один импульс, без прерываний
140
TIMER1_BASE->CNT=0; TIMER1_BASE->CR2=0; TIMER1_BASE->CCER=0;
141
TIMER1_BASE->PSC=F_CPU/15625 -1;
// тактовая таймера 15625 Герц
142
TIMER1_BASE->ARR=31250;
//считать до 31250 (2 секунды)
143
TIMER1_BASE->EGR=1;
//перечитать регистры.
144
timer_attach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT, myint);
145
TIMER1_BASE->CR1|=(1<<0);
// старт счёта 2х секунд
146
__asm volatile(
"cpsie i"
);
147
TIMER2_BASE->CR1=(1<<0);
// старт захвата PWM
148
while
( (TIMER2_BASE->SR&0x65F)!=0x65F) {
149
asm volatile(
"nop"
);
if
(mon_flag) {timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
return
;} }
150
TIMER2_BASE->CR1=0;
// стоп таймер
151
timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
152
imp_long=(uint32_t) ((TIMER2_BASE->CCR1)*divider);
153
imp_hi=(uint32_t) ((TIMER2_BASE->CCR2)*divider);
154
if
(freq <1000){ freq= F_CPU*10 /imp_long ;}
//если freq Менее 1кГц то использовать данные второго НЧ-измерения частоты (*10)
155
else
{freq*=10; }
//иначе просто удесятерить результат для корректного вывода информации.
156
duty_out= (
float
) imp_hi / (imp_long / 100.0) ;
157
if
(duty_out > 100 || duty_out < 0) {duty_out=0;}
// на всякий случай ограничение
158
t_low= (
double
)(imp_long-imp_hi) / (F_CPU/1E6) ;
159
t_hi= (
double
) imp_hi /(F_CPU/1E6);
160
mon_flag=1;
161
}
//END freq meter
162
163
// прерывание тайм-аута при отсутствиии сигнала на входе при измерении PWM
164
void
myint(){ mon_flag=1; t_low=0; t_hi=0; duty_out=0; freq=0;
165
timer_detach_interrupt(TIMER1, TIMER_UPDATE_INTERRUPT);
166
}
167
168
///////////////////////////////////////////////////////////////////////////
169
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
170
///////////////////////////////////////////////////////////////////////////
171
void
mon_out(){
172
char
mybuf[15];
173
//************** Вывод первой строчки*****************************
174
tft.setCursor(0, 0);
// вперёд, вниз
175
tft.setTextColor(ST7735_GREEN, paper);
176
tft.setTextSize(2);
177
switch
(mode){
178
case
0: tft.print(
" Clock Gen "
);
break
;
179
case
1: tft.print(
" PWM Mode "
);
break
;
180
case
2: tft.print(
" Duty Mode "
);
break
;
181
case
3: tft.print(
" Sinus DDS "
);
break
;
182
case
4: tft.print(
" Triangle DDS"
);
break
;
183
case
5: tft.print(
" Pila1 DDS "
);
break
;
184
case
6: tft.print(
" Pila2 DDS "
);
break
;
185
case
7: tft.print(
" Meandr DDS "
);
break
;
186
case
8: tft.print(
" Freq. meter "
);
break
;
187
}
188
//*****************Вывод второй строчки*****************************
189
tft.setTextColor(ST7735_WHITE, paper);
190
tft.setCursor(0, 19); tft.setTextSize(3);
191
if
(freq>=1E8) {tft.print(
" "
); tft.setTextSize(2);tft.setCursor(0, 21);}
192
if
(freq<10) {sprintf(mybuf,
" 0,%d "
, freq );}
//9 -> 0,9
193
else
if
(freq<100){sprintf(mybuf,
" %d,%d "
, freq/10, freq%10 );}
//99 -> 9,9
194
else
if
(freq<1E3){sprintf(mybuf,
" %d,%d "
, freq/10, freq%10 );}
//999 -> 99,9
195
else
if
(freq<1E4){sprintf(mybuf,
" %d,%d "
, freq/10, freq%10 );}
//9999 -> 999,9
196
else
if
(freq<1E5){sprintf(mybuf,
" %ld %03ld "
, freq/10000, (freq/10)%1000 );}
//99999 -> 9.999
197
else
if
(freq<1E6){sprintf(mybuf,
" %ld %03ld "
, freq/10000, (freq/10)%1000 );}
//999999 -> 99.999
198
else
if
(freq<1E7){sprintf(mybuf,
" %ld %03ld "
, freq/10000, (freq/10)%1000 );}
//999999 -> 999.999
199
else
if
(freq<1E8){sprintf(mybuf,
"%ld %03ld %03ld"
, freq/10000000, (freq%10000000)/10000, (freq%10000)/10 );}
//9999999 -> 9.999.999
200
else
{sprintf(mybuf,
"%3ld %03ld %03ld"
, freq/10000000, (freq%10000000)/10000, (freq%10000)/10 );}
//99999999 -> 99.999.999
201
tft.print(mybuf);
//вывод частоты
202
//Serial.println(mybuf);
203
//********************Вывод третьей строчки*****************************
204
tft.setTextSize(3);
//крупно
205
tft.setCursor(50, 43);
// вперёд, вниз
206
tft.setTextColor(ST7735_RED,paper);
207
tft.print(
"Herz"
);
208
//********************* "осциллограммы"******************************
209
tft.fillRect(5,90, 100,38,paper);
// зачистка пяточка (вправо, вниз, ширина вправо, длина вниз)
210
tft.drawRect(0,67, 160,61,ST7735_MAGENTA);
//вправо, вниз, ширина вправо, длина вниз
211
if
(mode==1 ||mode==2 || mode==8){
212
tft.drawFastVLine(5, 90, 30, ST7735_CYAN);
// восход фронта статическая вер линия
213
tft.drawFastHLine(5, 91, (
int
)duty_out, ST7735_YELLOW);
//длина единицы
214
tft.drawFastHLine(5, 90, (
int
)duty_out, ST7735_YELLOW);
//паралельная линия для выделения
215
tft.drawFastVLine((
int
)duty_out+5, 91, 30, ST7735_YELLOW);
// спад
216
tft.drawFastVLine((
int
)duty_out+4, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
217
tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);
//спад конец такта статическая вер. линия
218
tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);
//паралельная линия для выделения
219
tft.drawFastHLine((
int
)duty_out+5, 120, (100-(
int
)duty_out), ST7735_YELLOW);
//линия единицы 2-го такта
220
tft.drawFastHLine((
int
)duty_out+5, 119, (100-(
int
)duty_out), ST7735_YELLOW);
//паралельная линия для выделения
221
}
222
if
(mode==3){
// логотип синуса
223
for
(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+ sine_logo[n],ST7735_YELLOW);
224
}
//END for
225
}
// END if (mode==3)
226
else
if
(mode==4){
// логотип треугольника
227
tft.drawLine(5,98,30,73,ST7735_YELLOW);
228
tft.drawLine(30,73,80,123,ST7735_YELLOW);
229
tft.drawLine(80,123,105,98,ST7735_YELLOW);
230
}
//END mode==4
231
else
if
(mode==5){
//логотип пилы1
232
tft.drawLine(5,123,105,73,ST7735_YELLOW);
233
tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);
//спад конец такта статическая вер. линия
234
}
//END if (mode==5)
235
else
if
(mode==6){
//логотип пилы2
236
tft.drawFastVLine(5, 73, 50, ST7735_YELLOW);
// восход фронта статическая вер линия
237
tft.drawLine(5,73,105,123,ST7735_YELLOW);
238
}
// END if (mode==6)
239
else
if
(mode==7 || mode==0){
//логотип меандра
240
tft.drawFastVLine(5,73,25,ST7735_YELLOW);
241
tft.drawFastHLine(5,73,50,ST7735_YELLOW);
242
tft.drawFastVLine(55,73,50,ST7735_YELLOW);
243
tft.drawFastHLine(55,123,50,ST7735_YELLOW);
244
tft.drawFastVLine(105,98,25,ST7735_YELLOW);
245
}
246
//*********************** характеристики сигнала****************************************
247
tft.setCursor(5, 70);
// вперёд, вниз
248
tft.setTextColor(ST7735_WHITE, paper);
249
tft.setTextSize(1);
250
if
(mode==1 ||mode==2 || mode==8){
251
tft.print(
"+Width="
);
if
(t_hi<1E3) {tft.print(t_hi); tft.print(
" uS "
);}
else
{tft.print(t_hi/1000); tft.print(
" mS "
);}
252
tft.setCursor(5, 80);
// вперёд, вниз
253
tft.print(
"-Width="
);
if
(t_low<1E3) {tft.print(t_low); tft.print(
" uS "
);}
else
{tft.print(t_low/1000); tft.print(
" mS "
);}
254
tft.setCursor(114, 70); tft.print(
"Duty="
);
255
tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(
" % "
);
256
}
//END if (mode < 2 || mode==8)
257
if
(mode!=8){
// выводить шаг кроме частотометра
258
tft.setCursor(114, 95); tft.print(
"Step="
);
259
tft.setCursor(114, 105);
260
switch
(encstep) {
261
case
1: tft.print(
" 0,1"
);
break
;
262
case
10: tft.print(
" 1"
);
break
;
263
case
100: tft.print(
" 10"
);
break
;
264
case
1E3: tft.print(
" 100"
);
break
;
265
case
1E4: tft.print(
" 1E3"
);
break
;
266
case
1E5: tft.print(
" 1E4"
);
break
;
267
case
1E6: tft.print(
" 1E5"
);
break
;
268
case
1E7: tft.print(
" 1E6"
);
break
;
269
270
}
// END switch case
271
}
// END if (mode!=8)
272
//if ( freq < 1) {tft.fillRect(1,68, 158,59,paper); }
273
mon_flag=0;
274
enc_on();
275
}
//END mon_out
276
277
278
279
280
//обработчик прерываний энкодера
281
void
enc_int(){
282
if
((TIMER4_BASE->CR1)&1<<4) {enc_mode(1);}
283
else
{enc_mode(-1);}
284
enc_off();
//выключить энкодер
285
}
// END VOID
286
287
288
// ФУНКЦИЯ конфигурации режимов
289
void
enc_mode(
int
in
){
290
modebit= (GPIOB_BASE->IDR&2);
//состояние кнопки PB1. 0-нажата
291
if
(!modebit) {
// если сейчас идёт переключение режимов
292
mytone(880,30);
//звук переключения режимов
293
mode+=
in
;
294
if
(mode>8){mode=8;}
295
if
(si5351_found) {
if
(mode<0){mode=0;} }
else
{
if
(mode<1){mode=1;} }
296
if
(mode==1 || mode==2 ){timer_set(0);}
297
if
(mode==0){clock_gen();}
298
if
(mode==8) {freq=0;}
299
if
(mode >2 && mode !=8) {
if
(freq>DDSMAX) {freq=DDSMAX;} }
300
mon_flag=1;
301
return
;
302
}
303
mytone(4400,10);
//звук изменения частоты
304
switch
(mode){
//если сейчас идёт изменение частоты
305
case
0: freq+=(encstep*
in
); clock_gen();
break
;
306
case
1: timer_set(
in
);
break
;
307
case
2: duty_in+=
in
; timer_set(0);
break
;
308
case
8:
break
;
// в частотометре не реагировать на вращение энкодера
309
default
: freq+=(encstep*
in
);
if
(freq>DDSMAX) {freq=DDSMAX;} mon_flag=1;
//DDS режимы
310
}
//end switch case
311
if
(freq<0){freq=0;}
312
}
//end enc_mode
313
314
315
// обработчик кнопки энкодера
316
void
key_enc_int(){
317
if
(!modebit){modebit=1; enc_step_control() ;
return
;}
// если менялся режим -выйти
318
if
(mode==2) { mytone(880,30); mode=1; enc_step_control() ;
return
;}
//сменить режим и выйти если были в duty mode
319
mytone(220,75);
//звук переключения шага
320
encstep*=10;
321
enc_step_control();
322
}
//end
323
324
void
enc_step_control(){
//ограничение шага в зависимости от режимов и частот
325
// для CLOCK режима если шаг более 1 МГц то шаг сбросить на 0,1 или 1 Герц (зацикливание переключений)
326
if
(mode==0 && encstep >1E7) {encstep=1;}
// Для CLOCK режима макс шаг 1МГц
327
if
(mode==1 && encstep >1E5) {encstep=1;}
//для PWM макс шаг 10 000 Гц
328
if
(mode>2 && encstep >1E6) {encstep=1;}
//для DDS макс шаг 100 000 Гц
329
if
(encstep==1 && freq >=10000 ) encstep=10;
// менять шаг 0,1 Гц -> 1Гц на частотах выше 1кГц для всех режимов
330
mon_flag=1;
//флаг вывода на дисплей
331
}
332
333
334
335
////////////////НАСТРОЙКА ТАЙМЕРА-ГЕНЕРАТОРА/////////////////////////////////////////////
336
void
timer_set(
int
in
){
//принимает +1 -1 или 0
337
int
tim_arr; uint32_t imp_long, imp_hi;
338
//общие настройки таймера1
339
GPIOA_BASE->CRL=0xB4444444;
//PA7 alt_output
340
GPIOA_BASE->CRH=0x4444444B;
//PA8 alt_output
341
TIMER1_BASE->CR1=0;
342
TIMER1_BASE->CCMR2=0;TIMER1_BASE->PSC=0; TIMER1_BASE->CCR2=0;
343
TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);
//cc1e/cc1ne enable
344
//TIMER1_BASE->BDTR=(1<<15)| 255 ;// dead time sample
345
TIMER1_BASE->CCMR1=(1<<6)|(1<<5)|(1<<3);
//PWM mode 1
346
if
(freq < 84850){
//изменение частоты таймера по заданной частоте
347
if
(
in
) {freq+=(encstep*
in
);}
//если передавалось изменение частоты, то рассчитать
348
if
(freq<1){freq=1;}
if
(freq>(F_CPU/2*10)) {freq=F_CPU/2*10;}
// ограничение макс. частоты *10
349
tim_arr = F_CPU*10/freq;
350
divider=1;
while
( (tim_arr/divider) > 65535) {divider++;}
351
TIMER1_BASE->PSC=divider-1;
352
TIMER1_BASE->ARR=(tim_arr/divider)-1;
353
}
//end f (freq < 84850)
354
else
{
// изменение частоты таймера инкрементом регистра ARR
355
tim_arr=TIMER1_BASE->ARR;
//снять тукущее состояния регистра
356
if
(tim_arr<1000 && encstep > 1000) encstep=1000;
// уменьшать шаг с ростом частоты
357
if
(tim_arr<100 && encstep > 100) encstep=100;
// уменьшать шаг с ростом частоты
358
if
(tim_arr<10 && encstep > 10) encstep=10;
// уменьшать шаг с ростом частоты
359
in
*=(encstep/10);
360
tim_arr-=
in
;
361
if
(tim_arr<1) {tim_arr=1;}
if
(tim_arr>65535) {tim_arr=65535;}
362
TIMER1_BASE->ARR=tim_arr;
363
}
// END изменение частоты таймера инкрементом регистра ARR
364
// установка заданного DUTY
365
if
(duty_in>99){duty_in=99;}
if
(duty_in<1){duty_in=1;}
366
if
(mode==1 && TIMER1_BASE->ARR<100){ TIMER1_BASE->CCR1=(TIMER1_BASE->ARR+1)/2 ; duty_in=50;}
//сбрасывать duty на 50% на высоких частотах
367
else
{TIMER1_BASE->CCR1= (
float
) (TIMER1_BASE->ARR+1)* duty_in/100.0 ;}
//или рассчитать
368
freq= F_CPU*10 /((TIMER1_BASE->ARR+1)*divider);
// рассчёт фактической частоты
369
duty_out= (
float
) TIMER1_BASE->CCR1 / ((TIMER1_BASE->ARR+1) / 100.0) ;
//расчёт фактического duty
370
duty_out= floorf(duty_out);
//округление
371
imp_long=(uint32_t) ((TIMER1_BASE->ARR+1)*divider);
//длина периода в тактах
372
imp_hi=(uint32_t) ((TIMER1_BASE->CCR1)*divider);
// длина импульса в тактах
373
t_low= (imp_long-imp_hi) /(F_CPU/1E6) ;
//время LOW
374
t_hi= imp_hi /(F_CPU/1E6);
//время HI
375
TIMER1_BASE->CR1=1;
376
mon_flag=1;
377
}
//end timer_set
378
379
380
381
382
383
384
385
// КОНФИГУРАЦИЯ DDS РЕЖИМОВ
386
void
dds_set(){
387
static
uint32_t akkum;
388
static
byte
oldmode=255;
389
TIMER1_BASE->CCER=0;
//timer output pins disable
390
GPIOA_BASE->CRL = 0x33333333;
// pa0-pa7 выход
391
if
(oldmode !=mode) {
392
if
(mode==3) {
for
(uint16_t n=0; n<512; n++){wave[n]=255*(sin(TWO_PI*
float
(n)/512)+1)/2 ;}}
// синус
393
else
if
(mode==4){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=n;}
else
{wave[n]=(511-n);}}}
//треугол
394
else
if
(mode==5){
for
(uint16_t n=0; n<512; n++){ wave[n]=(n>>1);}}
//пила1
395
else
if
(mode==6){
for
(uint16_t n=0; n<512; n++){ wave[n]=((~n)>>1);}}
//пила2
396
else
if
(mode==7){
for
(uint16_t n=0; n<512; n++){
if
(n<256){ wave[n]=0;}
else
{wave[n]=255;}}}
//меандр
397
oldmode=mode; }
398
uint32_t dds_shag= (
double
)freq/10 * ((F_CPU==72E6)? dds_mpl_72 : dds_mpl_128) ;
// шаг= частота*коэффициент
399
asm volatile (
400
"mov R9, %[port];"
"\n\t"
// записать в r9 адресс порта "A"-ODR
401
"mov R8, %[wave];"
"\n\t"
//адресс массива положить в r8
402
"mov R7, %[shag];"
"\n\t"
// значение шага в r7
403
"dds_loop:"
"\n\t"
404
"add R6, r7;"
"\n\t"
//(1)добавить к аккумулятору шаг
405
"lsrs r2, r6, #23;"
"\n\t"
//(1) положить в R2 сдвинутый на 23 бита аккумулятор
406
"ldrb r2, [r8, r2];"
"\n\t"
//(2)загрузить в R2 выбранный байт из массива
407
"strb r2, [r9];"
"\n\t"
//(2) запиcать этот байт в PORTA-ODR
408
"ldr R2, [%[flag]];"
"\n\t"
//(2) подгрузить в R2 флаг
409
"cmp r2, 1;"
"\n\t"
//(1) сравнить
410
"bne dds_loop;"
"\n\t"
//(1) перейти в цикл
411
: : [wave]
"r"
(&wave),[shag]
"r"
(dds_shag),[port]
"r"
(&GPIOA_BASE->ODR),[flag]
"r"
(&mon_flag)
412
:
"r9"
,
"r8"
,
"r7"
,
"r6"
,
"r2"
413
);
414
GPIOA_BASE->CRL=0x44444444;
// все пины в Z
415
}
//END DDS set()
416
//
417
418
419
void
mytone(
int
frq,
int
ms ){
420
if
(mode==8){
return
;}
//таймер в режиме частотометра занят
421
uint16_t psc=1; uint32_t tim_arr;
422
// настройка генератора звука на таймере3
423
tim_arr = (F_CPU/2)/frq;
424
while
( (tim_arr/psc) > 65535) {psc++;}
425
__asm volatile(
"cpsid i"
);
426
TIMER2_BASE->SMCR=0;
427
TIMER3_BASE->CCR3=0;
//обнулить регистр соответсвующий используемому выходу
428
TIMER3_BASE->PSC=psc-1;
429
TIMER3_BASE->ARR=(tim_arr/psc)-1;
430
TIMER3_BASE->CCMR2=(1<<5)|(1<<4);
// OC3M:011
431
TIMER3_BASE->CCER=1<<8;
//cc3e подключить аппаратную ногу
432
TIMER3_BASE->SMCR=(1<<2)|(1<<0)|(1<<4);
//SMS:101 && TS:001 строб от 2-го таймера
433
TIMER3_BASE->EGR=1;
//перечитать регистры.
434
TIMER3_BASE->CR1=1;
435
/// настройка выдержки времени на таймере2
436
psc=1;
437
tim_arr = (F_CPU/1E3) * ms;
438
while
( (tim_arr/psc) > 65536) {psc++;}
439
TIMER2_BASE->CCMR2=0;
440
TIMER2_BASE->CR2=0;
441
TIMER2_BASE->CR1=(1<<3)|(1<<2);
//один импульс, без прероываний
442
TIMER2_BASE->CNT=0;
443
TIMER2_BASE->CR2=(1<<4);
//MMS:001 сигнал разрешения работы другим таймерам
444
TIMER2_BASE->PSC=psc-1;
445
TIMER2_BASE->ARR=(tim_arr/psc)-1;
446
TIMER2_BASE->EGR=1;
//перечитать регистры.
447
TIMER2_BASE->CR1|=(1<<0);
448
__asm volatile(
"cpsie i"
);
449
}
450
451
void
clock_gen(){
//функция работа с синтезатором si5351
452
if
(!si5351_found) {
return
;}
// на всякий случай
453
if
(freq <4E4){freq=4E4;}
if
(freq>200E7){ freq=200E7;}
//допустимые рамки частот
454
si5351.set_freq((freq/10)* SI5351_FREQ_MULT, SI5351_CLK0);
455
mon_flag=1;
456
}
457
458
void
comm(){
//коммутация выходов через мультиплексор
459
if
(mode==1 || mode==2 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,LOW);}
460
else
if
(mode>2 && mode<8 ) {digitalWrite(PB3,HIGH); digitalWrite(PB4,LOW);}
461
else
if
(mode==0 ) {digitalWrite(PB3,LOW); digitalWrite(PB4,HIGH);}
462
else
if
(mode==8) {digitalWrite(PB3,HIGH); digitalWrite(PB4,HIGH);}
463
}
PS обновлено 12.01.2019 в связи с тем, что у многих проблемы правильно вставить библиотеки, выкладываю полный архив моей arduino IDE со всеми библиотеками и с самим скетчем в примерах. Останется только запустить Arduino.exe и выбрать в примерах скетч. Архив в зипе. Размер архива 430МБ.
Получил сегодня Si5351 и решил замерить точность установки частоты. На частоте 36 МГц в режиме PWM отклонение около 800 Гц, а у Si5351 - немного более 3 кГц, а на 145 МГц - более 10 кГц. Мерил трансивером по нулевым биениям, предварительно проверив его по сигналам эталонных частот.
MAG-N, да похоже на правду. По крайней мере по части таймера МК, у меня тоже примерно такие цифры. Можно попробывать прописать свой дефайн F_CPU что б подогнать под истину, но этот вариант я не тестировал.
800 Гц на такой частоте - вполне нормально, а вот в разы большая погрешность у специально заточенной микросхемы - это не есть хорошо. Возможно, резонатор на платке "кривоватый", всё же китайская поделка.
#define F_CPU попробовал, но при этом изменяется частота на экране.
Поигрался с Si5351. Подцепил к Ардуино Нано и стал гонять примеры из библиотеки Si5351Arduino-master. Пример si5351_calibration натолкнул на некоторые мысли, в результате чего скетч генератора дополнился строкой (после стр. 45)
si5351.set_correction(80000, SI5351_PLL_INPUT_XO);
Ошибка убралась почти на "0", надо точнее подобрать величину коррекции. В примере всё это делается достаточно наглядно через монитор порта, потом только результат подставить. Всё это индивидуально для каждой платы, так что кому надо - может сделать.
Кстати, применённая микросхема позволяет задействовать для стабилизации частоты технологию GPSDO и, в частности одно из предложенных радиолюбителями решений выполнено Кареном Тадевосяном RA3APW )))
На диапазоне 10м в режиме GPSDO среднеквадратическое отклонение несущей частоты составляет примерно 7 миллиГц, а пиковое значение примерно 90 миллиГц.
Дима, что ты там говорил о любительских конструкциях )))
По-моему, для данного генератора это уже перебор, тем более что надо тащить кабель от подоконника до стола с генератором для антенны GPS. Врядли внутри помещения Глонасс-ГПС будет работать.