Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ADC c PDC и программным запуском
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
xelax
Про работу АЦП c PDC в мануале честно говоря мало информации. Я посчитал, что настроив PDC и ADC на работу с ним и программно запустив первое преобразование, далее PDC самостоятельно запустит N преобразований, а затем дернет прерывание, о том что приёмный буфер полный.

На практике оказалось иначе. Стартую преобразование, оно происходит один раз, причём вижу, что результат сохранился в регистре, а в PDC значение N(количество преобразований), стало равно N-1.
Далее отпускаю программу поработать какое-то время, затем останавливаю и вижу что в регистрах картина не изменилась. Тот же самый результат преобразования, теже значения в регистрах PDC.
Проверял в связке sam7x-ek + arm-elf-gdb + sam-ice + eclipse.

Собственно вопросы:
1. Может ли вообще АЦП работать таким образом? И если может, то почему у меня не работает?
2. А если не может, то на кой ляд они прикрутили к АЦП контроллер доступа к памяти, в чём его глубокий смысл?

вот код работы с АЦП
Код
void adcHandler(void)
{  
  if (AT91C_BASE_ADC->ADC_SR & AT91C_ADC_RXBUFF)
  {       
     // Disable data ready Interrupt
     AT91C_BASE_ADC->ADC_IDR = AT91C_ADC_RXBUFF;
     // disables all channels  
     AT91C_BASE_ADC->ADC_CHDR = ALL_CHANNEL_DISABLE;          
     AdcInterrupt();
  }
}

/******************************************************************************
Inits the ADC.
******************************************************************************/
void  OpenAdc(AdcParams_t *param)
{    
  //enable the clock of ADC
  AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_ADC;
                
  // initialization adc
  // reset adc
  AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST;
  
  /* disable receiver PDC transfer requests */
  AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
  
  // Starting a conversion is only possible by software. 8-bit resolution mode.
  AT91C_BASE_ADC->ADC_MR = (uint32_t)sampleRate | AT91C_ADC_LOWRES;
  
  /* set pdc pointer and counter */
  AT91C_BASE_ADC->ADC_RCR = selectionsAmount;
  AT91C_BASE_ADC->ADC_RPR = bufferPointer;
      
  // disables all channels  
  AT91C_BASE_ADC->ADC_CHDR = ALL_CHANNEL_DISABLE;                                                                                                                                                                                                      
  // disable all adc interrupt                  
  AT91C_BASE_ADC->ADC_IDR = ALL_PERIPHERIAL_INTERRUPT_DISABLE;
              
  if (AT91C_BASE_AIC->AIC_SVR[AT91C_ID_ADC] != (uint32_t)adcHandler)
  {    /*first start*/                           
    /* Enable interrupts */
    /* Disable the interrupt on the interrupt controller */
    AT91C_BASE_AIC->AIC_IDCR = (1 << AT91C_ID_ADC);
    /* Save the interrupt handler routine pointer and the interrupt priority */
    AT91C_BASE_AIC->AIC_SVR[AT91C_ID_ADC] = (uint32_t)adcHandler;
    /* Store the Source Mode Register */
    AT91C_BASE_AIC->AIC_SMR[AT91C_ID_ADC] = AT91C_AIC_SRCTYPE_HIGH_LEVEL |
    AT91C_AIC_PRIOR_LOWEST;
    /* Clear the interrupt on the interrupt controller */
    AT91C_BASE_AIC->AIC_ICCR = (1 << AT91C_ID_ADC);  
    /* Enable the interrupt on the interrupt controller */
    AT91C_BASE_AIC->AIC_IECR = (1 << AT91C_ID_ADC);
  }                       
}

/******************************************************************************
Starts convertion on the ADC channel.
Parameters:
  channel - channel number.
******************************************************************************/
void StartAdc(AdcChannelNumber_t channel)
{
  // enable current channel
  AT91C_BASE_ADC->ADC_CHER = channel;  
  // enable receiver PDC transfer requests  
  AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
  // enable receiver buffer full interrupt                     
  AT91C_BASE_ADC->ADC_IER = AT91C_ADC_RXBUFF;        
  // Starts conversion
  AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START;
}
_dem
Можно "клокить" ADC от таймеров.
Примерно так :

