Xmega: DMA и периферийные устройства


    Мы уже изучили элементарные основы работы DMA - пришло время перейти к практическим приложениям этой фантастической системы. На этот раз DMA загрузит данные с платы и отправит их на светодиоды X-порта на плате eXtrino XL , создав простую анимацию. Решение будет полностью основано на оборудовании - как только вы настроите отдельные периферийные устройства, позвольте им работать, а затем все произойдет автоматически.
    Структурная схема подключения периферии и код программы, описанные в этой статье, будут показаны ниже. Работать будет таймер E0, создающий цикл около 100 мс. В течение этого цикла процессор должен выполнить все запланированные задачи.
    Установка контакта порта CS в низкое состояние, который включает ведомый по SPI
    Чтобы более ярко представить, как ведут себя сигналы на шине SPI, я представляю картинку с осциллографа
    Для того чтобы рабочий цикл составлял 100 мс, мы должны установить предварительный делитель таймера на 1024 и установить регистр PER на 200.
    Начнем с  управления CS, который лучше всего использовать с помощью функции Capture / Compare, обычно называемой PWM. Физически CS на плате eXtrino XL направляется на вывод E6 процессора, но таймер E0 имеет доступ к контактам E0-E3, а таймер E1 может управлять выводами E4-E5. Что делать здесь? К счастью, разработчики XMEGA предсказывали такой случай и позволяли переназначать контакты в портах. Мы можем переместить выходы таймера из контактов E0-E3 на E4-E7. Достаточно переместить только E6, который подключен к каналу C функции Captue / Compara. Повторное отображение канала таймера С можно включить, введя PORT_TC0C_bm в регистр PORTE.REMAP .
    Установка регистра TCE0.CCC на 198, когда значение PER равно 200, позволит получить краткосрочное низкое состояние, необходимое для передачи данных по SPI. Большую часть времени вывод E6 будет высоким, а когда счетчик таймера равен 198 (= CCC ), контакт E6 будет подтянут к земле,  до тех пор, пока таймер не достигнет 200 (= PER ).
    В программе мы используем библиотеку extrino_portx.h . Он содержит определение PORTX_AUTOREFRESH , которое может быть 1 или 0, в зависимости от того, хотите ли вы, потому что PORTX автоматически обновляется с использованием прерываний.
    В качестве второго выхода таймера Capture / Compare мы будем использовать CCA (на этот раз он полностью свободен, вы можете выбрать другой канал). Этот вывод подключается к DMA через канал 0 системы событий. Триггер DMA может быть каналами 0, 1 и 2. После приема триггерного сигнала DMA автоматически копирует следующую ячейку из массива source [] и передает ее на интерфейс SPI, который немедленно начнет передавать на X-порт.
    Система DMA может запускаться различными сигналами, и, что интересно, у нее есть что-то вроде собственной системы событий. Таким образом, вы можете напрямую подключать DMA к различным периферийным устройствам и получать еще более высокую скорость копирования данных, но вы должны следить за определенной ловушкой. Более конкретно, передача DMA может активировать флаг прерывания выбранного периферийного чипа, но DMA не всегда может сбросить флаг! Это относится к таймерам - если бы мы приняли флаг остановки CCA в качестве триггера, то мы были бы очень удивлены тем, что после первого запуска DMA не останавливается, и работает всегда. В этом случае мы должны разблокировать прерывание, потому что флаг будет удален сразу после ввода процедуры прерывания. Однако, если процессор будет вводить его только для сброса флага, это совершенно бессмысленно. Поэтому лучше использовать систему событий, которая решит эту проблему.
    В коде программы мы должны обратить внимание на различия в настройках относительно первой программы. В регистр DMA.CH0.REPCNT введен в ноль. Это означает, что транзакция состоит из одного блока, который будет скопирован неограниченно. В регистр DMA.CH0.TRFCNT мы вводим количество байтов, которое состоит из блока, и это, конечно, размер исходной таблицы, взятый с помощью оператора sizeof (). Следующее различие заключается в DMA.CH0.ADDRCTRL , где, набрав DMA_CH_SRCRELOAD_BLOCK_g c, мы определили, что адрес исходного массива будет восстановлен в исходное состояние после завершения передачи блока. Последние различия касаются реестра DMA.CH0.CTRLA где исчезла команда запуска передачи и появились два дополнительных символа:
DMA_CH_SINGLE_bm - этот бит приводит к тому, что после сигнала запуска DMA будет выполнять только одну передачу пакета, после чего он будет ожидать следующего триггера
DMA_CH_REPEAT_bm - позволяет повторять транзакцию столько раз, сколько введено в регистре DMA.CH0.REPCNT. Ноль - особое значение и означает, что вы копируете бесконечное количество раз
    В конце программы мы имеем пустой цикл while (1). Мы компилируем программу, загружаем ее в процессор и наблюдаем, как появляются следующие элементы массива source [] на жк дисплее X-порта.
#include <avr/io.h>
#include "extrino_portx.h"

uint8_t source[] = {0b00000001,
                    0b00000011,
                    0b00000111,
                    0b00001111,
                    0b00011111,
                    0b00111111,
                    0b01111111,
                    0b11111111,
                    0b11111110,
                    0b11111100,
                    0b11111000,
                    0b11110000,
                    0b11100000,
                    0b11000000,
                    0b10000000,
                    0b01000000,
                    0b00100000,
                    0b00010000,
                    0b00001000,
                    0b00000100,
                    0b00000010,
                    0b00000001,
                    0b01010101,
                    0b10101010,
                    0b11111111,
                    0b00000000;
                    }

int main(void) {
    PortxInit();
    TCE0.CTRLB          =    TC_WGMODE_SINGLESLOPE_gc| TC0_CCCEN_bm;
    TCE0.CTRLA          =    TC_CLKSEL_DIV1024_gc;
    TCE0.CCC            =    198;
    TCE0.CCA            =    199;
    TCE0.PER            =    200;
    
    PORTE.REMAP         =    PORT_TC0C_bm;
    PORTE.DIRSET        =    PIN6_bm;
    
    EVSYS.CH0MUX        =    EVSYS_CHMUX_TCE0_CCA_gc;
    
    DMA.CTRL            =    DMA_ENABLE_bm| DMA_DBUFMODE_DISABLED_gc|  DMA_PRIMODE_RR0123_gc; 
    
    DMA.CH0.SRCADDR0    =    (uint16_t)source & 0xFF; 
    DMA.CH0.SRCADDR1    =    (uint16_t)source >> 8;
    DMA.CH0.SRCADDR2    =    0;
    
    DMA.CH0.DESTADDR0   =    (uint16_t)&SPIC.DATA & 0xFF; 
    DMA.CH0.DESTADDR1   =    (uint16_t)&SPIC.DATA >> 8;
    DMA.CH0.DESTADDR2   =    0;
    
    DMA.CH0.TRFCNT      =    sizeof(source);
    DMA.CH0.REPCNT      =    0; 
    DMA.CH0.TRIGSRC     =    DMA_CH_TRIGSRC_EVSYS_CH0_gc;
    DMA.CH0.ADDRCTRL    =    DMA_CH_SRCRELOAD_BLOCK_gc| DMA_CH_SRCDIR_INC_gc| DMA_CH_DESTRELOAD_NONE_gc|  DMA_CH_DESTDIR_FIXED_gc;
    DMA.CH0.CTRLA       =    DMA_CH_ENABLE_bm| DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_SINGLE_bm | DMA_CH_REPEAT_bm;
                              
    while(1) {}
}
    Blogger Comment
    Facebook Comment

0 коммент.:

Отправить комментарий