Увеличение производительности 32-битной математики на 16-битном процессоре - PullRequest
3 голосов
/ 05 ноября 2011

Я работаю над некоторыми встроенными программами для встроенного устройства, в котором используется 16-разрядный PIC, работающий на скорости 40 MIPS и программирующий на C. Система будет контролировать положение двух шаговых двигателей и всегда поддерживать положение шага каждого двигателя. Максимальное положение каждого двигателя составляет около 125000 шагов, поэтому я не могу использовать 16-битное целое число для отслеживания положения. Я должен использовать 32-разрядное целое число без знака (DWORD). Двигатель движется со скоростью 1000 шагов в секунду, и я разработал прошивку так, чтобы шаги обрабатывались в таймере ISR. Таймер ISR выполняет следующие действия:

1) сравнивает текущую позицию одного двигателя с целевой позицией, если они совпадают, установите флаг isMoving в значение false и вернитесь. Если они отличаются, установите флаг isMoving в значение true.

2) Если целевая позиция больше текущей позиции, сделайте шаг вперед, затем увеличьте текущую позицию.

3) Если целевая позиция меньше текущей позиции, сделайте шаг назад, затем уменьшите текущую позицию.

Вот код:

void _ISR _NOPSV _T4Interrupt(void)
{
    static char StepperIndex1 = 'A';    

    if(Device1.statusStr.CurrentPosition == Device1.statusStr.TargetPosition)
    {
        Device1.statusStr.IsMoving = 0;
        // Do Nothing
    }   
    else if (Device1.statusStr.CurrentPosition > Device1.statusStr.TargetPosition)
    {
        switch (StepperIndex1)      // MOVE OUT
        {
            case 'A':
                SetMotor1PosB();
                StepperIndex1 = 'B';
                break;
            case 'B':
                SetMotor1PosC();
                StepperIndex1 = 'C';
                break;
            case 'C':
                SetMotor1PosD();
                StepperIndex1 = 'D';
                break;
            case 'D':
                default:
                SetMotor1PosA();
                StepperIndex1 = 'A';
                break;      
        }
        Device1.statusStr.CurrentPosition--;    
        Device1.statusStr.IsMoving = 1;
    }   
    else
    {
        switch (StepperIndex1)      // MOVE IN 
        {
            case 'A':
                SetMotor1PosD();
                StepperIndex1 = 'D';
                break;
            case 'B':
                SetMotor1PosA();
                StepperIndex1 = 'A';
                break;
            case 'C':
                SetMotor1PosB();
                StepperIndex1 = 'B';
                break;
            case 'D':
                default:
                SetMotor1PosC();
                StepperIndex1 = 'C';
                break;      
        }
        Device1.statusStr.CurrentPosition++;
        Device1.statusStr.IsMoving = 1;
    }   
    _T4IF = 0;          // Clear the Timer 4 Interrupt Flag.
}

Целевая позиция устанавливается в основном цикле программы при получении запросов на перемещение. Строки SetMotorPos - это просто макросы для включения / выключения определенных выводов портов.

Мой вопрос: есть ли способ повысить эффективность этого кода? Код работает нормально, как если бы позиции были 16-битными целыми числами, но как 32-битные целые числа слишком много обработки Это устройство должно связываться с ПК без колебаний, и, как написано, наблюдается заметное снижение производительности. Мне действительно нужна только 18-битная математика, но я не знаю простого способа сделать это! Любой конструктивный вклад / предложения будут наиболее цениться.

Ответы [ 5 ]

6 голосов
/ 05 ноября 2011

Внимание: все номера составлены ...

Предположим, что вышеупомянутый ISR имеет около 200 (вероятно, меньше) инструкций скомпилированного кода, и они включают в себя инструкции по сохранению / восстановлению регистров ЦП до и после ISR, каждый из которых занимает 5 тактов (вероятно, от 1 до 3) и вы вызываете 2 из них по 1000 раз в секунду каждый, в итоге получается 2 *1000* 200 * 5 = 2 миллиона тактовых циклов в секунду или 2 MIPS.

