Перейти к содержанию
    

BF532 приём из UART автобуферизированным DMA

Столкнулся с необъяснимой и в то же время весьма серьёзной (для меня) проблемой: пишу принятые из UART данные в память с помощью DMA в режиме автобуферизации. Сделано из-за того, что принятый макрос может обрабатываться весьма продолжительно, а DMA тем делом будет в фоне накидывать новые команды в буфер из которого по готовности я бы их вынимал и исполнял. Число вновьпринятых данных в буфере пытался определять при помощи регистра DMA6_CURR_X_COUNT, а также в качестве альтернативы, как разность регистра текущего адреса DMA6_CURR_ADDR и адреса первой ячейки приёмного буфера - не тут то было! Как только буфер DMA заполняется и счётный регистр должен вновь стать равен числу элементов буфера, а регистр текущего адреса должен стать равен адресу первого элемента - наблюдается аномальное поведение: текущий адрес = (адресу последней ячейки -- Х), а счётчик = (0 + Х), где Х - некое значение, зависящее как от адреса ячейки до начала приёма очередного блока данных, так и от длины этого блока. Т.е. процессор не успевает отображать число принятых данных, хотя сами данные корректно сливаются в буфер без потерь. Читал аномалилисты, там подобные грабли расписаны, однако для ревизии моего чипа (0.5) вроде как неактуальны, да и предложенные пути обхода не помогают. Процессор BlackFin-BF532 rev0.5, VisualDSP 4.5. Кусок индусского кода:

 

#include <ccblkfn.h> 
#include <cdefBF532.h>
#include <sys/exception.h>
#include <stdio.h>
#include "utils.h"


#define RBUFLENGTH 1024        //Объём принимающего буфера UART

        //Приёмный буфер. Заполняется через UART по DMA (в циклическом режиме). Читается  WaitNewMacros()
unsigned char RBuff[RBUFLENGTH] = {0};//Буфер (циклический DMA), в который складываются принимаемые по UART байты макросов
unsigned short RBuffHead = 0;    //Указатель на начало принимаемого макроса в буфере приёма
unsigned short RBuffTail = 0;    //Указатель на конец (последний элемент) принимаемого макроса в буфере приёма

        //Буфер принятого макроса. В него переписывается принятый макрос из буфера приёма UART
unsigned char MacroBuff[256] = {0};        //Буфер хранящий принятый для обработки макрос
unsigned short MacroHead = 0;            //Указатель на актуальную ячейку в буфере принятого макроа

unsigned char CurMacroLength = 0;        //Число элементов текущего макроса, которое должны принять и обработать
.
.
.


void main (void)
{
    init();

    while (1)
    {
        WaitNewMacros();
        ExecuteMacros();
    }
    
}


void init(void)
{
        //----Настройка PLL
        SetupPLL((PLL_CLK/CLKIN), (PLL_CLK/MAIN_CLK), (PLL_CLK/SCLK));

        //----Настройка прерываний системы
        *pILAT |= EVT_IVG9 | EVT_IVG10 | EVT_IVG12; // clear pending IVG9, IVG10 IVG5(программный флаг) interrupts 
    *pSIC_IAR1 &= 0x00FFFFFF;
    *pSIC_IAR1 |= P15_IVG(10) | P14_IVG(9);    // Назначение прерываний: передача UART (IVG=10), приём UART (IVG=9)
    ssync();
    *pSIC_IMASK |= SIC_MASK(14);            // Разрешены прерывания приёма UART(14),
    *pSIC_IMASK &= ~SIC_MASK(15);            // Запретить прерывание "опустошение буфера передатчика UART"        
    register_handler(ik_ivg9, ISR_UART_receive);
    register_handler(ik_ivg10, ISR_UART_transmit);
    
    //*pIMASK |= EVT_IVG9;                // Разрешить событие с приоритетом 9
    //*pIMASK &= ~EVT_IVG10;            // Запретить событие с приоритетом 10
    *pIMASK    |= EVT_IVG9 | EVT_IVG10;        //Разрешить прерывание событий 9(приём UART),10(передатчик UART пуст)
    //*pIMASK &= ~EVT_IVG10;
    ssync();    

       
       //--==== Настройка DMA приёмника UART на фоновый приём байтов макросов ====--
       //----Остановить UART, т.к. он разрешается в загрузчике
    *pUART_LCR = 0;             //Разрешить доступ к UART_THR UART_RBR UART_IER
    *pUART_GCTL = 0;            //Прекратить работу UART
    *pUART_IER = 0;  
    // Очистить регистры (R1C) приёма, статус линии, регистр идентификации прерыв
    short temp;
    temp = *pUART_RBR;
    temp = *pUART_LSR;
    temp = *pUART_IIR;
       
       //----Инициализация канала DMA приёмника UART
       *pDMA6_CONFIG = 0;
       ssync();
    *pDMA6_START_ADDR = RBuff;
    *pDMA6_X_COUNT = RBUFLENGTH;
/*!!*/    *pDMA6_CURR_ADDR = RBuff;        //При старте до приёма первого байта в этом регистре мусор. НЕПОНЯТНО
/*!!*/    *pDMA6_CURR_X_COUNT = RBUFLENGTH;    //При старте до приёма первого байта в этом регистре мусор. НЕПОНЯТНО
       
       *pDMA6_X_MODIFY = 1;            //Однобайтные слова
       *pDMA6_CONFIG = (1<<12) | WDSIZE_8 | WNR | DMAEN;    //Одномерный ПРИЁМ (из UART в память) 8-ми битных слов с АВТОБУФЕРИЗАЦИЕЙ без прерываний
       
       //----Инициализация UART
    *pUART_LCR |= DLAB;             // Разрешить доступ к DLL и DLH (бит 7 DLAB=1)
    // Настройка скорости передачи
    *pUART_DLL = UART_DIV & 0x00FF;    // UART_DIV=SCLK/(SPEEDx16) (определено в GlobalParam.h)
    *pUART_DLH = UART_DIV>>8;
    // Формат кадра 8N1
    *pUART_LCR = WLS(8);            // WLS[1:0]=0#11 - 8 бит данных; формат 8N1; DLAB сброшен в 0 (доступ к THR,RBR,IER)
        ssync();
    // Очистить регистры приёма, статус линии, регистр идентификации прерыв ЧТЕНИЕМ их содержимого (из примера)
    short temp;
    temp = *pUART_RBR;
    temp = *pUART_LSR;
    temp = *pUART_IIR;

    // Разрешение прерывания по приёму и передаче (опустошение буфера передачи)    
    *pUART_IER = ERBFI | ETBEI;
    *pUART_GCTL |= UCEN;            // Разрешить подачу тактового сигнала (бит UCEN=1)

      
}
//-------------------------------------------------------------------


