Перейти к содержанию
    

Реализация DDS на LPC2388 (ARM7)

Мое приветствие вам всем!

Нужно мне вот в учебных целях реализовать генератор синуса,пилы,треугольника на LPC2388 (ARM7). Пишу на С в Keil.

На борту LPC2388 есть ЦАП с временем установления сигнала 1мкс. Таким образом, максимальная частота изменения выходного напряжения с ЦАП составляет 1 МГц ?

 

Микроконтроллер работает на частоте 72 МГц.

Таймер0 работает на частоте 72 МГц.

 

Таблица синуса состоит из 64 значений.

 

Аккумулятор фазы - 64 бита - unsigned long long

 

Моя реализация DDS не позволяет поднять частоту выходного синуса выше 10кГц. Мне кажется это слишком мало!!!

 

Использую IRQ прерывание от таймера, а оно еще забирает 25 тактов. Сдвиг фазы вычисляю в main(), а в прерывании от таймера только загружаю в регистр ЦАПа значение.

 

Скажите пожалуйста, нормально ли то что граничная частота синуса у меня 10кГц ?

Возможно ли поднять до больших значений, не переходя на АСМ. Посмотрел проекты в интернете, так там на Атмеге получают частоты в 45 кГц!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На борту LPC2388 есть ЦАП с временем установления сигнала 1мкс. Таким образом, максимальная частота изменения выходного напряжения с ЦАП составляет 1 МГц ?

Нет. 500 кГц. Как бы очевидно: V1, V2, V1, V2... каждую микросекунду дадут сигнал с периодом 2 мкс.

 

Скажите пожалуйста, нормально ли то что граничная частота синуса у меня 10кГц ?

Возможно ли поднять до больших значений, не переходя на АСМ. Посмотрел проекты в интернете, так там на Атмеге получают частоты в 45 кГц!

Надо посмотреть на DMA. Для максимальной скорости данные для DAC следует заготавливать заранее и заряжать DMA для скармливания этих данных DAC. Если DMA это позволяет, то при некоторой сноровке ассемблер не понадобится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ерунда какая-то. Я на MSP430 с тактовой чуть более 7МГц с помощью PWM получал 3-х фазный синус с дискретизацией 33 значения частотой до 1кГц. Аккумулятор фазы был 16-и разрядным. При перестройке частоты или амплитуды таблицу синуса формировал в фоновом режиме в буфере ОЗУ. В прерывании кроме расчета фазы успевал еще и проанализировать запрет/разрешение генерации PWM. Вам же наверное DMA нужно использовать. Так будет эффективнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если я правильно понял, то проблема в том что прерывание выполняется слишком долго?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если я правильно понял, то проблема в том что прерывание выполняется слишком долго?

Вашу программу никто не видел, а по приведённому описанию ответить на этот вопрос трудно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот собственно код :biggrin:

 

 

#include "LPC23XX.h"                        


int sinus[64]={512,562,613,662,710,756,800,840,878,
911,941,967,988,1004,1015,1022,1023,1019,1010,996,978,
954,927,895,859,820,778,733,686,638,588,537,486,435,385,
337,290,245,203,164,128,96,69,45,27,13,4,0,1,8,19,35,56,
82,112,145,183,223,267,313,361,410,461,511};


  const int array_size=sizeof(sinus)/sizeof(int);

  void GPIOResetInit(void);

  unsigned long long phase=0;

  unsigned char index=0;
  unsigned char flag0=1;
  
  unsigned int amplituda=1024;
  unsigned int dac=0; 
  unsigned int tmp2=0;


  /* Timer0 IRQ: Executed periodically                                          */
__irq void Timer0ISR (void) {
   
   DACR=dac;
   
   flag0=1;

   T0IR = 0x01; //sbros flaga prerivaniya.Avtomaticheski ne rabotaet 
      
   VICVectAddr = 0; /* Acknowledge Interrupt              */                     
   return;
}


