|
Bugfix для AT91SAM7X's SSC ( с кодеком AD73322L ), Обход граблей, заботливо разложенных Atmel-ом |
|
|
|
Mar 27 2008, 10:43
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Делюсь опытом, может кто столкнется или укажет на кривизну рук  Проблема - на плате стоит кодек AD73322l, подключен по SSC к SAM7X. SSC работает в слейв-режиме, клокится от кодека. Кодек двухканальный, формат данных потока ...[ 16 bit канал 1 ][ 16 bit канал 2 ]... Проблема в том, что после некоторого времени работы теста (10-30 минут) SSC передатчик теряет один фрейм и каналы на выходе кодека "меняются местами". Через некоторое время ситуается повторяется и все возвращается в норму. Приемник всегда работает четко. Работаю через DMA, переключение буферов в прерывании. Проверка времени реакции на прерывание, чтение свежей errata, различные телодвижения с бубном и выключением SPORT интерфейса кодека результата не дали. Проблему удалось (тьфу-тьфу-тьфу) решить (тьфу-тьфу-тьфу) только сбросом (SWRST) и переинициализацией SSC модуля при каждом входе в прерывание. Еще раз тьфу-тьфу-тьфу, ибо ловить такой баг в тестовой стойке с платами - занятие, несомненно, интересное, но крайне выматывающее...  Вот код рабочего обработчика : Код __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; }
|
|
|
|
|
 |
Ответов
(1 - 12)
|
Mar 27 2008, 11:48
|
Участник

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

|
Думаю, что проблема возникает из-за того, что программа не успевает подгрузить передающий канал PDC вовремя (из-за того, что прерывание запрещено слишком долго, скорее всего, на время обработки других прерываний). На сей случай рекомендую использовать двойную буферизацию, с помощью пар регистров SSC_TNPR - SSC_TNCR и SSC_RNPR - SSC_RNCR.
Удачи!
Сообщение отредактировал ljerry - Mar 27 2008, 11:49
|
|
|
|
|
Mar 27 2008, 11:56
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Не хотел загромождать основной пост - двойная буферизация использовалась в оригинальном коде, до решения проблемы. Собственно, я привел обработчик, почищенный от лишнего кода - в полном есть и double buffer, и попытки отключить кодек (у него есть линия SE) на время реакции прерывания - не помогает. Время входа в прерывание (реакция), общая загрузка системы - проверялось... Время обработки плавает (очень примерно) в пределах 10% размера такта SSC (по осциллу), и это под нагрузкой. К тому же, кодек работает дуплексом - RX/TX слова для одинаковых каналов синхронны - а на приемнике такой проблемы не наблюдается, т.е. PDC вроде как и ни причем
Сообщение отредактировал _dem - Mar 27 2008, 11:58
|
|
|
|
|
Mar 27 2008, 12:53
|
Участник

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

|
Цитата(_dem @ Mar 27 2008, 14:56)  Не хотел загромождать основной пост - двойная буферизация использовалась в оригинальном коде, до решения проблемы.
Собственно, я привел обработчик, почищенный от лишнего кода - в полном есть и double buffer, и попытки отключить кодек (у него есть линия SE) на время реакции прерывания - не помогает. Хотелось бы посмотреть именно на исходный код. Цитата(_dem @ Mar 27 2008, 14:56)  К тому же, кодек работает дуплексом - RX/TX слова для одинаковых каналов синхронны - а на приемнике такой проблемы не наблюдается, т.е. PDC вроде как и ни причем  Синхронны-то они только на шине, а в проце обрабатывать их надо по-разному. Для приемника можно включить и настроить канал PDC уже в момент приема первого слова, главное - успеть это сделать до завершения приема. С передатчиком ситуация другая: нужно сначала настроить и включить PDC, только потом включить передатчик (а иначе он может начать брать данные из буфера PDC только после отправки первого слова) и в самом конце разрешить работу внешнего устройства. Если эта последовательность нарушается - есть вероятность сбоя (как говорится, плавали, знаем  ). В общем, надо код смотреть P.S. Кстати, еще один момент: приемный и передающий каналы PDC заканчивают работу в разные моменты времени - передающий после передачи предпоследнего слова, а приемный - после приема последнего слова. И для них нужны отдельные обработчики прерываний, иначе есть шанс либо не успеть прогрузить передающий канал (при использовании прерывания от приемного канала), либо можно не до конца использовать приемный буфер - последнее слово данных попадет в начало нового буфера (при использовании прерывания от передающего канала). А у Вас все сидит в одном прерывании. Правда, при использовании прерывания от приемного канала и при двойной буферизации работать должно.
Сообщение отредактировал ljerry - Mar 27 2008, 13:33
|
|
|
|
|
Mar 27 2008, 13:27
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Хм, а как же у меня работает остальная периферия, там везде PDC.... ? Цитата(ljerry @ Mar 27 2008, 16:53)  Хотелось бы посмотреть именно на исходный код. Нет проблем  Обращаю ваше внимание на последовательность : Код AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXDIS | AT91C_SSC_TXDIS; 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; // starting RC/TM AT91C_BASE_SSC->SSC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; Вот весь код : ( перенес в аттач )
Сообщение отредактировал _dem - Mar 27 2008, 13:28
|
|
|
|
|
Mar 27 2008, 13:52
|
Участник

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

