|
ADC c PDC и программным запуском, sam7x |
|
|
|
May 12 2008, 06:49
|

Местный
  
Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035

|
Про работу АЦП 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; }
|
|
|
|
|
May 12 2008, 09:22
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Можно "клокить" 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; Извините, если код не очень читабелен - выдрал из тестового проекта.
|
|
|
|
|
May 12 2008, 12:59
|

Местный
  
Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035

|
Наверное примерно также и прийдётся сделать. По прерыванию от окончания преобразования рестартовать АЦП заново, а когда ДМА выдаст прерывание о заполнении приёмного буфера, прекращать все преобразования. По таймеру пинать АЦП не получится  таймеры уже все заняты. Что-то как-то не продумал Atmel с PDC, потому что автоматический набор значений в буфер сам собой напрашивается.
|
|
|
|
|
May 12 2008, 13:21
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Да елки-палки  все работает нормально, автоматически заполняет буфер - вот этот код выдрал из тестового проекта для осфиллографа-на-коленке-за-пять-минут. Почитайте еще раз ДШ, если что - вечером помогу с исходником более плотно. Ну не мог же я сделать осцилл на 300 килогерц на побайтных прерываниях, да еще и успевать по UDP слать данные
|
|
|
|
|
May 12 2008, 14:22
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Вечером ткну  Сейчас на работе, занят. Курим пока 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.
|
|
|
|
|
May 12 2008, 14:41
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(_dem @ May 12 2008, 22:22)  Вечером ткну  Сейчас на работе, занят. Курим пока Да это все уже прокурено  Я не хочу использовать таймер и создавать лишние прерывания, которых и так полно в проекте. По этому пинаю АЦП из задачи. Ну а с таймером и так все понятно. То, что буфер заполняется автоматом - это и так очевидно - работает ДМА, если его настроить. 2 xelaxВ любом случае АЦП нужен период измерений, который и задается либо таймером аппаратно, либо программным пинком.
--------------------
|
|
|
|
|
May 12 2008, 14:53
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

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

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
В атаче выдранные исходники из рабочего проекта. Присоединил как есть, дабы не вносить багов. Использовать снаружи так: Код 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.
|
|
|
|
|
May 13 2008, 08:34
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
А кто обещал пример без таймера 8( ??? Нет пока возможности, извините. Дедлайн  Обещал и выложу позже свой проект - пинание от таймера, складывание в PDC, отправка буферов по UDP (Ethernet). Тянет до 500 кбпс примерно, 8 бит, один канал.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|