Генерация синуса

Тема в разделе "Arduino & Shields", создана пользователем Batman, 19 июл 2020.

  1. Batman

    Batman Нуб

    Всем привет.
    Подскажите простой(по возможности) скетч, с помощью которого можно генерировать синус фиксированной частоты 420Гц?

    Задача - сигнал оповещения о событий. Подключать буду через УНЧ к динамику.
     
  2. Un_ka

    Un_ka Гуру

    Синус не выйдет на Ардуино. На ней нет аналоговых выходов - DAC.
    А вот прямоугольный сигнал можно функцией tone.
     
  3. Batman

    Batman Нуб

    А как же речь синтезируют?
    Шим + ФНЧ . нет?
     
  4. Batman

    Batman Нуб

    https://cxem.net/arduino/arduino16.php

    нашел такое.
    но ругается при компиляции на этот кусок кода
    Код (C++):
    PROGMEM  prog_uchar sine256[]  = {
    exit status 1
    'prog_uchar' does not name a type
     
  5. Un_ka

    Un_ka Гуру

    Это типа старый тип данных, и сейчас не используется.
    Можно заменить на char или byte.
    Или почитать вот это.
     
  6. parovoZZ

    parovoZZ Гуру

    C помощью ШИМ выйдет на любом МК. Но в AVR нет DMA, поэтому МК только и будет занят генерацией синуса.
     
  7. Batman

    Batman Нуб

    Получается для того чтобы воспроизводить синус и выполнять ещё задачи, как минимум нужен stm32?
     
  8. parovoZZ

    parovoZZ Гуру

    Нужен DMA. Если сигнал вообще не детерминирован, то лучше смотреть в сторону DSP и прочей кухни.
     
  9. Daniil

    Daniil Гуру

    Или дак, или генератор взять, а проще автогенератор собрать, если нужно что-то одно. HMC830, например от 25 до 3000 МГц синусы выдает.
     
  10. parovoZZ

    parovoZZ Гуру

    DAC сам по себе синус не выдаст. Необходимо постоянно обновлять его с частотой квантования.
     
  11. AlexU

    AlexU Гуру

    Тебя послушать, так нужны не только 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 преобразования. Правда это отнимет восемь ног у МК, но можно проблему решить, например, с помощью сдвигового регистра...
     
  12. Ariadna-on-Line

    Ariadna-on-Line Гуру

    Последнее редактирование: 20 июл 2020