/*-------------------------------------------------------------------
    Функция ожидания принятия нового макроса по UART с помощью DMA.
    дописать кммент....*/
void WaitNewMacros(void)
{
    unsigned int DMAByteCount;        //Количество байт в буфере приёма DMA от начала этого буфера. Вычисляется как 
                                    //...разность текушего адреса DMA и адреса первого элемента буфера
    bool ReceiveValidMacros = false;    //Как только будет принят макрос с правильным началом и концом и будет переписан в буфер хранения - выйдем
    
    while (1)
    {
        //Ждать принятия хотя бы 2-х новых байт (начало макроса и его длина) С учётом возможности перезаполнения циклического буфера
        while (1)
        {
/*!!*/            delay(4000*1000);        //ЗАДЕРЖКА 20 мс НЕОБХОДИМА
            DMAByteCount = (*(unsigned int *)pDMA6_CURR_ADDR) - (unsigned int)RBuff; //Сколько новых байт помещено в буфер?

            if ( (DMAByteCount-RBuffHead) >= 2 ) break;
            if ( RBuffHead > DMAByteCount )
                if ( (DMAByteCount+RBUFLENGTH-RBuffHead) >= 2 ) break;
        }
        
        //Вынуть информацию из байта кодирующего длину макроса (второй байт в посылке), предусмотреть возможность "закольцовывания"
        if (RBuffHead == (RBUFLENGTH-1))
            CurMacroLength = RBuff[0];
        else CurMacroLength = RBuff[RBuffHead+1];
        
        //Вычислить адрес хвоста макроса (общая длиа = длина + начало + байт кодирующий длину + конец -1 = длина + 2 байта)
        RBuffTail = RBuffHead + CurMacroLength + 2;
        if ( RBuffTail > (RBUFLENGTH-1) )    RBuffTail -= RBUFLENGTH;    //Хвост может лежать уже в новом цикле буфера
        
        //Ждать прихода всего остального макроса
        while (1)
        {
/*!!*/                delay(4000*1000);        //ЗАДЕРЖКА 20 мс НЕОБХОДИМА
            DMAByteCount = (*(unsigned int *)pDMA6_CURR_ADDR) - (unsigned int)RBuff;    //Глючный компилятор вынуждает приаттачивать 32 битные данные к типу int (хотя это априори так)

            if (RBuffHead < RBuffTail)    //Макрос расположен на "прямой" одного витка буфера
            {
                if (DMAByteCount > RBuffTail) {break;}        //Буфер и принятый макрос лежат в одном цикле
                else if (DMAByteCount < RBuffHead) {break;}    //Буфер обогнал принятый макрос на один цикл
                else continue;
            }
            if (RBuffHead > RBuffTail)    //Макрос "закольцевался" (RBuffTail < RBuffHead)
            {
                if (DMAByteCount > RBuffTail) {break;}    //Принятый макрос "закольцевался", но приёмный буфер тоже
                else continue;
            }
        }

        //Проверить на достоверность (первый байт = 0x01 , последний байт = 0x03 )
        //Если верно - переписываем макрос в "буфер хранения макроса" и выходим
        if ( (RBuff[RBuffHead] == 0x01) && (RBuff[RBuffTail] == 0x03) )
        {
            if ( ((RBuffHead+1) > RBuffTail) && (RBuffHead<(RBUFLENGTH-1)) )    //Конец принятого макроса лежит в новом цикле буфера, ячейка адреса в старом. Учесть "закольцовывание"
            {
                unsigned int i = RBuffHead+1;
                unsigned int j = 0;
                //Переписываем начальный кусок макроса из конца буфера (предыдущий цикл буфера)
                for (; i <= (RBUFLENGTH-1); i++)
                {
                    MacroBuff[j] = RBuff[i];
                    j++;
                }
                //Переписываем оставшийся кусок макроса из начала буфера (текущий цикл буфера)
                i = 0;
                for (; j <= CurMacroLength; j++)
                {
                    MacroBuff[j] = RBuff[i];
                    i++;
                }
            }
            else    //Конец принятого макроса и ячейка адреса лежат на одном витке приёмного буфера
            {
                unsigned int i = RBuffHead+1;
                unsigned int j = 0;
                for (; j <= CurMacroLength; j++)
                {
                    MacroBuff[j] = RBuff[i];
                    i++;
                }
            }
            MacroHead = 1;                //Указатель указывает на второй элемент (первый - число байт) в буфере макроса
            ReceiveValidMacros = true;        //Приём осуществлён, переменная позволит выйти из обработчика
        }
    
        if ( RBuffTail >= (RBUFLENGTH-1) ) RBuffHead = 0;//Начать с начала буфера приёма
        else RBuffHead = RBuffTail+1;    //Продолжить заполнять текущий виток буфера приёма
        
        if (ReceiveValidMacros) break;
        else continue;        //Продолжаем ждать макрос
    }
}
//-------------------------------------------------------------------


