|
|
  |
Реализация DDS на LPC2388 (ARM7) |
|
|
|
Sep 25 2010, 20:28
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
Мое приветствие вам всем! Нужно мне вот в учебных целях реализовать генератор синуса,пилы,треугольника на LPC2388 (ARM7). Пишу на С в Keil. На борту LPC2388 есть ЦАП с временем установления сигнала 1мкс. Таким образом, максимальная частота изменения выходного напряжения с ЦАП составляет 1 МГц ?
Микроконтроллер работает на частоте 72 МГц. Таймер0 работает на частоте 72 МГц.
Таблица синуса состоит из 64 значений.
Аккумулятор фазы - 64 бита - unsigned long long
Моя реализация DDS не позволяет поднять частоту выходного синуса выше 10кГц. Мне кажется это слишком мало!!!
Использую IRQ прерывание от таймера, а оно еще забирает 25 тактов. Сдвиг фазы вычисляю в main(), а в прерывании от таймера только загружаю в регистр ЦАПа значение.
Скажите пожалуйста, нормально ли то что граничная частота синуса у меня 10кГц ? Возможно ли поднять до больших значений, не переходя на АСМ. Посмотрел проекты в интернете, так там на Атмеге получают частоты в 45 кГц!
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 25 2010, 20:44
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(snayperAlfa @ Sep 26 2010, 00:28)  На борту LPC2388 есть ЦАП с временем установления сигнала 1мкс. Таким образом, максимальная частота изменения выходного напряжения с ЦАП составляет 1 МГц ? Нет. 500 кГц. Как бы очевидно: V1, V2, V1, V2... каждую микросекунду дадут сигнал с периодом 2 мкс. Цитата(snayperAlfa @ Sep 26 2010, 00:28)  Скажите пожалуйста, нормально ли то что граничная частота синуса у меня 10кГц ? Возможно ли поднять до больших значений, не переходя на АСМ. Посмотрел проекты в интернете, так там на Атмеге получают частоты в 45 кГц! Надо посмотреть на DMA. Для максимальной скорости данные для DAC следует заготавливать заранее и заряжать DMA для скармливания этих данных DAC. Если DMA это позволяет, то при некоторой сноровке ассемблер не понадобится.
|
|
|
|
|
Sep 25 2010, 20:58
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
Если я правильно понял, то проблема в том что прерывание выполняется слишком долго?
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 25 2010, 21:54
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
Вот собственно код Код #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; };
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 25 2010, 23:54
|
Местный
  
Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527

|
Это вообще работоспособно? Разве что при отключенной оптимизации. Почитайте про квалификатор 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 может поинтереснее получиться.
|
|
|
|
|
Sep 26 2010, 09:45
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
Частота таймера 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 В результате моделирования в Кеиле получил вот такой график
Код следующий: Код #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; };
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 26 2010, 14:52
|
Местный
  
Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527

|
Цитата(snayperAlfa @ Sep 26 2010, 13:45)  Для индексации элемента в таблице используются старшие 8 бит из аккумулятора фазы. Поэтому сдвигаем вправо на (2^Res) / N = 2^32 / 2^6 = 24 У Вас таблица из 64 значений, так что индекс - 6 бит. Сдвиг соответственно на 26. Цитата(snayperAlfa @ Sep 26 2010, 13:45)  В результате моделирования в Кеиле получил вот такой график Вполне ожидаемый результат. Ошибка частоты меньше чем 0,1% ИМХО не криминал. Попробуйте еще разогнать раза в три (TOMR0 = 100). И прерывание сделайте быстрым.
|
|
|
|
|
Sep 26 2010, 18:11
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
А то что синус такой корявенький? Это можно исправить только аналоговыми фильтрами ФНЧ на выходе?
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 26 2010, 19:02
|
Частый гость
 
Группа: Свой
Сообщений: 130
Регистрация: 3-12-08
Из: Солнечная Одесса
Пользователь №: 42 183

|
Может кто-то мне ткнуть пальцем на пример применения DMA в LPC2388 В User Manual он просто описывается. На сайте NXP я не нашел Apllication Note про DMA http://www.myplace.nu/avr/minidds/index.htmВ этой статье внизу приведены снимки осциллографа. В подписи к изображениям указано что частота сигнала 100 кГц! И это он использует Мегу с кварцем в 11 Мгц. А я использую АРМ7 с частотой 72 МГц и максимум что пока получил 50кГц
Сообщение отредактировал snayperAlfa - Sep 26 2010, 20:24
--------------------
Жили бы в пещерах и не знали бы горя.
|
|
|
|
|
Sep 26 2010, 21:11
|
Местный
  
Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527

|
Цитата(snayperAlfa @ Sep 26 2010, 23:02)  И это он использует Мегу с кварцем в 11 Мгц. А я использую АРМ7 с частотой 72 МГц и максимум что пока получил 50кГц Ну и сделайте как там - тупой цикл в мейне без всяких глупостей типа прерываний. Тактов в 8-10 уложитесь и получите возможность вообще мегагерцы синтезировать.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|