реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> AT91SAM7S работа ADC совместно с PDC (прямой доступ к памяти)
kumle
сообщение Dec 7 2011, 12:17
Сообщение #1


Частый гость
**

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



Очень плохо этот момент описан в даташите и примера в IAR как назло нет.
Пробовал сам сделать но не получилось. Может там есть какая то хитрость ?
Go to the top of the page
 
+Quote Post
RabidRabbit
сообщение Dec 7 2011, 17:49
Сообщение #2


Местный
***

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



Вот мой примерчик, правда, использованные константы не раскрыты - но на то есть даташит sm.gif
Сигнал запуска преобразования генерит 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
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 8 2011, 06:54
Сообщение #3


Гуру
******

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



QUOTE (kumle @ Dec 7 2011, 14:17) *
Пробовал сам сделать но не получилось. Может там есть какая то хитрость ?
Что именно не получилось? Хитростей там нет, все работает в соответствии с даташитом. Главное - успевать вовремя подсовывать DMA пустые буфера. Ибо если буфера закончились, то часть отсчетов АЦП будет проигнорирована, и если оцифровывается более одного канала, то может нарушиться соответствие между номером канала и положением отсчета в буфере.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
kumle
сообщение Dec 8 2011, 07:28
Сообщение #4


Частый гость
**

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



Цитата
на TC1 и TC2 внимания не обращать

Почему не обращать, я через TC2 пытался, может в этом ошибка была, сегодня попробую, спасибо

p/s правда я ARM овский ассемблер еще не освоил crying.gif , но зато PICовский хорошо знаю.

Цитата
Что именно не получилось?

Неполучилось то, что даже в обработчик прерывания отладчик не входит !
Хотя я первоначально сделал проектик для обычного АЦП, без PDC, и все отлично работало, но вот когда я его изменил для работы с PDC, "что-то сломалось" и прога даже в прерывание перестала входить.

Я даже нашел классный пример, и пробовал настроить как там: http://www.tomeko.net/miniscope_v2.php
но все равно ничего не удалось.

Сообщение отредактировал kumle - Dec 8 2011, 07:13
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Dec 8 2011, 08:09
Сообщение #5


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



Готовый проект возмите - там в файле hardware.c начиная с функции adc_initialize().
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 8 2011, 08:10
Сообщение #6


Гуру
******

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



QUOTE (kumle @ Dec 8 2011, 09:28) *
Неполучилось то, что даже в обработчик прерывания отладчик не входит !
Так показывайте, будем искать ошибку.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
kumle
сообщение Dec 9 2011, 08:25
Сообщение #7


Частый гость
**

Группа: Участник
Сообщений: 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, придется в них залесть
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 9 2011, 09:42
Сообщение #8


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
kumle
сообщение Dec 9 2011, 11:38
Сообщение #9


Частый гость
**

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



да один буфер использую (пока) чтоб не загромождать
потом когда заработает сделаю два буфера.

Да в прерывании накосячил видимо откуда-то бездумно дернул.

Сейчас буду разбираться и делать заново, но без использования функций из папки AT91LIB !!!

Есть подозрение что таймер просто неправильно инициализирую

Ура нашел ошибку, забыл вот это: PMC_EnablePeripheral(AT91C_ID_TC0);
То есть не включил питание периферии таймера TC0.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 9 2011, 23:21
Сообщение #10


Гуру
******

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



QUOTE (kumle @ Dec 9 2011, 13:38) *
Ура нашел ошибку, забыл вот это: PMC_EnablePeripheral(AT91C_ID_TC0);
То есть не включил питание периферии таймера TC0.


Как же оно без PDC работало? wacko.gif

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)
Go to the top of the page
 
+Quote Post
kumle
сообщение Dec 10 2011, 12:37
Сообщение #11


Частый гость
**

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



Цитата(Сергей Борщ @ Dec 10 2011, 02:21) *
Как же оно без PDC работало? wacko.gif

Работало,так как там в инициализации 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;    
}
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 14:35
Рейтинг@Mail.ru


Страница сгенерированна за 0.0148 секунд с 7
ELECTRONIX ©2004-2016