Всем привет. Подскажите простой(по возможности) скетч, с помощью которого можно генерировать синус фиксированной частоты 420Гц? Задача - сигнал оповещения о событий. Подключать буду через УНЧ к динамику.
Синус не выйдет на Ардуино. На ней нет аналоговых выходов - DAC. А вот прямоугольный сигнал можно функцией tone.
https://cxem.net/arduino/arduino16.php нашел такое. но ругается при компиляции на этот кусок кода Код (C++): PROGMEM prog_uchar sine256[] = { exit status 1 'prog_uchar' does not name a type
Это типа старый тип данных, и сейчас не используется. Можно заменить на char или byte. Или почитать вот это.
C помощью ШИМ выйдет на любом МК. Но в AVR нет DMA, поэтому МК только и будет занят генерацией синуса.
Или дак, или генератор взять, а проще автогенератор собрать, если нужно что-то одно. HMC830, например от 25 до 3000 МГц синусы выдает.
Тебя послушать, так нужны не только DMA, а ещё и шина PCI 16x, а то процессор все ресурсы потратит на генерацию синуса. Самый простой способ -- это выдать меандр частотой 420 Гц (правда не уверен, что можно получить именно такую частоту с точностью до Герца), а потом отфильтровать лишнее, думаю хватит RC-фильтра. Как советовал: либо самому с помощью одного из таймеров. Если хочется получить что-то близкое к синусу путём цифро-аналогового преобразования, то есть два способа: Первый с помощью PWM. Пример моей реализации синуса с частотой дискретизации 62,5 кГц в диапазоне частот 510 Гц - 6,5 кГц (но он не очень хороший: синус генерируется в оперативную память, которой и так мало): Спойлер: Генерация синуса Код (C++): /* * Speaker.c * * Created on: 18 апр. 2019 г. * Author: alexandr */ #include "Speaker.h" #include <avr/io.h> #include <avr/interrupt.h> #include <math.h> /* * Speaker connected to pin D3 (PD3). */ #define TIMER2_CLK F_CPU #define TIMER2_OVF_CLK (F_CPU / 256) static volatile uint8_t samplesCount, currentSample; static volatile uint8_t samples[128]; static volatile uint16_t lastFrequency = 0; static inline void fillSamples(uint16_t frequency) { uint8_t idx; double radStep; if (lastFrequency != frequency) { samplesCount = TIMER2_OVF_CLK / frequency; radStep = M_PI / samplesCount; for (idx = 0; idx < samplesCount; idx++) { samples[idx] = sin(radStep * idx) * 255; } lastFrequency = frequency; } currentSample = 0; } void speakerOn (uint16_t frequency) { if ((frequency > 510) && (frequency < 6500)) { DDRD |= (1 << PD3); // Speaker port as output PORTD &= ~(1 << PD3); // Turn off speaker. fillSamples(frequency); // Setup Timer2 for PWM on PD3 TCCR2A = _BV(COM2B1) // PD3 as PWM output | _BV(WGM21) // Fast PWM mode | _BV(WGM20); TCCR2B = _BV(CS20); // No prescaling -- T2clk = 16MHz OCR2A = 0; // Not used OCR2B = 0; // Initial value TIFR2 = 7; // Clean all interrupt flags TIMSK2 = _BV(OCIE2B); // Enable interrupt by comparison } } void speakerOff (void) { TCCR2A = 0; // Turn off prescaler -- stop Time2 TIFR2 = 7; // Clean all interrupt flags OCR2A = 0; OCR2B = 0; PORTD &= ~(1 << PD3); } void getSamplesTable(volatile uint8_t ** table, volatile uint8_t * size) { *table = samples; *size = samplesCount; } ISR(TIMER2_COMPB_vect) { OCR2B = samples[currentSample]; // The below code very expensive due the AVR don't have hardware divisor // currentSample = (currentSample + 1) % samplesCount; // The less expensive code: currentSample++; if (currentSample == samplesCount) { currentSample = 0; } } Если увеличить массив семплов (сейчас он 128 байт), то можно получить частоты ниже 510 Гц (а также нужно будет подправить проверку в функции 'speakerOn'). Второй способ -- генерация с помощью R2R преобразования. Правда это отнимет восемь ног у МК, но можно проблему решить, например, с помощью сдвигового регистра...
Если речь не об "поисупражняться" в програмлении, то обращение к аппаратным решениям - нормальная идея. Особенно когда решение стОит три копейки. Генератор с трехзвенной RC цепью - всё что вам надо. https://studfile.net/preview/5468513/page:2/ http://www.s-led.ru/81-shema-prostogo-rc-generatora.html