Код
    // Setup TC0
    AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0;   // Enable clock to TC0
    AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK | AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_SET | AT91C_TC_ACPC_CLEAR;   // Prescaler set to 1024, Compare mode on RC, Change TIOA0 according to RA and RC
    AT91C_BASE_TC0->TC_RC = TIME_PERIOD;   // Compare value of RC, clear TIOA0 and reset timer
    AT91C_BASE_TC0->TC_RA = TIME_PERIOD/2;  // Compare value of RA, set TIOA0
    AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; // Enable timer

    // configure ADC
    AT91C_BASE_PMC->PMC_PCER = ( 1 << AT91C_ID_ADC);
    // ad2, ad1 channels, pio PB 29,30
    AT91C_BASE_PIOB->PIO_PDR = ( 1 << 29) | (1<<30);
    
    AT91C_BASE_ADC->ADC_CHER = (1<<5);
    AT91C_BASE_ADC->ADC_MR = AT91C_ADC_LOWRES_8_BIT | AT91C_ADC_TRGEN_EN | AT91C_ADC_TRGSEL_TIOA0 | AT91C_ADC_SHTIM | (( 0x02 ) << 8);//prescaler


И тогда, как и хочется, прерывание будет по заполнению буфера

Код
    // Setup interrupts
    AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_ADC, INT_LEVEL_ADC, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, adc_isr);
//    AT91C_BASE_ADC->ADC_IER  = AT91C_ADC_EOC4;  //  IRQ enable EOC0
    AT91C_BASE_ADC->ADC_IER  = AT91C_ADC_RXBUFF;  //  IRQ enable EOC0
    AT91C_BASE_AIC->AIC_IECR = 1 << AT91C_ID_ADC;   // Enable interrupt in AIC
    AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTEN;

    
    // start timer
    AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;    // Start timer


В прерывании настраиваем PDC на новый буфер

Код
// starting PDC
AT91C_BASE_ADC->ADC_RPR = (long)(data);
AT91C_BASE_ADC->ADC_RCR    = BUFF_SIZE;


Извините, если код не очень читабелен - выдрал из тестового проекта.
xelax
Это понятно.

Только всё таки мне интересно, при программном старте измерения АЦП должен сам доизмерять оставшиеся разы или его нужно пинать каждый раз(таймером, внешним триггером, программой).
_dem
Вы либо пинаете его программно (на одно преобразование), либо настраиваете так, чтобы он сам себя пинал от таймера smile.gif

В любом случае один пинок = одно преобразование.
prottoss
Цитата(xelax @ May 12 2008, 19:08) *
пару недель назад тоже пинал АЦП. Хотелось, чтоб через ДМА АЦП сам загонял несколько измерений в буфер. Так ничего и не получилось. В итоге отказался от этой затеи и от прерываний от АЦП тоже. От ДМА у меня один плюс - не надо считывать каждый раз его регистр данных. В начале цикла измерений задаю адрес и размер буфера. Через нужные периоды времени пинаю АЦП

Запускаю АЦП из задачи.
xelax
Наверное примерно также и прийдётся сделать.
По прерыванию от окончания преобразования рестартовать АЦП заново, а когда ДМА выдаст прерывание о заполнении приёмного буфера, прекращать все преобразования.

По таймеру пинать АЦП не получится smile.gif таймеры уже все заняты.

Что-то как-то не продумал Atmel с PDC, потому что автоматический набор значений в буфер сам собой напрашивается. sad.gif
_dem
Да елки-палки smile.gif все работает нормально, автоматически заполняет буфер - вот этот код выдрал из тестового проекта для осфиллографа-на-коленке-за-пять-минут.

Почитайте еще раз ДШ, если что - вечером помогу с исходником более плотно.

Ну не мог же я сделать осцилл на 300 килогерц на побайтных прерываниях, да еще и успевать по UDP слать данные smile.gif
prottoss
Цитата(_dem @ May 12 2008, 21:21) *
вот этот код выдрал из тестового проекта для осфиллографа-на-коленке-за-пять-минут.
Какой код? smile.gif Ничего вроде не прикреплено. Ну так ткнули бы в даташит носом. Я вроде внимательно читал, но так и не понял про ЭТО
_dem
Вечером ткну smile.gif Сейчас на работе, занят.

