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

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

bayv пишет:

Ставил AD820, Кус = 1.11(300 Ом в "0", 33 Ом в обратную связь, +5 через переменник 15кОм в инверсный вход), Располовинить получилось нормально, но затухание на 1мГц более чем 10 раз. Если без него, по осцилографу 1.7В, с ним 0,08 В(p-p) и задирает вверх. Операционник граничная частота  по даташиту 1,8 мГц.

Если ОУ из китайщины - могут прислать перемаркированные LM358 или еще что похуже - частота единичного усиления (LM358) 700 кГц. Нарывался на такое.

Коммутатор - мои посты #57 #105

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Прошу помочь генератор V3.5 с ADG и SI  спаян на макетке . Работает все нормально кроме частотомера.

Подаю меандр 1Кгц 3.3в на выходе 0. О соплях- плата промыта от А15 все отпаяно (ADG) сигнал подаю прямо на А15.Перепрошивал эффект 0. Жду STM на V.3.6

Порт?

 

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

Сибиряк пишет:

 Прошу помочь генератор V3.5 с ADG и SI  спаян на макетке . Работает все нормально кроме частотомера.

Подаю меандр 1Кгц 3.3в на выходе 0. О соплях- плата промыта от А15 все отпаяно (ADG) сигнал подаю прямо на А15.Перепрошивал эффект 0. Жду STM на V.3.6

Порт?

 

Если прошивали через Arduino IDE , используя ST_Link, попрбуйте через USB, или наоборот, там есть некоторая разница.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Было прошито через USB сейчас прошил через ST_Link результат одинаков-0Гц.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

не испытывал необходимости в частотометре и даже не проверял его. сейчас попробовал подать со звукового выхода компа "до". 3.6, собирался как 3.5, с с коммутатором.

bayv
Offline
Зарегистрирован: 23.01.2021

Я так же хочу высказаться, тем более коллеге Сибиряк, который в самом начале очень помог мне. По моему, нет разницы, чем заливать РАБОЧУЮ прошивку. У меня нет ST Link, а есть HC430 с UART интерфейсом. Я заливал, и уже не однократно, им. Использовал софты: stm32 flash demo, stm32Cube programmer, шьется все одинаково. Это когда я выяснял, почему не запускается приборчик, после включения питания +5В, не всегда, правда, но частенько. С v3.5, этот эффект пропал. Я, конечно же, не читал все посты, но ковыряясь, я понял следующее: В STM32, рабочая прог-ма, располагается по адресу 8000000, а в Arduino, туда закачивается еще один загрузчик(меестный), там же происходит и инициализация stm32, в рабочей про-ге ее не видно. Потом  с помощью драйвера, инициализируется USB порт на stm32, там же определяется адрес, куда будет залита рабочая программа(адр. eeprom). Заливается рабочая прог-ма. После сброса или включения питания, stm32 передает управление меестному загрузчику(адр. 8000000), а он передает управление рабочей программе. Если я ошибаюсь, то пусть старшие товарищи меня поправят. Когда делаешь вслепую, "нажмите на А, дерните Б и будет С", всегда гложат какие то сомненья "а вдруг". О частотомере: на обеих версиях(v3.2, v3.5), частотомер работал сразу, и четко. проверял от 10Гц до 10мГц, выше проверить нечем. За точность, не скажу, вот сейчас меряю 10мГц, на индикаторе 9 999 476Гц. Может частотомер, может генератор врет все самоделки(игрушки). Вы посмотрите на ножке stm32, осц-ом, что там?.

 Уважаемый MAG-N, спасибо за ответ. Операционник я снял разделение +/-. реализовал на 2-х биполярных транзисторах разной проводимости. Но все равно нет четкой половины, она плывет от частоты, нужен другой подход. принцип реализации. Пока это оставил. Думаю, как реализовать коммутатор, что бы на все режимы работы был один вход/выход, и ЧИП 4052.

Посмотрел Ваши посты #57 #105.  Да это, пожалуй, самый простой и верный способ, только посмотрел коды на выводах порта В3, В4 в режиме вольтметра и частотомера - 1,1, в режиме ШИМ и одновибратора - 0, 0. Нужно придумать как их различать. Без дополнительного бита не получиться, наверное. Надо думать.

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

bayv пишет:

Посмотрел Ваши посты #57 #105.  Да это, пожалуй, самый простой и верный способ, только посмотрел коды на выводах порта В3, В4 в режиме вольтметра и частотомера - 1,1, в режиме ШИМ и одновибратора - 0, 0. Нужно придумать как их различать. Без дополнительного бита не получиться, наверное. Надо думать.

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