/******************************************************************************
**   Main Function  main()
******************************************************************************/
int main (void)
{
     GPIOResetInit();
    
      VICIntSelect = 0x00;      // all interrupts are IRQs
     VICIntEnable = 0x10;      // Channel#4 is the Timer0
       VICVectAddr4=(unsigned)Timer0ISR;
    
     FIO0DIR=1<<26;
     PINSEL1 =1<<21;  //vkluchaem DAC
     
     //timer initialization
     T0IR=0;
     T0CTCR=0;
     T0TC=0; //shchetniy register
     T0PC=0;
     T0PR=0;
     T0MR0=1200; // register sravnenia tipa OCR
                         
     T0MCR=3; 
     T0TCR=1; 


    while(1)
    {
        if(flag0==1)
        {
            tmp2=sinus[phase>>58];
            dac=(tmp2*amplituda)/1024;
            dac=dac<<6;
            phase+=1431655765;
            flag0=0;
        }
    }

  return 0;
}



void GPIOResetInit( void )
{
  /* Reset all GPIO pins to default: primary function */
  PINSEL0 = 0x00000000;
  PINSEL1 = 0x00000000;
  PINSEL2 = 0x00000000;
  PINSEL3 = 0x00000000;
  PINSEL4 = 0x00000000;
  PINSEL5 = 0x00000000;
  PINSEL6 = 0x00000000;
  PINSEL7 = 0x00000000;
  PINSEL8 = 0x00000000;
  PINSEL9 = 0x00000000;
  PINSEL10 = 0x00000000;

  SCS |= 0x01;
    
  IODIR0 = 0x00000000;
  IODIR1 = 0x20000000;
  IOSET0 = 0x00000000;
  IOSET1 = 0x00000000;
    
  FIO0DIR = 0x0000000f;
  FIO1DIR = 0x20000000;
  FIO2DIR = 0x0000000f;
  FIO3DIR = 0x00000000;
  FIO4DIR = 0x00000000;
    
  FIO0SET = 0x00000000;
  FIO1SET = 0x00000000;
  FIO2SET = 0x00000000;
  FIO3SET = 0x00000000;
  FIO4SET = 0x00000000;  
  return;     
};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это вообще работоспособно? Разве что при отключенной оптимизации.

Почитайте про квалификатор volatile - переменные flag0 и dac нужно объявлять с его использованием.

Зачем Вам аккумулятор фазы в 64 бита? Хочется иметь нижний предел установки частоты в микрогерцы? 32 бит ИМХО достаточно.

Непонятно какой частотой тактируется таймер (PCLK = ?), но у Вас минимум 1200 тактов CPU между прерываниями. Прерывания у АРМ7 может и не особо быстрые, но не 1200 же тактов. Раз в 10 можно и поднять. И перенесите всю обработку прямо в прерывание.

Вместо:

tmp2=sinus[phase>>58];
dac=(tmp2*amplituda)/1024;
dac=dac<<6;

Будет

DACR = ((sinus[phaze>>26]*amplituda)>>4) & 0x0000FFFC; // аккумулятор фазы в 32 бита
phaze += delta; // delta - приращение фазы, глобальная переменная
// phaze, amplituda, delta неплохо в структуру собрать.
// ну и дальше как было, только flag0 уже не нужен.

Еще можно прерывание как FIQ сконфигурировать - еще несколько тактов отыграете.

Но вообще с DMA может поинтереснее получиться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Частота таймера 72 МГц.

Я рассчитываю числа следующим образом:

 

Ft - частота таймера = 72000000 Hz

Fd - частота дискретизации. Выбираю 240000 Hz

 

Тогда, число для загрузки в регистр сравнения таймера TOMR0= Ft / Fd = 72000000 / 240000 = 300

 

Длина таблицы значений синуса N = 64 = 2^6;

 

Fs - необходимая частота выходного сигнала = 30000 Hz

 

Значит всю таблицу сигнала необходимо проходить за K прерываний. K = Fd/Fs = 240000 / 30000 = 8

 

Разрядность аккумулятора фазы Res=32

 

Тогда, приращение фазы delta = (2^Res) / K = (2^32) / 8 = 536870912

 

Для индексации элемента в таблице используются старшие 8 бит из аккумулятора фазы. Поэтому сдвигаем вправо на (2^Res) / N = 2^32 / 2^6 = 24

 

В результате моделирования в Кеиле получил вот такой график

 

post-42183-1285494255_thumb.jpg

 

 

 

Код следующий:

 

 

#include "LPC23XX.h"                        