Курим пока
35.4.5 Timer Triggers
35.5.5 Conversion Triggers
Conversions of the active analog channels are started with a software or a hardware trigger. The
software trigger is provided by writing the Control Register (ADC_CR) with the bit START at 1.

The conversion sequencer allows automatic processing with minimum processor intervention
and optimized power consumption. Conversion sequences can be performed periodically using
a Timer/Counter output. The periodic acquisition of several samples can be processed automatically
without any intervention of the processor thanks to the PDC.
prottoss
Цитата(_dem @ May 12 2008, 22:22) *
Вечером ткну smile.gif Сейчас на работе, занят.
Курим пока
Да это все уже прокуреноsmile.gif Я не хочу использовать таймер и создавать лишние прерывания, которых и так полно в проекте. По этому пинаю АЦП из задачи. Ну а с таймером и так все понятно. То, что буфер заполняется автоматом - это и так очевидно - работает ДМА, если его настроить.

2 xelax

В любом случае АЦП нужен период измерений, который и задается либо таймером аппаратно, либо программным пинком.
_dem
Понятно.

Из вашего сообщения
"пару недель назад тоже пинал АЦП. Хотелось, чтоб через ДМА АЦП сам загонял несколько измерений в буфер. "

тяжело понять, что "сам загонял" - это без пинаний его вообще smile.gif
prottoss
Цитата(_dem @ May 12 2008, 22:53) *
Из вашего сообщения
"пару недель назад тоже пинал АЦП. Хотелось, чтоб через ДМА АЦП сам загонял несколько измерений в буфер. " тяжело понять, что "сам загонял" - это без пинаний его вообще smile.gif
Да, пока не изучил работу АЦП - хотел smile.gif В итоге сделал так, как сделал.
defunct
В атаче выдранные исходники из рабочего проекта. Присоединил как есть, дабы не вносить багов.

Использовать снаружи так:

Код
while(adcContext.pReadDesc->status)
{
    memcpy_burst( (U32 *)din, (U32 *)adcContext.pReadDesc->storage, sizeof( din ));
    // free up the DMA descriptor
    adcContext.pReadDesc->status = 0;
    adcContext.pReadDesc = adcContext.pReadDesc->pNext;
    .... обработка текщего фрейма в din
              
}

if (adcContext.Overload)
{
    //printf("ALERT: OUT OF MIPS\n");
    printf("~");
    adcContext.Overload = 0;
}


где din - массив такой же длины как и pReadDesc->storage.
xelax
2 _dem

