Синус 10кГц

Protas
Offline
Зарегистрирован: 18.03.2018

Здравствуйте. Подскажите пожалуйста, можно ли программно решить такую задачку на ардуино уно:

1. Синус 10 кГц

2. ШИМ с регулируемой скважностью (переменник) 500 Гц

3. Снятие синуса с внешней обмотки и сравнение с синусом генерируемым ардуинкой.

Зарание всем спасибо)

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

Protas, первые два однозначно можно, и примеры на форуме есть. (3) -это уже задача для осциллографа. В любом случае одновременно задачи (1) и (3) вряд ли могут сосуществовать на одном МК.

Protas
Offline
Зарегистрирован: 18.03.2018

Да, форум я смотрел. Не пойму просто как частоты в 10 кГц достичь

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

Protas, Ардуино - это не конкретный контроллер, а целое семейство.

На данном форуме под Ардуино зачастую подразумевается семейство AVR. Строго говоря, аналоговые сигналы это семейство не способно генерить в принципе, поэтому задача 1 невыполнима.

Хотя, при наличии некоторого внешнего обвеса можно получить результат достаточно длизкий к желаемому. Например, генерить Ардуиной меандр 10 кГц, а в синус превращаать его узкополосным фильтром. И, кстати, для такой реализации увтерждение уважаемого dimax о невозможности совмещения 1 и 3 перестает быть истинным.

Что касается ввода аналоговых сигналов, то для указанного семейства максимальная частота дискретизации по умолчанию - 9 кГц. Если мы хотим уверенно отличать синус от прямоугольника, оцифровка должна ловить, минимум, 5-7 гармоник, для чего частота дискретизации должна быть не ниже 120-150 кГц.

Думаю, что единственный контроллер из семейства Ардуино, который способен выполнять задачи типа тех, что приведены Вами - это Arduino Due.

Protas
Offline
Зарегистрирован: 18.03.2018

Спасибо. Уже заказал

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Вот я причесал старый DDS для удобного использования.

Он на Bluepill STM32. Эта плата, в отличии от Due, стоит 100 р, а не 1000.

Замечу, что loop() - пустой и в нем можно делать, что захочется ;).

ДДС построен на ДМА и Таймере. ЦАП сделан за счет ШИМ и RC фильтра на выходе.

В примере использован RC фильтр из 180 Ом и 10 нФ.

Вот поясняющее видео на youtube.

Ниже - код. Он самодокументирован. Если нужны пояснения - пишите тут, я когда-нибудь отвечу ;). Если кто-то считает это правильным - можно перенести в проекты, Хоть это и смешно.

#include <libmaple/dma.h>

//Задаем ФУНКЦИЮ DDS! Функция определена на  0..1 и приниммает значениея от 0 до 1! 
//ЗАДЕМ ЕЁ !!!!!!В_Н_И_М_А_Т_Е_Л_Ь_Н_О!!!!!!!
//#define F(x)   (0.5*(1+sin(2*3.1415926*x))) //это пример для синуса
#define F(x)  ( (x<0.5)?(x):(0.5-(x-0.5)) )   //Это пример для пилы
//#define F(x)  ( (x<0.7)?(x):(0.7-(x-0.7)*7/3) ) //Несимметричная пила


#define Tperiod     100 //Задаем период в мкс
#define SAMPLES     100 //Задаем число точек в периоде
#define Prec        (72*Tperiod/SAMPLES-1)   //Тут вычисляем точность вычислений функции в точках по вертикальной оси
// 72МГц - частота таймера. Остальное понятно, на высоких частотах нужно брать  меньше самплов
#define   dt  (1.0/SAMPLES) // Дельта т - шаг по времени


dma_tube_config dma_cfg, dma_cfg2;

int flag1 = 0;  // это просто ни для чего - счетчик числа периодов

int out1 = PB7; 
//номер вывода DDS СТМ32 - штука не простая, к выводам привязаны таймер и канал, к ним и свой ДМА привязан
//если нет знаний о СТМ32, то сюда не нужно лезть
          

int val1[SAMPLES]; // массив значений DDS 

float amp = 1.0; // амплитуда сигнала, глобальна потому, что была когда-то регулировка кнопками, ее можно добавить

//уже служебные вещи, не нужно лезть, если нет знаний, тут определяем таймер и канал
timer_dev *dev1 = PIN_MAP[out1].timer_device;
uint8 cc_channel1 = PIN_MAP[out1].timer_channel;

