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

 
 
> Bugfix для AT91SAM7X's SSC ( с кодеком AD73322L ), Обход граблей, заботливо разложенных Atmel-ом
_dem
сообщение Mar 27 2008, 10:43
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970



Делюсь опытом, может кто столкнется или укажет на кривизну рук smile.gif

Проблема - на плате стоит кодек AD73322l, подключен по SSC к SAM7X.
SSC работает в слейв-режиме, клокится от кодека.

Кодек двухканальный, формат данных потока ...[ 16 bit канал 1 ][ 16 bit канал 2 ]...
Проблема в том, что после некоторого времени работы теста (10-30 минут) SSC передатчик теряет один фрейм и каналы на выходе кодека "меняются местами". Через некоторое время ситуается повторяется и все возвращается в норму.
Приемник всегда работает четко.

Работаю через DMA, переключение буферов в прерывании.

Проверка времени реакции на прерывание, чтение свежей errata, различные телодвижения с бубном и выключением SPORT интерфейса кодека результата не дали.

Проблему удалось (тьфу-тьфу-тьфу) решить (тьфу-тьфу-тьфу) только сбросом (SWRST) и переинициализацией SSC модуля при каждом входе в прерывание.

Еще раз тьфу-тьфу-тьфу, ибо ловить такой баг в тестовой стойке с платами - занятие, несомненно, интересное, но крайне выматывающее... sad.gif

Вот код рабочего обработчика :
Код
__arm void PSSC_InterruptHandler(void)
{

    unsigned long STATUS = AT91C_BASE_SSC->SSC_SR;
    /////////    //////////////////

    // first, updating buffer count
    if (sscProcessor->mainBuffer == FIRST_BUFFER)
        {
        sscProcessor->mainBuffer = SECOND_BUFFER;
        sscProcessor->shadowBuffer = FIRST_BUFFER;
        }
    else
        {
        sscProcessor->mainBuffer = FIRST_BUFFER;
        sscProcessor->shadowBuffer = SECOND_BUFFER;
        }
    
    if (STATUS & AT91C_SSC_RXBUFF)
    {
        
        #ifdef SSC_ENABLE_IRQ_RESETS_SSC
        __SSC_PeripheryInit();

        // forcibly enable IRQ
        AT91C_BASE_SSC->SSC_IER = AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF;
        #endif
        
        // we've lost both buffers, need to reprogram all and to restart transfer
        // restarting Xchange

        // disable transmitter and PDC

        #ifdef SSC_ENABLE_IRQ_RESETS_SSC
        AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXDIS | AT91C_SSC_TXDIS;        
        #endif
        
        AT91C_BASE_SSC->SSC_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
        //
        unsigned short read = AT91C_BASE_SSC->SSC_RHR; // reading that shit for SSC clears itself
      
        read = AT91C_BASE_SSC->SSC_RHR; // reading that shit !
               read = AT91C_BASE_SSC->SSC_RHR; // reading that shit !

        // current TX buffer
        AT91C_BASE_SSC->SSC_TPR      = (unsigned long)(sscProcessor->TXbuffers[ sscProcessor->mainBuffer ]);
        AT91C_BASE_SSC->SSC_TCR      = sscProcessor->buffersLength;        
        
        // current RX buffer
        AT91C_BASE_SSC->SSC_RPR      = (unsigned long)(sscProcessor->RXbuffers[ sscProcessor->mainBuffer ]);
        AT91C_BASE_SSC->SSC_RCR      = sscProcessor->buffersLength;        

            
        // starting RC/TM
            AT91C_BASE_SSC->SSC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
        
            #ifdef SSC_ENABLE_IRQ_RESETS_SSC
        AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN;    
        #endif
        
        // posting semaphore to signal codec 'bout buffers switched
        OSSemPost( sscProcessor->buffersSwitchSemaphore );
    }


    ///////////////////////////    ///////////////////////////    ///////////////////////////
    AT91C_BASE_AIC->AIC_IVR = 0;                          
    
    AT91C_BASE_AIC->AIC_ICCR    =  (1 << AT91C_ID_SSC);    
    AT91C_BASE_AIC->AIC_EOICR   = 0;                      
    
}


И код инициализации :
Код
void __SSC_PeripheryInit( void )
{
    // SSC Control Register
    AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST;                                       /* (SSC) Software Reset */

    // SSC Receive Clock Mode Register
    AT91C_BASE_SSC->SSC_RCMR = ((0x2 << 0) & AT91C_SSC_CKS) | ((0x4 << 8) & AT91C_SSC_START);
//    AT91C_BASE_SSC->SSC_RCMR = ((0x2 << 0) & AT91C_SSC_CKS) | AT91C_SSC_CKI | ((0x3 << 8) & AT91C_SSC_START);    
    // SSC Receive Frame Mode Register
    AT91C_BASE_SSC->SSC_RFMR = ((15 << 0) & AT91C_SSC_DATLEN) | AT91C_SSC_MSBF;
    // SSC Transmit Clock Mode Register
    AT91C_BASE_SSC->SSC_TCMR = ((0x2 << 0) & AT91C_SSC_CKS) | AT91C_SSC_CKI | ((0x4 << 8) & AT91C_SSC_START);;

    // SSC Transmit Frame Mode Register
    AT91C_BASE_SSC->SSC_TFMR = ((15 << 0) & AT91C_SSC_DATLEN) | AT91C_SSC_MSBF;    
}
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
_dem
сообщение Mar 27 2008, 15:15
Сообщение #2


Местный
***

Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970