Где же обещанный пример без таймера, где PDC самостоятельно запускает и читает ADC. wink.gif
_dem
А кто обещал пример без таймера 8( ???
Нет пока возможности, извините.
Дедлайн sad.gif

Обещал и выложу позже свой проект - пинание от таймера, складывание в PDC, отправка буферов по UDP (Ethernet).
Тянет до 500 кбпс примерно, 8 бит, один канал.
xelax
Я думал у Вас без таймера работает, а с таймером и так всё понятно. Да и пример defunct уже выложил.

Считаю вопрос исчерпанным. Спасибо всем поучаствовавшим в обсуждении.
nicks80
Вот как надо и все работает непрерывно в два буффера попеременно


#include "cpu.h"


volatile unsigned short *adc_buffers;
volatile unsigned char adc_current_buffer=0; ///
volatile unsigned char adc_samples=0;
volatile unsigned char adc_num_channels=0;


char s[32];
void adc_interupt_irq(void) __irq
{
unsigned int status = AT91C_BASE_ADC->ADC_SR;
int i;
int k=0;
int flg=0;
AT91F_PDC_SetNextRx(AT91C_BASE_PDC_ADC, (char *)&adc_buffers[adc_current_buffer*adc_samples/2], adc_samples*sizeof(unsigned short)/4); // çàðÿæàåì âòîðîé áóôåð
if(adc_current_buffer==0)
adc_current_buffer = 1;
else
adc_current_buffer=0;
// Delay(10);
// lsd_clear_screen() ;


sprintf(s,"%d",adc_buffers[0]);

for(i=0;i<128;i++)
{
// if(adc_buffers2[i]>120 || adc_buffers2[i]<132)
// if(adc_buffers2[i]<adc_buffers2[i+1])
// flg=1;
// if(flg)
lcd_set_pixel(i,(adc_buffers[i])/8,0x0000);
}

// lcd_set_str(s,32,32,0,0xFFFFFF, 0x000000) ;




AT91C_BASE_AIC->AIC_EOICR = status;
//âàø êîä
}


/// Èíèöèàëèçàöèÿ ÀÖÏ
void adc_init(volatile unsigned short *buffers,unsigned short samples,unsigned char channels)

{
adc_buffers = buffers;
adc_samples = samples/2;
adc_current_buffer = 0;
AT91C_BASE_ADC->ADC_MR = 0;

/// Ìàòåìàòèêà êðàñèâî ïîëó÷èëàñü

//adc_num_channels = channels;

AT91C_BASE_ADC->ADC_CHER = AT91C_ADC_CH7;//channels;

/// ADCClock = 47.9232 / (7+1)*2 = 2.9952 Mhz
AT91C_BASE_ADC->ADC_MR =(AT91C_ADC_SHTIM & (3<<24)) | (AT91C_ADC_STARTUP & (8 << 16)) | (AT91C_ADC_PRESCAL & (7<<8)) //3F
| AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_LOWRES_8_BIT | AT91C_ADC_TRGSEL_TIOA0 | AT91C_ADC_TRGEN_EN;

// çàðÿæàåì ïåðâûé áóôåð
AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, (char*)&adc_buffers[adc_current_buffer*adc_samples], (adc_samples*sizeof(unsigned short)) );
adc_current_buffer = 1;

// çàðÿæàåì âòîðîé áóôåð
AT91F_PDC_SetNextRx(AT91C_BASE_PDC_ADC, (char*)&adc_buffers[(adc_current_buffer*adc_samples)/2], (adc_samples*sizeof(unsigned short)) );

AT91F_PDC_EnableRx(AT91C_BASE_PDC_ADC);
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0; // Enable clock to TC0 // 47.9232/AT91C_TC_CLKS_TIMER_DIV1_CLOCK[/2] = 23.9616 Mhz
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK|AT91C_TC_WAVESEL_UP_AUTO|AT91C_TC_WAVE|AT91C_TC_A
CPA_SET|AT91C_TC_ACPC_CLEAR;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; // Enable timer
AT91C_BASE_TC0->TC_RC = 768;
AT91C_BASE_TC0->TC_RA = AT91C_BASE_TC0->TC_RC>>1; ///meander

AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_ADC, AT91C_AIC_PRIOR_HIGHEST, AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE , (void *)adc_interupt_irq);
AT91C_BASE_ADC->ADC_IER = AT91C_ADC_ENDRX; // IRQ enable, end of receive buffer
AT91C_BASE_AIC->AIC_IECR = 1 << AT91C_ID_ADC; // Enable interrupt in AIC
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; // Start timer
};
aaarrr
Цитата(nicks80 @ Jun 10 2008, 01:18) *
Вот как надо и все работает непрерывно в два буффера попеременно

Нет, так не надо:
Во-первых, ознакомьтесь с темой - про запуск АЦП от таймера все уже давно все поняли; во-вторых, если уж выкладываете исходник, то приведите его в человеческий вид; в-третьих, пользуйтесь тегами [сode][/сode].
nicks80
Цитата(aaarrr @ Jun 10 2008, 00:37) *
Нет, так не надо:
Во-первых, ознакомьтесь с темой - про запуск АЦП от таймера все уже давно все поняли; во-вторых, если уж выкладываете исходник, то приведите его в человеческий вид; в-третьих, пользуйтесь тегами [сode][/сode].


Спасибо.
Я уже понял что здесь пытались запустить не от таймера.
Но в этом примере есть плюс один, для частоты 23.9616 мгц и периода 768 тиков
имеем целую (без дроби частоту преобразования для разного набора каналов меняя только период тиков 128..256...512....768....).

PLL_DIV = 5
PLL_MUL = 25

PLL_LOCK_COUNT = 28
PRESCAL_MCK = 2
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.