void fun()
{
//тут можно что-то делать забавное, каждый раз при окончании периода DDS
    flag1++;
}


void timer_conf()
{
  
  timer_dma_set_base_addr(dev1, TIMER_DMA_BASE_CCR2);
  timer_dma_set_burst_len(dev1, 1);
  timer_dma_enable_req(dev1, cc_channel1);
  timer_set_reload(dev1, Prec);
  timer_set_prescaler(dev1, 0);
}

void dma_conf()
{
    dma_init(DMA1);
    /* T4C2 DMA C4 */
    dma_cfg.tube_dst = &(dev1->regs.gen->DMAR);
    dma_cfg.tube_dst_size = DMA_SIZE_32BITS;
    dma_cfg.tube_src = val1;
    dma_cfg.tube_src_size = DMA_SIZE_32BITS;
    
    dma_cfg.tube_nr_xfers = SAMPLES;
    dma_cfg.tube_flags = DMA_CFG_SRC_INC | DMA_CFG_CIRC | DMA_CFG_CMPLT_IE;
    
    dma_cfg.tube_req_src = DMA_REQ_SRC_TIM4_CH2;
    dma_cfg.target_data = 0;
    
    dma_tube_cfg(DMA1, DMA_CH4, &dma_cfg);
}

void dma_start()
{
  dma_attach_interrupt(DMA1, DMA_CH4, fun);
    
  dma_enable(DMA1, DMA_CH4);
  timer_resume(dev1);
  
}

void init_wave()
{
  float maxy=0;
  float y;
  int i;
  for(i=0;i<SAMPLES;i++) if ((y = F(dt * i)) > maxy) maxy=y;
  for(i=0;i<SAMPLES;i++) val1[i] = 1+amp*((Prec-1)/maxy)*F(dt * i);
     
}


void setup() {
  int i;
  pinMode(out1, PWM);
  
//  Serial.begin(57600);
// ну это если управление по сериалу писать
// можно даже интерпретатор сделать, чтобы функцию писать и разбирать ее "на лету"... если заняться больше нечем ;)

  timer_conf();
  dma_conf();
  dma_start();
  
  init_wave();
}

void loop() {
  // тут можно с сериала принимать параметры DDS, если есть желание и сами напишем ;) мне- не надо
}

 

Logik
Онлайн
Зарегистрирован: 05.08.2014

Делал я генерацию синусов, причем сразу двух для DTMF, и ввод аналогового сигнала (собственно этого самого DTMF).  Все это в прерывании таймера 1 на 62,5КГц. На нем же и ШИМ. Может можна и больше частоту, насколько помню выбиралась по возможностям ШИМа, его период равен 256 тактов и он 8-битный. Синус конечно таблично генерится, не как у wdrakula. Походу введеный сигнал еще на экран выводится, гдето на форуме даже ссылка на видео работы есть.

Добавлю еще функцию табличного синуса по четверти периода. А то есть любители целый период хранить.

signed char TabSin(unsigned char x)
{
const byte TabSin[64]={0, 3,  6,  9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49,
                       51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, 90,
                       92, 94, 96, 98,100,102,104,106,107,109,111,112,113,115,116,117,
                      118,120,121,122,122,123,124,125,125,126,126,126,127,127,127};

signed char r=TabSin[((x&0x40)?-x-1:x)&0x3f];
 if(x&0x80)
   return -r;
 return r;
 
  
}

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

 Синус конечно таблично генерится, не как у wdrakula.

Стесняюсь спросить: а у меня как?

Я и строю таблицу при запуске в init_wave(). Строю для сигнала любой формы. Там первый for ишет максимум, а второй строит таблицу.

Если в контроллере дохрена памяти, то таблицу можно и нужно строить прямо в нем, нет?

Я люблю эффективное программирование, люблю "упихаить" а тиньку13 сложный код, но (!) для практической, не тренировочной, задачи - предпочту (в случае нехватки ресурсов) взять контроллер "потолще" ;).

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

А я обычно строю таблицы на ПК и объявляю const PEROGMEM. На практике более половины используемого объема flash уходит именно на таблицы.

Protas
Offline
Зарегистрирован: 18.03.2018

Купил Due. Залил скетч, найденый в инте...

