|
AT91SAM7S работа ADC совместно с PDC (прямой доступ к памяти) |
|
|
|
 |
Ответов
(1 - 10)
|
Dec 7 2011, 17:49
|

Местный
  
Группа: Свой
Сообщений: 397
Регистрация: 3-12-09
Из: Россия, Москва
Пользователь №: 54 040

|
Вот мой примерчик, правда, использованные константы не раскрыты - но на то есть даташит  Сигнал запуска преобразования генерит TC0, есть два буфера, после заполнения одного из буферов дёргается прерывание, в котором заполненный буфер копируется "во вне" и подставляется в качестве "следующего". На всякий случай скажу, что PDC при работе с ADC считает переданные отсчёты, а не байты. Настройка ADC CODE ; ; adc.s - ADC setup and isr ;
INCLUDE common.inc
AREA TEXT, CODE, READONLY ARM
EXPORT initAdc EXPORT adcTargetBuffer
initAdc ; здесь надо очистить все буферы и проинициализировать переменные!!! ldr r0, =adcTargetBuffer ldr r1, =0 ldr r2, =ADC_BUFFER_COUNT initAdd_clearLoop str r1, [r0], #4 subs r2, #1 bne initAdd_clearLoop ; обнулим переключатель буферов add r2, r0, #(adcBufferFlipFlop - adcTargetBuffer) str r1, [r2] ; настройка ldr r0, =ADC_BASE ; setup PDC ldr r1, =adcFirstBuffer str r1, [r0, #PDC_RPR] ldr r1, =adcSecondBuffer str r1, [r0, #PDC_RNPR] ldr r1, =ADC_BUFFER_COUNT str r1, [r0, #PDC_RCR] str r1, [r0, #PDC_RNCR] ldr r1, =1 ; RXTEN str r1, [r0, #PDC_PTCR] ; настроим АЦП ; опорная частота АЦП = 55000000 / ( ( 10 + 1 ) * 2 ) = 2500000 Гц ; время SHTIM = 3 / 2500000 = 1.2 мкс ldr r1, =0x04170A01 ; триггер по сигналу TIOA0 str r1, [r0, #ADC_MR] ldr r1, =0xFF str r1, [r0, #ADC_CHDR] ldr r1, =(1 << 4) str r1, [r0, #ADC_CHER] ; enable interrupt for buffer full event ldr r1, =0x00040000 str r1, [r0, #ADC_IER] ; настроим AIC ldr r0, =AIC_BASE ldr r1, =0x00000006 str r1, [r0, #AIC_SMR4] ldr r1, =adcBufferReaded str r1, [r0, #AIC_SVR4] ldr r1, =(1 << 4) str r1, [r0, #AIC_IECR] ; включаем прерывание от АЦП ; выход bx LR
; обработчик прерывания от АЦП adcBufferReaded stmfd SP!, {r0-r5,r7,r8,LR} ; меняем буфер ldr r0, =adcFirstBuffer ldr r7, [r0, #(adcBufferFlipFlop - adcFirstBuffer)] tst r7, #1 add r7, #1 str r7, [r0, #(adcBufferFlipFlop - adcFirstBuffer)] ; адрес прочитанного буфера помещаем в PDC_RNCR moveq r8, r0 addne r8, r0, #(adcSecondBuffer - adcFirstBuffer) ldr r4, =ADC_BASE str r8, [r4, #PDC_RNPR] ldr r2, =ADC_BUFFER_COUNT str r2, [r4, #PDC_RNCR] ; вхреначиваем считанные данные в накопительный буфер add r5, r0, #(adcTargetBuffer - adcFirstBuffer) ldr r2, =ADC_BUFFER_COUNT / 8 adcBufferReaded_addToAcc ldmia r8!, {r1,r3,r4,r7} stmia r5!, {r1,r3,r4,r7} subs r2, #1 bne adcBufferReaded_addToAcc ; выход из прерывания adcBufferReaded_exit ldr r0, =ADC_BASE ldr r1, [r0, #ADC_SR] ldr r0, =AIC_BASE ldr r1, =(1 << 4) str r1, [r0, #AIC_EOICR] ldmfd SP!, {r0-r5,r7,r8,LR} subs PC, LR, #4
AREA ZDATA, NOINIT, READWRITE
adcFirstBuffer SPACE ADC_BUFFER_COUNT * 2 ; 10 bits ADC, 2 bytes - one shot adcSecondBuffer SPACE ADC_BUFFER_COUNT * 2 adcTargetBuffer SPACE ADC_BUFFER_COUNT * 2 ; buffer visible for main task EXPORT adcBufferFlipFlop adcBufferFlipFlop SPACE 4 ; флаг переключения буферов и счётчик циклов
END
Настройка таймера TC0 (на TC1 и TC2 внимания не обращать) для пинания ADC: CODE ; enable clock for TC0, TC1 and TC2 ldr r2, =PMC_BASE ldr r1, =((1 << 12) + (1 << 13) + (1 << 14)) str r1, [r2, #PMC_PCER] ; ldr r0, =TC_BASE ldr r1, =0x00000015 str r1, [r0, #TC_BMR] ; setup TC0 - ADC trigger timer, по CPA сигнал TIOA0 поднимаем, по CPC - бросаем ldr r1, =0x0009C400 str r1, [r0, #TC0_CMR] ldr r1, =275 str r1, [r0, #TC0_RC] ldr r1, =139 str r1, [r0, #TC0_RA] ; setup TC1 - pulse make timer ldr r1, =0x8343E400 str r1, [r0, #TC1_CMR] ldr r1, =550 ; 0x01F4 str r1, [r0, #TC1_RC] ldr r1, =274 ; (RC / 2) - 1 str r1, [r0, #TC1_RB] add r1, r1, #2 str r1, [r0, #TC1_RA] ; setup TC2 - tracking timer ldr r1, =0x0000C400 str r1, [r0, #TC2_CMR] ldr r1, =1100 ; 550 * 2 str r1, [r0, #TC2_RC] ; enable interrupt from TC2 RC compare event ldr r1, =0x00000010 str r1, [r0, #TC2_IER] ; enable clock for TC0, TC1 and TC2 ldr r1, =0x00000001 str r1, [r0, #TC0_CCR] str r1, [r0, #TC1_CCR] str r1, [r0, #TC2_CCR] ; start clock for all TCs ldr r1, =0x00000001 ; SYNC str r1, [r0, #TC_BCR] ; start count
Сообщение отредактировал RabidRabbit - Dec 7 2011, 17:53
|
|
|
|
|
Dec 8 2011, 07:28
|

Частый гость
 
Группа: Участник
Сообщений: 149
Регистрация: 15-12-09
Из: Москва
Пользователь №: 54 280

|
Цитата на TC1 и TC2 внимания не обращать Почему не обращать, я через TC2 пытался, может в этом ошибка была, сегодня попробую, спасибо p/s правда я ARM овский ассемблер еще не освоил  , но зато PICовский хорошо знаю. Цитата Что именно не получилось? Неполучилось то, что даже в обработчик прерывания отладчик не входит ! Хотя я первоначально сделал проектик для обычного АЦП, без PDC, и все отлично работало, но вот когда я его изменил для работы с PDC, "что-то сломалось" и прога даже в прерывание перестала входить. Я даже нашел классный пример, и пробовал настроить как там: http://www.tomeko.net/miniscope_v2.phpно все равно ничего не удалось.
Сообщение отредактировал kumle - Dec 8 2011, 07:13
|
|
|
|
|
Dec 9 2011, 08:25
|

Частый гость
 
Группа: Участник
Сообщений: 149
Регистрация: 15-12-09
Из: Москва
Пользователь №: 54 280

|
Цитата Так показывайте, будем искать ошибку. Вот инициализация ADC, PDC и таймера Код //ADC init ADC_Initialize( AT91C_BASE_ADC, AT91C_ID_ADC, AT91C_ADC_TRGEN_EN, AT91C_ADC_TRGSEL_TIOA0, AT91C_ADC_SLEEP_NORMAL_MODE, AT91C_ADC_LOWRES_10_BIT, BOARD_MCK, BOARD_ADC_FREQ, 10, 1200); ADC_EnableChannel(AT91C_BASE_ADC, ADC_NUM_3); IRQ_ConfigureIT(AT91C_ID_ADC, AT91C_AIC_PRIOR_LOWEST, ADCC0_IrqHandler); IRQ_EnableIT(AT91C_ID_ADC);
//PDC init ADC_Ptr1 = ADC_Result; AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTDIS; AT91C_BASE_ADC->ADC_RPR = (unsigned long)ADC_Ptr1; // Receive Pointer Register AT91C_BASE_ADC->ADC_RCR = 64; // Receive Counter Register // Set receive pointer register for next transfer AT91C_BASE_ADC->ADC_RNPR = (unsigned long)ADC_Ptr1; // Set receive counter register for next transfer AT91C_BASE_ADC->ADC_RNCR = 64; AT91C_BASE_ADC->ADC_IER = AT91C_ADC_ENDRX; //enable interrupt on transfer complete AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTDIS; //ADC start
//timer init TC_Configure( AT91C_BASE_TC0, (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING | AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | 0 )); TC_Start(AT91C_BASE_TC0); //старт таймера вот прерывание: Код void ADCC0_IrqHandler(void) { unsigned int status, i; unsigned short* ADC_Ptr2; status = AT91C_BASE_ADC->ADC_SR; if (status & AT91C_ADC_ENDRX) { ADC_Ptr2 = ADC_Result; AT91C_BASE_ADC->ADC_RPR = (unsigned long) ADC_Ptr2; // Receive Pointer Register AT91C_BASE_ADC->ADC_RCR = AD_DATA_BUFFER_SIZE; // Receive Counter Register //AT91C_BASE_ADC->ADC_RNPR = (unsigned long)ADC_Ptr_Next; // Receive Next Pointer Register //AT91C_BASE_ADC->ADC_RNCR = AD_DATA_BUFFER_SIZE; // Receive Next Counter Register conv_done = 1; } else if((status & AT91C_ADC_ENDRX) == AT91C_ADC_ENDRX) { AT91C_BASE_ADC->ADC_RNCR = AD_DATA_BUFFER_SIZE; } } Цитата(Genadi Zawidowski @ Dec 8 2011, 11:09)  Готовый проект возмите - там в файле hardware.c начиная с функции adc_initialize(). Спасибо буду смотреть и сравнитвать со своим, правда я делал с помощью функций из папки at91lib, придется в них залесть
|
|
|
|
|
Dec 9 2011, 09:42
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (kumle @ Dec 9 2011, 10:25)  Вот инициализация ADC, PDC и таймера Покажите еще объявление ADC_Result. И не совсем понятно - вы используете всего один буфер? То есть пока вы вынимаете из него результаты измерений, PDC складывает в него же следующие отсчеты? А вы уверены, что успеете "выгрести" первые результаты до того, как они затрутся? И в прерывании: CODE if (status & AT91C_ADC_ENDRX) { ..... } else if((status & AT91C_ADC_ENDRX) == AT91C_ADC_ENDRX) второе условие никогда нее будет выполнено, ибо идентично первому. У меня примерно так: CODE struct sample_t { uint8_t Audio; uint8_t RSSI; };
sample_t Buffer[BUFFERS][BUFFER_SIZE];
volatile uint_fast8_t wr_idx; volatile uint_fast8_t rd_idx;
void adc_init() { wr_idx = 2; // buffers 0 and 1 in PDC. rd_idx = 0;
AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST;
AT91C_BASE_ADC->ADC_CHER = 0 | (1<<4) // Audio | (1<<6) // RSSI ;
AT91C_BASE_ADC->ADC_RPR = (uintptr_t)Buffer[0]; AT91C_BASE_ADC->ADC_RCR = sizeof(Buffer[0])/sizeof(Buffer[0][0]); AT91C_BASE_ADC->ADC_RNPR = (uintptr_t)Buffer[1]; AT91C_BASE_ADC->ADC_RNCR = sizeof(Buffer[1])/sizeof(Buffer[1][0]); AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTEN; AT91C_BASE_ADC->ADC_IER = AT91C_ADC_ENDRX;
AT91C_BASE_ADC->ADC_MR = 0 | (4 * AT91C_ADC_SHTIM / 0x0F) | (0 * AT91C_ADC_STARTUP / 0x1F) | (2 * AT91C_ADC_PRESCAL / 0x3F) | AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_LOWRES_8_BIT | AT91C_ADC_TRGSEL_TIOA0 | AT91C_ADC_TRGEN_EN ; }
void adc_handler() { uint_fast8_t Tmp = wr_idx;
AT91C_BASE_ADC->ADC_RNPR = (uintptr_t)Buffer[Tmp]; AT91C_BASE_ADC->ADC_RNCR = sizeof(Buffer[Tmp]) /sizeof(Buffer[Tmp][0]);
wr_idx = (Tmp + 1) & (BUFFERS - 1); Ready.signal_isr(); AT91C_BASE_AIC->AIC_EOICR = 0; // Reset AIC logic }
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 9 2011, 23:21
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (kumle @ Dec 9 2011, 13:38)  Ура нашел ошибку, забыл вот это: PMC_EnablePeripheral(AT91C_ID_TC0); То есть не включил питание периферии таймера TC0. Как же оно без PDC работало? QUOTE (kumle @ Dec 8 2011, 09:28)  Хотя я первоначально сделал проектик для обычного АЦП, без PDC, и все отлично работало, но вот когда я его изменил для работы с PDC, "что-то сломалось" и прога даже в прерывание перестала входить.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 10 2011, 12:37
|

Частый гость
 
Группа: Участник
Сообщений: 149
Регистрация: 15-12-09
Из: Москва
Пользователь №: 54 280

|
Цитата(Сергей Борщ @ Dec 10 2011, 02:21)  Как же оно без PDC работало?  Работало,так как там в инициализации ADC бит внешнего триггера был отключен, и каждое следующее преобразование вызывалось вручную в главном цикле. Все у меня все заработало (правда подмену буферов пока недоделал) Инициализация Код //ADC init (with PDC) AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; //канал PDC связанный с АЦП ADC_Initialize( AT91C_BASE_ADC, AT91C_ID_ADC, AT91C_ADC_TRGEN_EN, //включаем работу АЦП от внешнего триггера AT91C_ADC_TRGSEL_TIOA0, //используем таймер TC0 в качестве внешнего триггера AT91C_ADC_SLEEP_NORMAL_MODE, AT91C_ADC_LOWRES_8_BIT, //8-и битный результат BOARD_MCK, //48000000 BOARD_ADC_FREQ, //5000000 10, //stattup_time = 10us 1200); //sample & hold time = 1200ns
ADC_EnableChannel(AT91C_BASE_ADC, ADC_NUM_3); IRQ_ConfigureIT(AT91C_ID_ADC, AT91C_AIC_PRIOR_LOWEST, ADC_IrqHandler); IRQ_EnableIT(AT91C_ID_ADC);
//ADC_Ptr1 = ADC_Result; //AT91C_BASE_ADC->ADC_RPR = (unsigned long)ADC_Ptr1; // Receive Pointer Register //AT91C_BASE_ADC->ADC_RCR = 512; // Receive Counter Register // Set receive pointer register for next transfer //AT91C_BASE_ADC->ADC_RNPR = (unsigned long)ADC_Ptr1; // Set receive counter register for next transfer //AT91C_BASE_ADC->ADC_RNCR = 512;
//timer TC0 init // Enable TC0 peripheral clock PMC_EnablePeripheral(AT91C_ID_TC0); TC_Configure( AT91C_BASE_TC0, (AT91C_TC_ASWTRG_CLEAR | //сброс TIOA от программного триггера AT91C_TC_ACPC_CLEAR | //при достижении RС отрицательный фронт AT91C_TC_ACPA_SET | //при достижении RA положительный фронт AT91C_TC_LDRA_RISING | //загрузка RA при положительном фронте AT91C_TC_WAVE | //выбран режим формирования импульсов AT91C_TC_WAVESEL_UP_AUTO | //инкрементирование счетчика от 0 до RC AT91C_TC_CLKS_TIMER_DIV1_CLOCK)); //выбираем источник синхросигнала MCK/2 = 24000000 AT91C_BASE_TCB->TCB_TC0.TC_RC = 1000; //значение счетчика для окончания импульса AT91C_BASE_TCB->TCB_TC0.TC_RA = 990; //значение счетчика для начала импульса AT91C_BASE_ADC->ADC_IER = AT91C_ADC_ENDRX; //enable interrupt on transfer complete AT91C_BASE_ADC->ADC_PTCR = AT91C_PDC_RXTEN; //ADC start TC_Start(AT91C_BASE_TC0); Прерывание Код void ADC_IrqHandler(void) { unsigned int status, i; unsigned short* ADC_Ptr2;
status = AT91C_BASE_ADC->ADC_SR;
//if (status & AT91C_ADC_ENDRX) //{//Both bit set when get the end of next buffer // ADC_Ptr2 = ADC_Result; // AT91C_BASE_ADC->ADC_RPR = (unsigned long) ADC_Ptr2; // Receive Pointer Register // AT91C_BASE_ADC->ADC_RCR = AD_DATA_BUFFER_SIZE; // Receive Counter Register // //AT91C_BASE_ADC->ADC_RNPR = (unsigned long)ADC_Ptr_Next; // Receive Next Pointer Register // //AT91C_BASE_ADC->ADC_RNCR = AD_DATA_BUFFER_SIZE; // Receive Next Counter Register // conv_done = 1; //} //else if((status & AT91C_ADC_ENDRX) == AT91C_ADC_ENDRX) //{//ENDRX bit set when reach the end of buffer. // AT91C_BASE_ADC->ADC_RNCR = AD_DATA_BUFFER_SIZE; //Write the register to Clear STATUS register ENDRX bit ==>But don't clear the flag??? //}
conv_done = 1; AT91C_BASE_AIC->AIC_EOICR = 0; }
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|