int sinus[64]={512,562,613,662,710,756,800,840,878,
911,941,967,988,1004,1015,1022,1023,1019,1010,996,978,
954,927,895,859,820,778,733,686,638,588,537,486,435,385,
337,290,245,203,164,128,96,69,45,27,13,4,0,1,8,19,35,56,
82,112,145,183,223,267,313,361,410,461,511};


  const int array_size=sizeof(sinus)/sizeof(int);

  void GPIOResetInit(void);

  static unsigned long phase=0;
  unsigned int amplituda=1024;
  static unsigned long delta=536870912;


  /* Timer0 IRQ: Executed periodically                                          */
__irq void Timer0ISR (void) {
  
   DACR = ((sinus[phase>>26]*amplituda)>>4) & 0x0000FFFC;
   phase += delta;

   T0IR = 0x01; //sbros flaga prerivaniya.Avtomaticheski ne rabotaet 
      
   VICVectAddr = 0; /* Acknowledge Interrupt              */                     
   return;
}

/******************************************************************************
**   Main Function  main()
******************************************************************************/
int main (void)
{
     GPIOResetInit();
    
      VICIntSelect = 0x00;      // all interrupts are IRQs
     VICIntEnable = 0x10;      // Channel#4 is the Timer0
       VICVectAddr4=(unsigned)Timer0ISR;
    
     FIO0DIR=1<<26;
     PINSEL1 =1<<21;  //vkluchaem DAC
     
     //timer initialization
     T0IR=0;
     T0CTCR=0;
     T0TC=0; //shchetniy register
     T0PC=0;
     T0PR=0;
     T0MR0=300; // register sravnenia tipa OCR                     
     T0MCR=3; 
     T0TCR=1; 

    while(1)
    {
    
    }

  return 0;
}

void GPIOResetInit( void )
{
  /* Reset all GPIO pins to default: primary function */
  PINSEL0 = 0x00000000;
  PINSEL1 = 0x00000000;
  PINSEL2 = 0x00000000;
  PINSEL3 = 0x00000000;
  PINSEL4 = 0x00000000;
  PINSEL5 = 0x00000000;
  PINSEL6 = 0x00000000;
  PINSEL7 = 0x00000000;
  PINSEL8 = 0x00000000;
  PINSEL9 = 0x00000000;
  PINSEL10 = 0x00000000;

  SCS |= 0x01;
    
  IODIR0 = 0x00000000;
  IODIR1 = 0x20000000;
  IOSET0 = 0x00000000;
  IOSET1 = 0x00000000;
    
  FIO0DIR = 0x0000000f;
  FIO1DIR = 0x20000000;
  FIO2DIR = 0x0000000f;
  FIO3DIR = 0x00000000;
  FIO4DIR = 0x00000000;
    
  FIO0SET = 0x00000000;
  FIO1SET = 0x00000000;
  FIO2SET = 0x00000000;
  FIO3SET = 0x00000000;
  FIO4SET = 0x00000000;  
  return;     
};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для индексации элемента в таблице используются старшие 8 бит из аккумулятора фазы. Поэтому сдвигаем вправо на (2^Res) / N = 2^32 / 2^6 = 24

У Вас таблица из 64 значений, так что индекс - 6 бит. Сдвиг соответственно на 26.

 

В результате моделирования в Кеиле получил вот такой график

Вполне ожидаемый результат. Ошибка частоты меньше чем 0,1% ИМХО не криминал. Попробуйте еще разогнать раза в три (TOMR0 = 100). И прерывание сделайте быстрым.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А то что синус такой корявенький? Это можно исправить только аналоговыми фильтрами ФНЧ на выходе?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, только фильтром. Ускорите свой DDS в три раза и та же частота 30КГц станет красивее и без фильтра.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может кто-то мне ткнуть пальцем на пример применения DMA в LPC2388

В User Manual он просто описывается. На сайте NXP я не нашел Apllication Note про DMA

 

 

http://www.myplace.nu/avr/minidds/index.htm

В этой статье внизу приведены снимки осциллографа. В подписи к изображениям указано что частота сигнала 100 кГц!

И это он использует Мегу с кварцем в 11 Мгц.

 

А я использую АРМ7 с частотой 72 МГц и максимум что пока получил 50кГц

Изменено пользователем snayperAlfa

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И это он использует Мегу с кварцем в 11 Мгц.

А я использую АРМ7 с частотой 72 МГц и максимум что пока получил 50кГц

Ну и сделайте как там - тупой цикл в мейне без всяких глупостей типа прерываний. Тактов в 8-10 уложитесь и получите возможность вообще мегагерцы синтезировать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...