#define maxSamplesNum 120

static int sinwave[maxSamplesNum] = {
  0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1,
  0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4,
  0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc,
  0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f,
  0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a,
  0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a,
  0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d,
  0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a,
  0x112, 0xdf, 0xb1, 0x87, 0x64, 0x45, 0x2c, 0x19, 0xb, 0x2,
  0x0, 0x2, 0xb, 0x19, 0x2c, 0x45, 0x64, 0x87, 0xb1, 0xdf,
  0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4,
  0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794
};

int i = 0;

void setup() {
  analogWriteResolution(12);
}

void loop() {
  i++;
  if(i == maxSamplesNum)
    i = 0;
    
  analogWrite(DAC1, sinwave[i]);
}

На цифровом осцилографе показывает синус, но частота постоянно скачет. Примерно 8кГц - 20 кГц. Подскажите плз, как стабилизировать частоту на 10 кГц.

Вот ссылка на автора http://arduino-er.blogspot.com.by/2014/12/generate-sin-wave-on-arduino-due.html

P.S. огромное спасибо всем кто откликнулся!

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

Protas, это пожалуй самый плохой генератор синуса из всех, что существуют =) Посмотрите в этой ветке программу от Волшебника. Там к слову есть и мой вариант для дуе, который даёт максимально качественный синус, но он занимает 100% вычислительной мощности, и делать что-то ещё во время генерации синуса невозможно. Вариант Волшебника с точностью до наоборот. Синус не выского качества, но вычислительная мощность почти вся свободна.

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

Logik пишет:

Все это в прерывании таймера 1 на 62,5КГц. На нем же и ШИМ.

А в примере wdrakula и прерывания не понадобились... и луп пустой... Прикинь!!!??? )))))))))

Цитата:

Может можна и больше частоту, насколько помню выбиралась по возможностям ШИМа, его период равен 256 тактов и он 8-битный.

Убогость налицо...

Цитата:

Синус конечно таблично генерится, не как у wdrakula.

А у него и таблично, и хардварно... )))))))))

Цитата:

Добавлю еще функцию табличного синуса по четверти периода. А то есть любители целый период хранить.

Нашёл чем удивить... И дельта Т меняется??? ))))))))

Protas
Offline
Зарегистрирован: 18.03.2018

dimax, спасибо. Буду пробовать скетч от Волшебника!

gaalx
Offline
Зарегистрирован: 07.02.2018
 
Доброе время года. Люди подскажите где почитать про это. (пост 5)
 
030 //уже служебные вещи, не нужно лезть, если нет знаний, тут определяем таймер и канал
031 timer_dev *dev1 = PIN_MAP[out1].timer_device;
032 uint8 cc_channel1 = PIN_MAP[out1].timer_channel

А то не лез  а уже заблудился. В МАПЛЕ не нашёл или не увидел.может зрение конечно не то.

 

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Если от корня Arduino_STM32, то вот тут:

./STM32F1/variants/generic_stm32f103c/board.cpp
 


// Note. See the enum of pin names in board.h

extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = {


    {&gpioa, &timer2, &adc1,  0, 1,    0}, /* PA0 */
    {&gpioa, &timer2, &adc1,  1, 2,    1}, /* PA1 */
    {&gpioa, &timer2, &adc1,  2, 3,    2}, /* PA2 */    
    {&gpioa, &timer2, &adc1,  3, 4,    3}, /* PA3 */
    {&gpioa,   NULL, &adc1,  4, 0,    4}, /* PA4 */
    {&gpioa,   NULL, &adc1,  5, 0,    5}, /* PA5 */
    {&gpioa, &timer3, &adc1,  6, 1,    6}, /* PA6 */
    {&gpioa, &timer3, &adc1,  7, 2,    7}, /* PA7 */
    {&gpioa, &timer1, NULL,  8, 1, ADCx}, /* PA8 */    
    {&gpioa, &timer1, NULL,  9, 2, ADCx}, /* PA9 */    
    {&gpioa, &timer1, NULL, 10, 3, ADCx}, /* PA10 */
    {&gpioa, &timer1, NULL, 11, 4, ADCx}, /* PA11 */
    {&gpioa,   NULL, NULL, 12, 0, ADCx}, /* PA12 */    
    {&gpioa,   NULL, NULL, 13, 0, ADCx}, /* PA13 */    
    {&gpioa,   NULL, NULL, 14, 0, ADCx}, /* PA14 */
    {&gpioa,   NULL, NULL, 15, 0, ADCx}, /* PA15 */
    
    {&gpiob, &timer3, &adc1,  0, 3,    8}, /* PB0 */    
    {&gpiob, &timer3, &adc1,  1, 4,    9}, /* PB1 */
    {&gpiob,   NULL, NULL,  2, 0, ADCx}, /* PB2 */
    {&gpiob,   NULL, NULL,  3, 0, ADCx}, /* PB3 */
    {&gpiob,   NULL, NULL,  4, 0, ADCx}, /* PB4 */
    {&gpiob,   NULL, NULL,  5, 0, ADCx}, /* PB5 */
    {&gpiob, &timer4, NULL,  6, 1, ADCx}, /* PB6 */
    {&gpiob, &timer4, NULL,  7, 2, ADCx}, /* PB7 */    
    {&gpiob, &timer4, NULL,  8, 3, ADCx}, /* PB8 */    
    {&gpiob, &timer4, NULL,  9, 4, ADCx}, /* PB9 */    
    {&gpiob,   NULL, NULL, 10, 0, ADCx}, /* PB10 */    
    {&gpiob,   NULL, NULL, 11, 0, ADCx}, /* PB11 */
    {&gpiob,   NULL, NULL, 12, 0, ADCx}, /* PB12 */
    {&gpiob,   NULL, NULL, 13, 0, ADCx}, /* PB13 */
    {&gpiob,   NULL, NULL, 14, 0, ADCx}, /* PB14 */
    {&gpiob,   NULL, NULL, 15, 0, ADCx}, /* PB15 */

    {&gpioc,   NULL, NULL, 13, 0, ADCx}, /* PC13 */    
    {&gpioc,   NULL, NULL, 14, 0, ADCx}, /* PC14 */
    {&gpioc,   NULL, NULL, 15, 0, ADCx}, /* PC15 */



};

А зачет это тебе?

Если задал такой вопрос, значит не понимаешь в теме. Если не понимаешь - ничего не сумеешь изменить.

И конечно, такой файл есть и в мапл и в мапл-мини. Это часть пакета ардуино-стм32, то есть программирования плат СТМ в среде ардуино.

gaalx
Offline
Зарегистрирован: 07.02.2018

wdrakula.

Спасибо. А что всё так безнадёжно? :)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Пока я слежу за темой, можно попросить меня пояснить или изменить что либо. К сожалению мне лень было доделывать код так, что бы все параметры и таймера и канала дма сами опредеоялись по выходному пину.
Так что, если поменяешь пин, придется менять во могих местах руками. Это собственно все, что тут нужно знать про СТМ.

gaalx
Offline
Зарегистрирован: 07.02.2018

wdrakula. Буду весьма признателен. Поясните пожалуйста. PB7 это вроде 2 канал. тогда что значит 32 строка. так же непонятно назначение 17ст. и 55-66 в частности (dma_cfg.tube_). также инициализация таймеров например в  http://arduino.ru/forum/programmirovanie/stm32-preryvanie-po-sravneniyu-v-taimere  выглядит иначе.ещё не понятно с чего начинать если понадобится допусти режим шим захвата.честно говоря думал что с ардуино проще чем к примеру с кейлом или иаром.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Нужно уточнить, что я обещал пояснения по коду, а не лекции по программированию СТМ32 в среде Ардуино.

Итак:

1. в строке з2 определячется канал таймера по пину. Ссылку я уже давал, изучай STM32duino.

2. в 17 строке декларация переменной dma_cfg. вторая (dma_cfg2) - не нужна, наследство от версии для двух каналов.

3. 55-66 - конфигурация DMA  канала, Таймер 4 канал 2 соответствуют 4ому каналу DMA.

ОЧЕНЬ ПОДРОБНО ИЗУЧАЕМ  rm0008  от ST! Он ищется в яндексе просто первым в выдаче по rm0008 ;).

Там написано абсолютно все про контроллер и его настройки.

4. там же про таймеры. Замечу, что прерывания таймера в этом коде не используются совсем, используется DMA.

-------------------------------------

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

Учитесь пользоваться гуглом, а не форумом.

gaalx
Offline
Зарегистрирован: 07.02.2018

wdrakula. Спасибо.