Ок, я попробую разнести в разные ветки RX/TX, но потеря байта происходит независимо от времени реакции МК - я уже писал, что это была первая версия, пришедшая в голову, и она была проверена - увы, во время сбоя на осциллографе все красиво ( в процессе обхода граблей "удалось" добится потери через десятки секунд после запуска, чтобы прицепить осцилл).

Если МК специально нагружен кодом с большим количеством критических участков либо если в обработчик добавить dummy код, тормозящий его - то все идет так, как и должно - постоянные сбои, слеты каналов (как RX, так и TX), на осцилле видно большое смещение сигнала на тестовой ножке по сравнению с фрейм-синхронимпульсами.
С обычной прошивкой такого нет, за время фрейма можно хоть 100 раз войти в прерывание smile.gif

У вас SSC в каком режиме ? Master или Slave ?
Какой кодек Вы используете ?

ps.

//#define SSC_ENABLE_PDC_DOUBLE_BUFFER
//#define SSC_PDC_IRQ_SWITCHES_CODEC_SE
#define SSC_ENABLE_IRQ_RESETS_SSC

Сообщение отредактировал _dem - Mar 27 2008, 15:15
Go to the top of the page
 
+Quote Post
ljerry
сообщение Mar 27 2008, 16:46
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-02-06
Из: Зеленоград
Пользователь №: 14 071



В общем, рекомендую нарисовать что-либо навроде:

Код
void __SSC_PeripheryInit( void )
{
    // SSC Control Register
    AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST;  /* (SSC) Software Reset */

    // !!! PDC !!!
    AT91C_BASE_SSC->SSC_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;    
    // current buffer
    AT91C_BASE_SSC->SSC_TPR      = (unsigned long)(sscProcessor->TXbuffers[ sscProcessor->mainBuffer ]);
    AT91C_BASE_SSC->SSC_TCR      = sscProcessor->buffersLength;        
    AT91C_BASE_SSC->SSC_TNPR      = (unsigned long)(sscProcessor->TXbuffers[ sscProcessor->shadowBuffer ]);
    AT91C_BASE_SSC->SSC_TNCR      = sscProcessor->buffersLength;        
    // starting RC/TM
    AT91C_BASE_SSC->SSC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

    // !!! SSC !!!
    // SSC Receive Clock Mode Register
    AT91C_BASE_SSC->SSC_RCMR = ((0x2 << 0) & AT91C_SSC_CKS) | ((0x4 << 8) & AT91C_SSC_START);
//    AT91C_BASE_SSC->SSC_RCMR = ((0x2 << 0) & AT91C_SSC_CKS) | AT91C_SSC_CKI | ((0x3 << 8) & AT91C_SSC_START);    
    // SSC Receive Frame Mode Register
    AT91C_BASE_SSC->SSC_RFMR = ((15 << 0) & AT91C_SSC_DATLEN) | AT91C_SSC_MSBF;
    // SSC Transmit Clock Mode Register
    AT91C_BASE_SSC->SSC_TCMR = ((0x2 << 0) & AT91C_SSC_CKS) | AT91C_SSC_CKI | ((0x4 << 8) & AT91C_SSC_START);;
    // SSC Transmit Frame Mode Register
    AT91C_BASE_SSC->SSC_TFMR = ((15 << 0) & AT91C_SSC_DATLEN) | T91C_SSC_MSBF;
    // SSC Control Register
    AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN;
}

__arm void PSSC_InterruptHandler(void)
{
    unsigned long STATUS = AT91C_BASE_SSC->SSC_SR;

    if (STATUS & AT91C_SSC_ENDRX)
    {
        // first, updating buffer count
        if (sscProcessor->mainBuffer == FIRST_BUFFER)
        {
            sscProcessor->mainBuffer = SECOND_BUFFER;
            sscProcessor->shadowBuffer = FIRST_BUFFER;
        }
        else
        {
            sscProcessor->mainBuffer = FIRST_BUFFER;
            sscProcessor->shadowBuffer = SECOND_BUFFER;
        }
    
        // current TX buffer
        AT91C_BASE_SSC->SSC_TNPR      = (unsigned long)(sscProcessor->TXbuffers[ sscProcessor->mainBuffer ]);
        AT91C_BASE_SSC->SSC_TNCR      = sscProcessor->buffersLength;        
        
        // current RX buffer
        AT91C_BASE_SSC->SSC_RNPR      = (unsigned long)(sscProcessor->RXbuffers[ sscProcessor->mainBuffer ]);
        AT91C_BASE_SSC->SSC_RNCR      = sscProcessor->buffersLength;        

        // posting semaphore to signal codec 'bout buffers switched
        OSSemPost( sscProcessor->buffersSwitchSemaphore );
    }

    ///////////////////////////    ///////////////////////////    ///////////////////////////
    AT91C_BASE_AIC->AIC_IVR = 0;                          
    AT91C_BASE_AIC->AIC_ICCR    =  (1 << AT91C_ID_SSC);    
    AT91C_BASE_AIC->AIC_EOICR   = 0;
}


Вот smile.gif

Цитата(_dem @ Mar 27 2008, 18:15) *
У вас SSC в каком режиме ? Master или Slave ?
Какой кодек Вы используете ?


Использую PDC совместно с DBGU, UART и SPI. В принципе идеология использования PDC совместно с SPI такая же, как и при использовании с SSC. Так что пробуйте. Правда, инициализацию собственно SSC я взял из Вашего кода как есть, так что пусть она будет на Вашей совести smile.gif
Go to the top of the page
 
+Quote Post

Сообщений в этой теме


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

 


RSS Текстовая версия Сейчас: 31st July 2025 - 12:53
Рейтинг@Mail.ru


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