|
В общем, я бы порекомендовал такую идеологию построения кода: А. Инициализация: 1. Останов кодека; 2. Запрет SSC и PDC - приемника и передатчика (на всякий пожарный); 3. Загрузка указателей буферов и их длин в PDC для приемника и передатчика, причем как пар TPR-TCR и RPR-RCR, так и пар TNPR-TNCR и RNPR-RNCR; 4. Разрешение PDC для приемника и передатчика; 5. Разрешение приемника и передатчика; 6. Запуск кодека. Б. Обработчик прерываний: Можно использовать обработчик с проверкой только события ENDRX, но кошернее (ИМХО) использовать раздельные ветки с проверкой событий ENDRX и ENDTX. В обработчике выполняем запись в пары TNPR-TNCR и RNPR-RNCR. Если хотите использовать события TXBUFE и RXBUFF, то для них обязательно должны быть отдельные ветки. Ну как-то вот так
Сообщение отредактировал ljerry - Mar 27 2008, 14:02
|
|
|
|
|
Mar 27 2008, 14:21
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Вот так-то оно и работает, за исключением : 1. Останов/запуск кодека. Как видно из кода, это есть, но отключено - так как это вызывает слышимые щелчки в аудио-потоке, что неприемлимо. 2. Пробовал "использовать раздельные ветки с проверкой событий ENDRX и ENDTX", пробовал все делать в одном прерывании - разницы не обнаружил, оставил в одном (сэкономил себе 1 вызов)
|
|
|
|
|
Mar 27 2008, 14:52
|
Участник

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

|
Цитата(_dem @ Mar 27 2008, 17:21)  Вот так-то оно и работает, за исключением : 1. Останов/запуск кодека. Как видно из кода, это есть, но отключено - так как это вызывает слышимые щелчки в аудио-потоке, что неприемлимо. Из кода не видно - дефайны не показаны. К тому же останавливать его нужно только перед инициализацией (на всякий случай) и перед завершением работы. В прерывании трогать его просто нельзя. Цитата(_dem @ Mar 27 2008, 17:21)  2. Пробовал "использовать раздельные ветки с проверкой событий ENDRX и ENDTX", пробовал все делать в одном прерывании - разницы не обнаружил, оставил в одном (сэкономил себе 1 вызов)  Если используется двойная буферизация, то разницы не будет, и один вызов действительно можно сэкономить. В противном случае обязательно нужны раздельные ветки, иначе обязательно словите проблему. Теперь по приведенному Вами коду. Есть там ветка, начинающаяся с проверки условия RXBUFF. Так вот, в момент возникновения этого события потеря одного фрейма уже произошла и сдвиговый регистр передатчика уже содержит некорректные данные. Почему так происходит - см. мое сообщение, отправленное в 15:53. По какой причине это происходит - скорее всего, из-за запрета прерываний на большое время, возможно, обработчиками других прерываний.
|
|
|
|
|
Mar 27 2008, 15:15
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Ок, я попробую разнести в разные ветки RX/TX, но потеря байта происходит независимо от времени реакции МК - я уже писал, что это была первая версия, пришедшая в голову, и она была проверена - увы, во время сбоя на осциллографе все красиво ( в процессе обхода граблей "удалось" добится потери через десятки секунд после запуска, чтобы прицепить осцилл). Если МК специально нагружен кодом с большим количеством критических участков либо если в обработчик добавить dummy код, тормозящий его - то все идет так, как и должно - постоянные сбои, слеты каналов (как RX, так и TX), на осцилле видно большое смещение сигнала на тестовой ножке по сравнению с фрейм-синхронимпульсами. С обычной прошивкой такого нет, за время фрейма можно хоть 100 раз войти в прерывание  У вас 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
|
|
|
|
|
Mar 27 2008, 16:46
|
Участник

Группа: Участник
Сообщений: 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; } Вот  Цитата(_dem @ Mar 27 2008, 18:15)  У вас SSC в каком режиме ? Master или Slave ? Какой кодек Вы используете ? Использую PDC совместно с DBGU, UART и SPI. В принципе идеология использования PDC совместно с SPI такая же, как и при использовании с SSC. Так что пробуйте. Правда, инициализацию собственно SSC я взял из Вашего кода как есть, так что пусть она будет на Вашей совести
|
|
|
|
|
Mar 28 2008, 07:41
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Хм, у меня в проекте из внешних интерфейсов не используется только CAN, все остальные при деле. Везде, кроме USART - PDC... А тут наступил то ли на грабли, то ли кривизна рук...
В приведенном Вами варианте обработчика PDC, по-моему, будет проблема в случае, если мы пропустим прерывание "half buffer". Хоть это фактически и ЧП (если произойдет запрещение прерываний на *такое* время), но ИМХО это стоит обработать...
Сообщение отредактировал _dem - Mar 28 2008, 07:44
|
|
|
|
|
Mar 28 2008, 08:31
|
Участник

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

|
Цитата(_dem @ Mar 28 2008, 10:41)  Хм, у меня в проекте из внешних интерфейсов не используется только CAN, все остальные при деле. Везде, кроме USART - PDC... А тут наступил то ли на грабли, то ли кривизна рук... У меня PDC и с USART 'ом используется, правда, только в передающем канале. Цитата(_dem @ Mar 28 2008, 10:41)  В приведенном Вами варианте обработчика PDC, по-моему, будет проблема в случае, если мы пропустим прерывание "half buffer". Хоть это фактически и ЧП (если произойдет запрещение прерываний на *такое* время), но ИМХО это стоит обработать... Заодно этот обработчик будет хорошим индикатором системной ошибки. Напишите потом, заработало или нет - самому интересно стало
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|