У контроллера есть свободные порты, можно какой-нибудь под третий бит задействовать. Почти в конце скетча есть функция

566   void comm(){ //коммутация выходов через мультиплексор

вот там эти нолики-единички и выставляются. 

bayv
Offline
Зарегистрирован: 23.01.2021

Да, Вы правы ШИМ и одновибратор с одного вывода. Остается различить только вольтметр и частотомер. Если задействовать дополнительный бит, как это у вас делается, нужно спрашивать разрешение у Автора (DiMax) об изменении исходника?. И еще как ведет себя ШТАТНЫЙ коммутатор, по даташиту у него очень маленькое сопротивление открытого канала, а это, как я понимаю, грозит большими проходными емкостями. Сильно он давит сигналы высоких частот?, какое затухание в диапазоне 1кГц - 1мГц?.

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

venus пишет:

не испытывал необходимости в частотометре и даже не проверял его. сейчас попробовал подать со звукового выхода компа "до". 3.6, собирался как 3.5, с с коммутатором.

Очень плохо: кто-то врет в малую терцию, либо комп, либо частотомер.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

andriano пишет:
Очень плохо: кто-то врет в малую терцию, либо комп, либо частотомер.

это не была иллюстрация точности, а лишь работоспособности частотометра. на компе вообще софт без претензий на что-либо, просто штатный speaker-test -c2 -t sine

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

bayv пишет:
Операционник я снял разделение +/-. реализовал на 2-х биполярных транзисторах разной проводимости. Но все равно нет четкой половины, она плывет от частоты, нужен другой подход. принцип реализации.

просто интересно, зачем эта "четкая половина"?

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

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

bayv пишет:

Да, Вы правы ШИМ и одновибратор с одного вывода. Остается различить только вольтметр и частотомер. Если задействовать дополнительный бит, как это у вас делается, нужно спрашивать разрешение у Автора (DiMax) об изменении исходника?. И еще как ведет себя ШТАТНЫЙ коммутатор, по даташиту у него очень маленькое сопротивление открытого канала, а это, как я понимаю, грозит большими проходными емкостями. Сильно он давит сигналы высоких частот?, какое затухание в диапазоне 1кГц - 1мГц?.

Если у Вас нет модуля SI5351, то можно попробовать свободный вход коммутатора использовать для вольтметра, немного поменяв функцию "//коммутация выходов через мультиплексор". Кстати,  в версии 3.5 там ошибочка имеется: вместо mode==9 (частотомер)  стоит mode==10 (вольтметр). В версии 3.6 все правильно.

По поводу изменения исходника - Автор давно добро дал.

Про коммутатор ничего не скажу - у меня реле стоят.

bayv
Offline
Зарегистрирован: 23.01.2021

Ответ MAG-N. Спасибо, строки нашел, как делать понятно. Попробую.

В моем скетче стоит (строка569) else if (mode==9), все правильно. Это уже мне предстоит вводить во все строки digitalWrite(PB12,Low); и писать еще одну else if (mode==10) {digitalWrite(PB3,Low); digitalWrite(PB4,Low; digitalWrite(PB12,High)}, и делать дешифратор на 3 входа. Порт B12, кажется, свободный.

bayv
Offline
Зарегистрирован: 23.01.2021

Ответ venus. Почему двуполярный синус? Ну я просто пытаюсь получить. Вы увлекаетесь Аудиотехникой? Я когда то то же. Делали мы в 70-е годы усилитель мощности Тигр (TIGER) из американского журнала конца 50 или начала 60. Он от входа до выхода не имел ни одного проходного конденсатора, полный комплементарный мост. Полоса 0Гц - 200кГц. Вход у него был прямым. Подал на вход +, на выходе +, подал -, на выходе -. даже если Вы поставили разделяющий конденсатор, и подали синус однополярный. Ваш конденсатор будет заряжаться от 0В до +3В( в нашем случае). Верхний выходной транзистор моста откроится, и ток потечет в нагрузку. потом синус пойдет на спад от +3В до 0В, и транзистор закроится, до тока покоя. Нижнее плечо моста не сработает никогда, ему нужен МИНУС. То же и операционники жвухполярные. Для меня сейчас это просто прихоть, дурь, если хотите. Усилитель я делать не буду. А вот попробовать из цифры сделать синус, да еще двухполярный, мне раньше не приходилось, вот и дурю. извините что не по теме, но как еще объяснить, эту приходь.

 

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

bayv пишет:

Ответ MAG-N. Спасибо, строки нашел, как делать понятно. Попробую.

В моем скетче стоит (строка569) else if (mode==9), все правильно. Это уже мне предстоит вводить во все строки digitalWrite(PB12,Low); и писать еще одну else if (mode==10) {digitalWrite(PB3,Low); digitalWrite(PB4,Low; digitalWrite(PB12,High)}, и делать дешифратор на 3 входа. Порт B12, кажется, свободный.

В скетче, который в тексте сообщения 97 под спойлером, все правильно, а в архиве 7zip (mode==10). А дешифратор - что-то вроде 74HC138 (3x8). Коммутатор ADG658 - 8-канальный.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

bayv пишет:
Ваш конденсатор будет заряжаться от 0В до +3В( в нашем случае). Верхний выходной транзистор моста откроится, и ток потечет в нагрузку. потом синус пойдет на спад от +3В до 0В, и транзистор закроится, до тока покоя. Нижнее плечо моста не сработает никогда, ему нужен МИНУС.

конденсатор "посадит" синус на ноль. на первой фотке синус до конденсатора, на второй - после, на третьей - меандр. ноль по центру, видно по левой метке и avg. у синуса есть положительная и отрицательная полуволна. у сигнала на второй фотке чего-то не хватает для полноценной работы усилителя? с меандром несколько сложнее, какая попало емкость может попортить его форму, но и надобность в меандре поменьше.

bayv
Offline
Зарегистрирован: 23.01.2021

 Уважаемый venus, я не хочу спорить с Вами по этому вопросу. Конденсатор можно было не ставить, а просто переключить режим осц-а с DC на AC, эффект тот же. Подумайте какое R входа у осц-а и кокой у усилителя или другого прибора который Вы захотите исследовать. C уважением bayv.

bayv
Offline
Зарегистрирован: 23.01.2021

Уважаемый MAG-N, я посмотрел даташит дешифратора 138, он вполне подходит для подключения реле. У меня где то валяются сдвоенные герконовые реле,(еще Советские) но они на 12В. Хотелось бы использовать мультиплексор. Попробую, не получиться, буду искать реле на 5В. Есть еще G6K-2F-Y. купил в Китае, думал поляризованные, о оказались полярные. Но их только 2 шт. В общем спасибо за помощь, буду ковыряться потихоньку.

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

bayv пишет:

Уважаемый MAG-N, я посмотрел даташит дешифратора 138, он вполне подходит для подключения реле. У меня где то валяются сдвоенные герконовые реле,(еще Советские) но они на 12В. Хотелось бы использовать мультиплексор. Попробую, не получиться, буду искать реле на 5В. Есть еще G6K-2F-Y. купил в Китае, думал поляризованные, о оказались полярные. Но их только 2 шт. В общем спасибо за помощь, буду ковыряться потихоньку.

Если применить SN74145 (К155ИД10), у которого ток выхода до 80 мА и допустимое напряжение на выходе 15 В - можно использовать 9-12 вольтовые реле с бОльшим током срабатывания.

bayv
Offline
Зарегистрирован: 23.01.2021

Дело в том, уважаемый MAG-N, сам генератор "кушает" более 90mA, конвертор +5/-5В, около 50mA,  дешифратор н 155 серии тоже потянет милиАмпер 15, реле, так же, получается не генератор, а простенький осциллограф. 

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

bayv пишет:

Дело в том, уважаемый MAG-N, сам генератор "кушает" более 90mA, конвертор +5/-5В, около 50mA,  дешифратор н 155 серии тоже потянет милиАмпер 15, реле, так же, получается не генератор, а простенький осциллограф. 

Это что за "конвертор +5/-5В", который 50 мА кушает? Ну будет генератор миллиампер 200 потреблять, это разве много? Мой генератор с 74138 и реле ест чуть меньше 130 мА. А если потребление экономить и четырех входов коммутатора не хватает, попробуйте восьмиканальный ADG658.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

bayv пишет:

 Уважаемый venus, я не хочу спорить с Вами по этому вопросу. Конденсатор можно было не ставить, а просто переключить режим осц-а с DC на AC, эффект тот же. Подумайте какое R входа у осц-а и кокой у усилителя или другого прибора который Вы захотите исследовать.

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

решения по установке двуполярных ОУ и преобразования питания в теме помнится были. не спорю, кому-то это может и актуально.

bayv
Offline
Зарегистрирован: 23.01.2021

По поводу конвертора. Он на 34063, и такой ток это еще мало, если делать по даташиту, (I вых = 500mA), там вообще за сотню. Это уж я индуктор ставлю большой индуктивности сейчас 470mkH. Мощность меньше, но и "шуму" и потребление меньше. А большой ток по выходу мне не нужен пока, 2 транзистора уровень нуля держат по 3,5 mA. и повторитель 4mA.

bayv
Offline
Зарегистрирован: 23.01.2021

Пред идущий пост стерся почему то. Я в нем хотел поблагодарить Вас за такой отличный мультиплексор. Посмотрел даташит, ADG658, гораздо лучше 4051. Вы вероятно на производстве с ними работаете, поэтому так быстро достаете документацию. Пойду на aliexpress. посмотрю что есть, и сколько стоит. Еще раз спасибо.

Офигеть, этот ЧИП на Aliexpress, стоит 666 руб. и бесплатная доставка. И только в одном магазине нашел. Вот почему он мне так понравился.

oldman
Offline
Зарегистрирован: 27.03.2020

MAG-N пишет:

... в версии 3.5 там ошибочка имеется: вместо mode==9 (частотомер)  стоит mode==10 (вольтметр). В версии 3.6 все правильно.

  Спасибо! Исправил, теперь и у меня частотомер работает.

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

bayv пишет:

Офигеть, этот ЧИП на Aliexpress, стоит 666 руб. и бесплатная доставка. И только в одном магазине нашел. Вот почему он мне так понравился.

Цена - прям число дьявола :-)

Плохо искали - есть и подешевле. Паять их хреновато, шаг 0.65.  А лучше всего 74HC138 и реле SIP-1A05 - 200 руб за пучок с доставкой.

https://aliexpress.ru/item/1005001836571592.html?spm=a2g0o.productlist.0.0.607b16adQU3Ct0&algo_pvid=6c76c2fc-f439-49f8-bba8-5f44c36c9108&algo_expid=6c76c2fc-f439-49f8-bba8-5f44c36c9108-2&btsid=0b8b15cb16122637753268990ed698&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_&sku_id=12000017800787125

Сибиряк
Offline
Зарегистрирован: 08.09.2018

Пришел STM 128 прошил 3.6.bin от Dimax один beep и черный экран. Отключаю от SI запускается и работает но тормоза жуткие кварца то нет.  Прошиваю через Аrduino Ide дает ошибку

C:\Arduino\hardware\Arduino_STM32\STM32F1\libraries\SPI\src\SPI.cpp:48:2: warning: #warning "Unexpected clock speed; SPI frequency calculation will be incorrect" [-Wcpp]

 #warning "Unexpected clock speed; SPI frequency calculation will be incorrect"
  ^
C:\Arduino\hardware\Arduino_STM32\STM32F1\libraries\SPI\src\SPI.cpp: In function 'spi_baud_rate _ZL19determine_baud_rateP7spi_devm.isra.8(rcc_clk_id, uint32_t)':
C:\Arduino\hardware\Arduino_STM32\STM32F1\libraries\SPI\src\SPI.cpp:806:15: warning: 'clock' may be used uninitialized in this function [-Wmaybe-uninitialized]
     clock /= 2;
               ^
конденсатор отпаян. земля от генератора есть.
Что то не так с библиотеками ?
venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

вроде было подобное. если память не изменяет, Adafruit BusIO откатить до 1.6.0. с 1.7.1 тоже вроде собиралось.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Venus Так ведь ошибки в SPI.

Если у Вас есть рабочий bin ver3.6 скиньте пожалуйста.

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

Сибиряк пишет:

 Venus Так ведь ошибки в SPI.

Adafruit BusIO.  This is a library for abstracting away UART, I2C and SPI interfacing

ShAlex13
Offline
Зарегистрирован: 19.01.2018

Сибиряк пишет:

 Venus Так ведь ошибки в SPI.

Если у Вас есть рабочий bin ver3.6 скиньте пожалуйста.

Заливал этот, все работает

https://cloud.mail.ru/public/foSS/Rk4v3Rgej

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

Упс

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

Сибиряк пишет:
Venus Так ведь ошибки в SPI.

Если у Вас есть рабочий bin ver3.6 скиньте пожалуйста.

в spi, да.

рабочие версии с разными дисплейми лежали тут: http://venus.rontel.ru/gen/

бинарники, в названии которых есть "venus" - с измененными выходами SI.

от декабря - с исправленным проскоком энкодера, я писал об этом.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Генератор 3.6 работает нормально. Все проверял до 50 Мгц больше нечем посмотреть. Не мог запустить процессор с Si, решилось просто до безобразия вход PD0 подал на контакт 1 (0) платы SI до этого подавал на CLC 0 вывод на вч разьем. Стартует без проблем.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

ну так и в схеме 3.6 на 3-й странице темы. clk0 - pd0, clk1 - выход.

если после версии 3.5 нет желания менять выводы, то по моей ссылке есть прошивки, в которых clk0 и clk1 поменяны местами.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

Я  и подавал сигнал c clk0 на pd0, но физически не с вч разьема а с пина 0 платы Si. Если подавал с контакта под вч разьем clk0 то процессор не запускал генератор Si и собственно не тактировался сам или сигнал был нестабильный ("грязный") как то так.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Генератор запускается надежно даже если убрать дополнительную землю

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

у меня pd0 сидит на clk, площадка под вч-разъем, соединение экранированным кабелем, так же как все выходные цепи. кабель тонкий, которым обычно разводятся wifi-антенны внутри корпусов.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

venus пишет:

у меня pd0 сидит на clk, площадка под вч-разъем, соединение экранированным кабелем, так же как все выходные цепи. кабель тонкий, которым обычно разводятся wifi-антенны внутри корпусов.

до этого делал так же но переключил кабель и все пошло.

Сибиряк
Offline
Зарегистрирован: 08.09.2018

Как то нужен был осциллограф аналог карманный купил набор для сборки dso 138 mini. Потом решил заказать к нему корпус в кармане таскать жалко экран. Пришел корпус с лишней передней панелью и угловыми стойками. Вот сейчас вспомнил. Вышло вот это:

 

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

То Сибиряк: В нижнем правом углу две восьмилапые "мелкосхемки" - это регулировка уровня и сдвига? Если да, можно ли схему получить?

 

Сибиряк
Offline
Зарегистрирован: 08.09.2018

 Схему на  регулировку и смещение взял с  поста 137 оу AD8552 какой был.  Отрицательное напряжение на Icl7660. Входное частотомера Ваше.

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

Опять возвращаюсь к вопросу как промодулировать выход ВЧ?

Никто не заморачивался?

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

man_200 пишет:

Опять возвращаюсь к вопросу как промодулировать выход ВЧ?

Никто не заморачивался?

У Si5351 есть нога питания выходных буферов. Можно АМ организовать. Но в последней версии генератора контроллер тактируется от этой Si5351, врядли ему хорошо будет от промодулированной тактовой. Как вариант, но опять с АМ - истоковый повторитель на двухзатворном полевике. На второй затвор напряжение модуляции.

bayv
Offline
Зарегистрирован: 23.01.2021

Попробовал несколько вариантов реализации вход/выход на один вывод мультиплексора. По моему, лучше реле ничего нет. Нарисовал схемку, поправил скейч, прошил. Попробовал на 561КТ3 в однополюсном варианте, реле еще не подошли. На большое входное сопротивление (осц-ф) работает нормально, выходное (внутренне) сопротивление великовато. Переделал двух полюсный вариант на AD8022.  Теперь свободен один операционник. Спасибо MAG-N, его идея.




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


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

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

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

///////////////////////////////////////////////////////////////////////////
/////////*********** ВЫВОД НА ДИСПЛЕЙ************//////////////////////////
///////////////////////////////////////////////////////////////////////////
void mon_out(){
char mybuf[15];
//************** Вывод первой строчки*****************************
  tft.setCursor(0, 0); //  вперёд, вниз
   tft.setTextColor(ST7735_GREEN, paper);
    tft.setTextSize(2);
    switch(mode){ 
       case 0: tft.print("  Clock Gen  ");  break;       
        case 1: tft.print("  PWM Mode   "); break;
          case 2: tft.print("  Duty Mode  "); break;
           case 3: tft.print(" Impuls Mode "); break;
           case 4: tft.print("  Sinus DDS  "); break;
            case 5: tft.print(" Triangle DDS"); break;
           case 6: tft.print("  Pila1 DDS  "); break;
          case 7: tft.print("  Pila2 DDS  "); break;
         case 8: tft.print("  Meandr DDS "); break;
       case 9: tft.print(" Freq. meter "); break;
      case 10: tft.print(" Volt. meter "); break;
       }
 //*****************Вывод второй строчки*****************************
tft.setTextColor(ST7735_WHITE, paper); 
tft.setCursor(0, 19); tft.setTextSize(3);
if (mode !=3) { // в одновибраторе не использовать стандартный вывод 
 if (freq>=1E8) {tft.print("         ");   tft.setTextSize(2);tft.setCursor(0, 21);}    
     if (freq<10) {sprintf(mybuf,"   0,%ld   ", freq );}                //9 -> 0,9
else if (freq<100){sprintf(mybuf,"   %ld,%ld   ", freq/10, freq%10 );}   //99 -> 9,9                
else if (freq<1E3){sprintf(mybuf,"   %ld,%ld  ", freq/10, freq%10 );}    //999 -> 99,9           
else if (freq<1E4){sprintf(mybuf,"  %ld,%ld  ", freq/10, freq%10 );}    //9999 -> 999,9             
else if (freq<1E5){sprintf(mybuf,"  %ld %03ld  ", freq/10000, (freq/10)%1000 );} //99999 -> 9.999               
else if (freq<1E6){sprintf(mybuf,"  %ld %03ld ", freq/10000, (freq/10)%1000 );}  //999999 -> 99.999                       
else if (freq<1E7){sprintf(mybuf," %ld %03ld ", freq/10000, (freq/10)%1000 );} //999999 -> 999.999                       
else if (freq<1E8){sprintf(mybuf,"%ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //9999999 -> 9.999.999                       
else              {sprintf(mybuf,"%3ld %03ld %03ld", freq/10000000, (freq%10000000)/10000, (freq%10000)/10  );} //99999999 -> 99.999.999                       
} //end if (mode !=3)
// вывод в режиме одновибратора
if (mode==3) { sprintf(mybuf,"   %3ld   ", freq/10 );  if (imp_mode_menu == 0) {tft.setTextColor(ST7735_BLACK,ST7735_WHITE);} }
tft.print(mybuf);    //вывод частоты
  //********************Вывод третьей строчки*****************************
               tft.setTextColor(ST7735_RED,paper); //красный цет строки для всех вариантов              
              
              if (mode==10){ //если вольтметр
                tft.setTextSize(3); //крупно
               tft.setCursor(50, 43);
                tft.print(" mV"); }              

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

               else if (mode==3) { //если одновибратор
                if (imp_mode_menu == 1) {tft.setTextColor(ST7735_RED,ST7735_WHITE);}
               tft.setTextSize(2); 
                tft.setCursor(50, 43);
                   switch (imp_mode) { 
                   case 0: tft.print("  mS  "); break;
                   case 1: tft.print("  uS  "); break;
                   case 2: tft.print(" Takt "); break;
                }}

              
               else if (mode <9) { //если генераторы
                tft.setTextSize(3); 
                tft.setCursor(50, 43);
               tft.print("Herz");  
                }

               
  //********************* "осциллограммы"******************************
     if (mode!=10){ tft.fillRect(5,90, 100,38,paper); }// зачистка пяточка (вправо, вниз, ширина вправо, длина вниз)
     tft.drawRect(0,67, 160,61,ST7735_MAGENTA);//рамка: вправо, вниз, ширина вправо, длина вниз
      if (mode==1 ||mode==2 || mode==9){
        tft.drawFastVLine(5, 90, 30, ST7735_CYAN); // восход фронта статическая вер линия
         tft.drawFastHLine(5, 91, (int)duty_out, ST7735_YELLOW);//длина единицы
          tft.drawFastHLine(5, 90, (int)duty_out, ST7735_YELLOW);//паралельная линия для выделения
           tft.drawFastVLine((int)duty_out+5, 91, 30, ST7735_YELLOW);// спад
            tft.drawFastVLine((int)duty_out+4, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
             tft.drawFastVLine(105, 90, 30, ST7735_YELLOW);//спад конец такта статическая вер. линия
              tft.drawFastVLine(104, 90, 30, ST7735_YELLOW);//паралельная линия для выделения
               tft.drawFastHLine((int)duty_out+5, 120, (100-(int)duty_out), ST7735_YELLOW);//линия единицы 2-го такта
                tft.drawFastHLine((int)duty_out+5, 119, (100-(int)duty_out), ST7735_YELLOW);//паралельная линия для выделения
                 }
                        if (mode==3){ //логотип одновибратора
                         tft.drawFastHLine(5,123,28,ST7735_YELLOW);// смещ вправо, смещ вниз, длина вправо                           
                          tft.drawFastHLine(5,122,26,ST7735_YELLOW);// паралельная линия для выделения                                                   
                           tft.drawFastVLine(32,73,50,ST7735_YELLOW); // смещ вправо, смещ вниз, высота вниз                           
                            tft.drawFastVLine(31,73,50,ST7735_YELLOW);// паралельная линия для выделения                             
                             tft.drawFastHLine(31,73,30,ST7735_YELLOW);// смещ вправо, смещ вниз, длина вправо 
                             tft.drawFastHLine(31,74,30,ST7735_YELLOW);                             
                               tft.drawFastVLine(61,73,50,ST7735_YELLOW);
                               tft.drawFastVLine(60,73,50,ST7735_YELLOW);                               
                                 tft.drawFastHLine(61,123,32,ST7735_YELLOW);
                                tft.drawFastHLine(61,122,32,ST7735_YELLOW);
                                }
                       if (mode==4){ // логотип синуса
                    for(uint8_t n=0; n<100; n++){tft.drawPixel(5+n, 73+ sine_logo[n],ST7735_YELLOW);
                    } //END  for
                 } // END if (mode==4)
                       else if (mode==5){// логотип треугольника                         
                      tft.drawLine(5,98,30,73,ST7735_YELLOW);
                     tft.drawLine(30,73,80,123,ST7735_YELLOW);
                    tft.drawLine(80,123,105,98,ST7735_YELLOW); 
                  } //END  mode==5
                         else if (mode==6){ //логотип пилы1
                           tft.drawLine(5,123,105,73,ST7735_YELLOW);
                           tft.drawFastVLine(105, 73, 50, ST7735_YELLOW);//спад конец такта статическая вер. линия
                           } //END  if (mode==6)
                            else if (mode==7){//логотип пилы2
                             tft.drawFastVLine(5, 73, 50, ST7735_YELLOW); // восход фронта статическая вер линия 
                             tft.drawLine(5,73,105,123,ST7735_YELLOW);
                              }// END  if (mode==7)
                                else if (mode==8 || mode==0){ //логотип меандра
                                  tft.drawFastVLine(5,73,25,ST7735_YELLOW);
                                  tft.drawFastHLine(5,73,50,ST7735_YELLOW);
                                  tft.drawFastVLine(55,73,50,ST7735_YELLOW);
                                  tft.drawFastHLine(55,123,50,ST7735_YELLOW);
                                  tft.drawFastVLine(105,98,25,ST7735_YELLOW);
                                }
                   //*********************** характеристики сигнала****************************************
                  tft.setCursor(5, 70); //  вперёд, вниз
                 tft.setTextColor(ST7735_WHITE, paper);
                tft.setTextSize(1);
                 if (mode==1 ||mode==2 || mode==9){
               tft.print("+Width="); if (t_hi<1E3) {tft.print(t_hi); tft.print(" uS  ");} else {tft.print(t_hi/1000); tft.print(" mS  ");}  
              tft.setCursor(5, 80); //  вперёд, вниз
            tft.print("-Width="); if (t_low<1E3) {tft.print(t_low); tft.print(" uS  ");} else {tft.print(t_low/1000); tft.print(" mS  ");}  
           tft.setCursor(114, 70); tft.print("Duty=");
          tft.setCursor(114, 80); tft.print(duty_out,0);tft.print(" %   ");
          } //END if (mode < 2 || mode==9)
   if (mode==10) { tft.print("Vcc=");tft.print(Vcc); tft.print(" mV   ");          }

  if (mode==9){ return;} // в режиме  частотометра выводить на экран больше ничего не нужно.
          
  /////////// установка курсора и вывод шага в разных режимах ///////////////   
              if (mode==3) { // в режиме одновибратора    
if (imp_mode_menu==2) { tft.setTextColor(ST7735_BLACK,ST7735_WHITE);}                     
                              tft.setCursor(114, 70); tft.print("Step=");  
                               tft.setCursor(114, 80);//курсор в поле  значения шага
                               }
                else if (mode<10) { tft.setCursor(114, 95); tft.print("Step="); tft.setCursor(114, 105);} 
                 else if (mode==10)  {tft.print("Step=");} //только в вольтметре
                
                   switch (encstep) { 
                    case 1: tft.print(" 0,1"); break;
                    case 10: tft.print("   1"); break;
                    case 100: tft.print("  10"); break;
                    case 1E3: tft.print(" 100");break;
                    case 1E4: tft.print(" 1E3");break;
                    case 1E5:  tft.print(" 1E4");break;
                    case 1E6:  tft.print(" 1E5");break;
                    case 1E7:  tft.print(" 1E6");break;
                    case 1E8:  tft.print(" 1E7");break;
                   }// END switch case           
   // вывод прочей информации
                 if (mode==3) {// вывод меню запуска импульса
                              if (imp_mode_menu==3) { tft.setTextColor(ST7735_RED,ST7735_WHITE );  }                                                                                                 
                                else { tft.setTextColor(ST7735_RED, paper );        }
                              tft.setTextSize(3);
                              tft.setCursor(100, 95);
                              tft.print("Run");
                              //tft.setTextColor(ST7735_WHITE, paper);
                              //tft.setTextSize(1); 
                            }


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

}//END mon_out




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


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

    case 9:   break; // в частотометре не реагировать на вращение энкодера 
   case 10:   modevolt?  Vin_low+=(encstep/10*in) : Vin_hi+=(encstep/10*in); //регулировка пределов вольтметра
   if (Vin_low<0||Vin_low>99999 ){Vin_low=0;} if (Vin_hi<0||Vin_hi >99999){Vin_hi=0;} break; //ограничения не менее ноля и не более 4х символов 
  default: freq+=(encstep*in); if (freq>DDSMAX) {freq=DDSMAX;}  //DDS режимы
 } //end switch case
 if (freq<0){freq=0;}   
mon_flag=1; 
}//end enc_mode


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

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



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

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


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

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

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

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

///////////////////// ФУНКЦИЯ ОДНОВИБРАТОРА///////////////////////
void impuls(){ 
uint32_t tim_arr=0, div_imp_mode=0; 
pinMode(PA7,PWM); pinMode(PA8,PWM);
__asm volatile( "cpsid i" );
 RCC_BASE->APB2RSTR |= 1<<11; //reset timer 1
  RCC_BASE->APB2RSTR&= ~(1<<11); 
   TIMER1_BASE->BDTR= 1<<14; //Automatic output enable
    TIMER1_BASE->CCMR1=(7<<4);//PWM2 Mode
     TIMER1_BASE->CCER=(1<<0)|(1<<2)|(pwm2_polar<<3);//cc1e/cc1ne enable 
      TIMER1_BASE->CCR1=1; // поднять ногу на этом такте
      TIMER1_BASE->CR1=(1<<3);//режим "один импульс"     
   if (imp_mode<2){ // кроме такта
  switch(imp_mode){// 0-мс, 1-мкс, 2 -такт
 case 0:  div_imp_mode=1000;  break; // делитель для миллисекунд
case 1:  div_imp_mode=1000000;  break;// делитель для микросекунд
 }
 tim_arr =  (freq/10) * (uint32_t)(F_CPU/div_imp_mode) ; 
  divider=1; 
   while ( (tim_arr/divider) > 65535) {divider++;} 
    TIMER1_BASE->PSC=divider-1; // Прескалер
     TIMER1_BASE->ARR=(tim_arr/divider)-1; //длина импульса
      TIMER1_BASE->CCR4= TIMER1_BASE->ARR;//повесить флаг по окончании импульса
      }
      else {  TIMER1_BASE->ARR = (freq/10);} //если imp_mode == такт,
      TIMER1_BASE->EGR=1; 
      mytone(2200,100);
     TIMER1_BASE->CR1|=(1<<0);
  while( (TIMER1_BASE->SR&(1<<4))==0 )   ;//ждать подождать пока закончится импульс
  __asm volatile( "cpsie i" );
}

Файл: Gen_3_5_1. ino, тоже сформировался, но как и куда его прикрепить на обозрение, не знаю 

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

"По моему, лучше реле ничего нет..... Попробовал на 561КТ3" -

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

"Спасибо MAG-N, его идея." Ну и за спасибо - спасибо :-)

mihlit
Offline
Зарегистрирован: 16.05.2020

Всем доброго времени суток! Очень понравился мне проект, пробую повторить, да только затык случился. Нет у меня (и не предвидится) дисплея на ST7735, есть на SPFD54124B. Он почти такой же, только обмен по SPI 9-битный, т.е. первый бит команда/данные. Нашел в инете его библиотеку для Ардуино, только вот прикрутить ее вместо ST7735 не получается. Вопрос такой: насколько реально для новичка в Ардуино переделать скетч для работы с SPFD54124B? Я с STM32 имел дело, из языков Си (ну и родной матерный).

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

Собственно вывод информации в скетчах генератора не разбросан по всему тексту программы, а находится в "одной кучке", именуемой ВЫВОД НА ДИСПЛЕЙ, что очень хорошо и удобно. Соответственно, устанавливаете нужную библиотеку, ну и далее с помощью Си и родного матерного правите эту кучку. Для начала запустите какой-нибудь пример из библиотеки.

mihlit
Offline
Зарегистрирован: 16.05.2020

Спасибо за быстрый ответ. Еще вопрос - пытался писАть  простенькие скетчи и выяснил, что в IDE нет режима отладки. И как отлаживать? В AVR Studio есть симулятор, в Keil через ST-Link отладка, а тут как? Меняешь строку в скетче и, если компиляция пройдет успешно, заливаешь скетч и видишь - что видишь?

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

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