/*-------------------------------------------------------------------
    Поочерёдное выполенине команд принятого макроса*/
void ExecuteMacros(void)
{
    while (MacroHead <= CurMacroLength)
    {
        switch (MacroBuff[MacroHead])
        {
            case COMAND1:
                Comand1Do();
                break;
            case COMAND2:
                Comand2Do();
                break;
            case COMAND3:
                Comand3Do();
                break;
            default:                //Важно! Если не нашли подходящую команду - прекращаем обрабатывать принятый макрос и выходим (команда не
                MacroHead = CurMacroLength + 1;    //...поддерживается или принят битый байт). Если не выйти - НАСВСЕГДА ЗАВИСНЕТ!"
                break;
        }
    }
}
.
.
.

 

Пришлось вставить два костыля в виде задержек по 20 мс (за это время успевает приняться примерно 23 байта скоростью 115кбод). По другому никак. При коротких посылках - проблема вроде неактуальна, без задержек всё работает. Однако когда я запихиваю в UART массив из 256 байт и при этом буфер DMA заполняется и переходит к началу - алгоритм даёт сбой, т.к. счётчик байт DMA6_CURR_X_COUNT не успевает обновиться раньше 20мс (время весьма условно, расчитывалось подбором+запас). В мануале упомянается про бит Rsest регистра DMA6_CONFIG, который должен чистить FIFO при завершении приёме блока данных, но его установка также не приносила результата.

И ещё один косячёк: после запуска DMA в регистры DMA6_CURR_ADDR и DMA6_CURR_X_COUNT должны переписываться данные из DMA6_START_ADDR и DMA6_X_COUNT соответственно. Но этого не поисходит, в регистрах находится мусор и для корректной работы алгоритма приходится их конфигурировать вручную. Думаю проблема имеет один корень.

Очень надеюсь услышать совет местных гуру в решении этой проблемы. Также не буду против критики кода, т.к. сам понимаю, что выглядит громоздко (но работает зараза), но с СИ пока ещё не "на Ты".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насколько знаю, регистры переписываются после записи конфигурационного регистра, а не после записи в регистры адреса/счетчика. Задержка нехарактерна для работы процессора, такой она быть в принципе не может, есть предположение, что она где-то зарыта на алгоритмическом уровне.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насколько знаю, регистры переписываются после записи конфигурационного регистра, а не после записи в регистры адреса/счетчика.

__ Совершенно верно (точнее так гласит мануал), просто неточно написал (вечер знаете-ли). Информацию из регистров читал естественно после конфигурации DMA6_CONFIG, в котором и запускал DMA. Информация в регистре счётчика внутреннего цикла и регистре текущего адреса обновляется ТОЛЬКО ПОСЛЕ ПРИЁМА ПЕРВОГО БАЙТА. Причём адрес уже указывает на второй элемент буфера, а счётчик имеет значение (BuffLength - 1). Т.е. DMA на аппаратном уровне работает верно, а вот укзанные регистры что-то косячат.

__ А вот в алгоритме я сам не очень уверен (для этого-то код и выложил)) ). Надеюсь, как бы это пародоксально не звучало, что ошибка именно в программе, ибо отбраковывать партию процессоров и перепаивать их на собранных платах - смерти подобно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...