Вы действительно потребляете остальные 38 MIPS в другом месте?

Единственное, что может быть важно здесь, и я не вижу этого, это то, что делается внутри функций SetMotor * Pos * () . Они делают какие-либо сложные вычисления? Они выполняют некоторую медленную связь с двигателями, например ждать, пока они ответят на команды, отправленные им?

В любом случае, сомнительно, что такой простой код будет заметно медленнее при работе с 32-разрядными целыми числами, чем с 16-разрядными.

Если ваш код работает медленно, выясните, где и сколько времени потрачено, профилируйте его. Генерация прямоугольного импульсного сигнала в ISR (значение 1, когда ISR запускается, значение 0, когда ISR собирается вернуться) и измерение его длительности с помощью осциллографа. Или сделайте все, что проще, чтобы выяснить это. Измерьте время, проведенное во всех частях программы, а затем оптимизируйте его там, где это действительно необходимо, а не там, где вы раньше думали, что это будет.

1 голос
/ 05 ноября 2011

Разница между 16 и 32-битной арифметикой не должна быть такой большой, я думаю, поскольку вы используете только приращение и сравнение. Но, возможно, проблема в том, что каждая 32-битная арифметическая операция подразумевает вызов функции (если компилятор не может / не хочет делать встраивание более простых операций).

Одним из предложений было бы сделать арифметику самостоятельно, разбив Device1.statusStr.CurrentPosition на две, скажем, Device1.statusStr.CurrentPositionH и Device1.statusStr.CurrentPositionL. Затем используйте некоторые макросы для выполнения операций, например:

#define INC(xH,xL) {xL++;if (xL == 0) xH++;}

0 голосов
/ 07 ноября 2011

Когда вы изменяете его с 16-битного на 32-битный, вы меняете любой из флагов компиляции, чтобы вместо этого указывать, что он должен компилироваться как 32-битное приложение.

вы пытались компилировать с 32-битными расширениями, но с использованием только 16-битных целых чисел.Вы по-прежнему испытываете такое падение производительности?

Вполне вероятно, что при изменении с 16-битного на 32-битный режим некоторые операции скомпилированы по-разному, возможно, проведите Diff между двумя наборами скомпилированного кода ASM и посмотрите, что на самом деле отличается, это много или это всего лишь пара строк.

Решения могут быть, вместо того, чтобы использовать 32-битное целое число, просто использовать два 16-битных целых числа, когда значение A равно int16.Max, затем установите его в 0, а затемувеличьте значение B на 1, иначе просто увеличьте ValueA на 1, когда значение B будет> = 3, тогда вы проверяете valueA> = 26696 (или что-то подобное, в зависимости от того, используете ли вы беззнаковое или подписанное int16), а затем ваш двигатель проверяет на 12500.

0 голосов
/ 05 ноября 2011

Извините, но вы используете неправильный дизайн программы.

Давайте проверим разницу между 16-битным и 32-битным асим-кодом PIC24 или PIC33 ...

16-битный прирост

inc    PosInt16               ;one cycle

Итак, 16-битное приращение занимает один цикл

32-битное приращение

clr    Wd                     ;one cycle
inc    low PosInt32           ;one cycle
addc   high PosInt32, Wd      ;one cycle

и 32 приращение занимает три цикла. Общая разница составляет 2 цикла или 50 нс (нано секунд).

Простое проскальзывание покажет вам все. У вас 1000 шагов в секунду и 40Mips DSP , поэтому у вас есть 40000 инструкций на шаг при 1000 шагах в секунду. Более чем достаточно!

0 голосов
/ 05 ноября 2011

Я бы избавился от переменной StepperIndex1 и вместо этого использовал бы два младших бита CurrentPosition для отслеживания текущего индекса шага. Также можно отслеживать текущую позицию в полных поворотах (а не на каждом шаге), чтобы она могла помещаться в 16-битную переменную. При перемещении вы увеличиваете / уменьшаете положение только при переходе к фазе «А». Конечно, это означает, что вы можете ориентироваться только на каждый полный оборот, а не на каждый шаг.

...