Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: DMA - для чего он и как его использовать
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Abakt
Подскажите для чего нужен DMA и если можно пример как его использовать. Пример "фирменный" или свой - в чем "СОЛЬ" DMA ?
beer_warrior
DMA aka Direct Memory Access - дает возможность принимать данные полностью на аппаратном уровне. Проинициализировав DMA один раз можно уже не беспокоиться об обработке приема/передачи. Например УАРТ надо постоянно поллить или вызывать прерывание по получению байта. С помощью DMA указываеться адрес буфера и количество байт. После этого ядро будет заниматься своими делами, а данные потихоньку капать в буфер. Прерывание вызоветься уже по окончании приема. То же самое и с передачей.
Abakt
Спасибо. подскажите апноут какой нить чтоб посмотреть конкретно.

и еще - этот буфер будет требовать данные или принимть по мере помтупления ? т.е. как передатчик даст ... или по всякому бывает.

и еще - ресь идет о внутренней РАМ или и внешней тоже ?
Abakt
и какова скорость приема дданных ? например по параллельному интерфейсу в LPC или SAM - во сколько раз медленней такта ?
defunct
DMA - это маленький простеникий и тупой slave сопроцессор, который занимается только тем что копирует данные из одного места в другое. Сам по себе DMA работать не может. Основной процессор должен инициализировать DMA - задать ему адрес источника данных, адрес приемника данных, количество данных, после чего - запустить DMA. DMA оповестит основной процессор с помощью прерывания после того как окончит работу.

Скорость работы DMA - определяется источником и приемником данных. С памятью DMA работает точно также как и процессор, т.о. он может писать и во внешнюю память и во внутреннюю. За исключением того, что DMA и процессору приходится делить шину памяти. Доминирует на шине процессор, тобиш если процессору срочно потребовалось что-то прочитать/записать в/из памяти - то DMA в это время будет курить в сторонке.
beer_warrior
Цитата
Спасибо. подскажите апноут какой нить чтоб посмотреть конкретно.

Нечего там смотреть. Прописываются два регистра - указатель на буфер и количество байт. Для SAM все описание узла страницы на три включая раскладку регистров.
Цитата
и еще - этот буфер будет требовать данные или принимть по мере помтупления ? т.е. как передатчик даст ... или по всякому бывает.

По сути DMA это второй счетчик адреса. Т.е. в обычном режиме идет пересылка IO -> регистры ядра -> RAM, с DMA IO -> RAM (для передачи наоборот) пока не насчитает заданное количество байт.
Для IO будет ждать готовности данных, для памяти будет молотить пока свободна шина.
Abakt
большое спасибо за помощь. сижу разбираюсь.
zltigo
Цитата(defunct @ Jan 31 2007, 01:36) *
Доминирует на шине процессор, тобиш если процессору срочно потребовалось что-то прочитать/записать в/из памяти - то DMA в это время будет курить в сторонке.

C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.
Dron_Gus
Цитата(zltigo @ Jan 31 2007, 20:34) *
C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.


Странно. Всегда считал наоборот. Например DMA у ARMов от TI курят как раз, когда проц работает. smile.gif
beer_warrior
Цитата
C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.

А и действительно. Не дадите ли ссылку на доку? Момент ведь весьма принципиальный.
ASN
zltigo
Режим работы контроллера DMA определяется в первую очередь типом используемой накристальной шины. В частности, шина AHB может иметь несколько ведущих и ведомых, и несколько шина адреса данных, подключаемых через мультиплексор. Поэтому время задержки транзакции незначительно, то есть как CPU, так и DMA имеют, в среднем, одинаковые возможности для доступа к ресурсам.
Поправите, если не прав.
Abakt
В режиме DMA не используются ресурсы (регистры, АЛУ и т.п.) на операции пересылки данных.
sonycman
Цитата(beer_warrior @ Jan 31 2007, 23:35) *
А и действительно. Не дадите ли ссылку на доку? Момент ведь весьма принципиальный.

Вот что написано в мануале к SAM7S:

The Memory Controller has a simple, hard-wired priority bus arbiter that gives the control of the
bus to one of the two masters. The Peripheral DMA Controller has the highest priority; the ARM
processor has the lowest one.
[AT91SAM7S.pdf, 6175G–ATARM–22-Nov-06, page 120]

Действительно, курить в сторонке будет процессор, а не DMA...
Karl
Помогите, пожалуйста, с конкретной задачей: Необходимо через ДМА передавать массив в SPI и получать из него ответ (контроллер SAM7S64). Какие операции необходимо произвести?
Kitsok
Цитата(Karl @ Feb 1 2007, 08:49) *
Помогите, пожалуйста, с конкретной задачей: Необходимо через ДМА передавать массив в SPI и получать из него ответ (контроллер SAM7S64). Какие операции необходимо произвести?


Вот как делаю я (коментарии от специалистов приветствуются wink.gif )
Здесь только отсылка, ну и особенности от FreeRTOS, но общий смысл должен быть понятен.

Код
void SPI_task(void *pvParameters)
{
    unsigned portCHAR i;
    unsigned portCHAR SPI_buf[8];
    ( void ) pvParameters;
    portENTER_CRITICAL();

    AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SPI);
    AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, AT91C_PA11_NPCS0 |
        AT91C_PA12_MISO |
        AT91C_PA13_MOSI |
        AT91C_PA14_SPCK, 0);
    AT91F_SPI_Reset(AT91C_BASE_SPI);

    AT91C_BASE_SPI->SPI_MR=0x0E0011;//Master mode, fixed select, disable decoder, FDIV=0 (MCK), PCS=1110

    AT91F_SPI_CfgCs(AT91C_BASE_SPI, 0,  AT91C_SPI_NCPHA | (128 << 8) | 0x04 );
    AT91F_SPI_Enable(AT91C_BASE_SPI);
    AT91F_PDC_Open(AT91C_BASE_PDC_SPI);
    for(i=0;i<8;i++) SPI_buf[i]=i;
    portEXIT_CRITICAL();

    for(;;)

     {
        while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf[0],8,0,0) == 0)
          vTaskDelay(portTICK_RATE_MS * 10);
        i++;
        SPI_buf[7]=i;
        vTaskDelay(portTICK_RATE_MS * 1000);
     }
}
Karl
Спасибо, вроде все понятно. А с приемом Вы не разбирались?
Kitsok
Цитата(Karl @ Feb 1 2007, 13:26) *
Спасибо, вроде все понятно. А с приемом Вы не разбирались?

Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?
Dron_Gus
Цитата(Kitsok @ Feb 1 2007, 14:08) *
Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?


Мне надо. smile.gif Если не затруднит.
Karl
Цитата(Kitsok @ Feb 1 2007, 16:08) *
Цитата(Karl @ Feb 1 2007, 13:26) *

Спасибо, вроде все понятно. А с приемом Вы не разбирались?

Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?


Да, пригодится. Кстати, передача заработала сразу smile.gif Спасибо!

У меня на SPI АЦП сидит. Так что надо и передачу и прием.
zltigo
Цитата(sonycman @ Jan 31 2007, 23:40) *
The Memory Controller has a simple, hard-wired priority bus arbiter that gives the control of the
bus to one of the two masters. The Peripheral DMA Controller has the highest priority; the ARM
processor has the lowest one.
[AT91SAM7S.pdf, 6175G–ATARM–22-Nov-06, page 120]

Действительно, курить в сторонке будет процессор, а не DMA...

Причем по другому для периферийного железа которое не имеет своих буферов и быть не может,
например, летит в 100 Mbit интерфейс 1500 байтовый пакет. Куда ему прикажете его девать?
Тут уж или терять, или процессор идет на перекур. Без вариантов.



Цитата(ASN @ Jan 31 2007, 22:05) *
zltigo
Режим работы контроллера DMA определяется в первую очередь типом используемой накристальной шины.

Определяется, например, свежие LPC23xx имеют два банка памяти и соответственно две шины,
что позволяет достаточно независимо работать DMA (в своих 8K) и CPU каждому в своем банке большую часть времени. Обычные ARM7 такого не имеют.
defunct
Цитата(zltigo @ Feb 1 2007, 18:38) *
Причем по другому для периферийного железа которое не имеет своих буферов и быть не может,
например, летит в 100 Mbit интерфейс 1500 байтовый пакет. Куда ему прикажете его девать?
Тут уж или терять, или процессор идет на перекур. Без вариантов.

Действительно.
My bad..
Kitsok
Это инициализация

Код
#define NCHANNELS 8

extern volatile unsigned portSHORT sADC_RAW[NCHANNELS*2];
#define   TRGEN    (0x0)    // Hardware triggering
#define   TRGSEL   (0x0)    // Use a Timer output signal (on rising edge) from TIOA0 (for this example)
#define   LOWRES   (0x0)    // 8-bit result output
#define   SLEEP    (0x0)    // Normal Mode
#define   PRESCAL  (0x0f)    // Max value
#define   STARTUP  (0xc)    // This time period must be higher than 20 ╣s and not 20 ms
#define   SHTIM    (0x2)    // Must be higher than 3 ADC clock cycles but depends on output
                            // impedance of the analog driver to the ADC input

void InitADC(void) {

  // Reset ADC
  AT91F_ADC_SoftReset (AT91C_BASE_ADC);

  // Open PDC for ADC
  AT91F_PDC_Open (AT91C_BASE_PDC_ADC);

  // Configure PDC for 2 buffers NCHANNELS bytes long each (Warning! Depends on LOWRES bit)
  AT91F_PDC_ReceiveFrame ( AT91C_BASE_PDC_ADC ,
                                (char *) &sADC_RAW[0], NCHANNELS,
                                (char *) &sADC_RAW[NCHANNELS], NCHANNELS);

  // Configure ADC
  AT91F_ADC_CfgModeReg (AT91C_BASE_ADC, (SHTIM << 24) | (STARTUP << 16) | (PRESCAL << 8) |
                             (SLEEP << 5) | (LOWRES <<4) | (TRGSEL << 1) | (TRGEN ) );

  // Enable all 8 channes
  AT91F_ADC_EnableChannel(AT91C_BASE_ADC, (1<<NCHANNELS) - 1);

  // Fire conversion
  AT91F_ADC_StartConversion(AT91C_BASE_ADC);
}


А это собственно работа
Код
        // Check what buffer to use from ADC
        if (AT91C_BASE_PDC_ADC->PDC_RCR == 0)
         {
          ptrADC_RAW=NCHANNELS;
          AT91F_PDC_ReceiveFrame ( AT91C_BASE_PDC_ADC ,
                                (char *) &sADC_RAW[0], NCHANNELS,
                                (char *) &sADC_RAW[NCHANNELS], NCHANNELS);
         }
        else
         {
          ptrADC_RAW=0;
         }

        // Re-fire conversion
        AT91F_ADC_StartConversion(AT91C_BASE_ADC);


Тут возможно следовало бы использовать библиотечные функции, но я не запарился и читаю напрямую PDC_RCR.
ptrADC_RAW нужен для того, чтобы знать, в какой части буфера лежат свежие данные:

Код
// Convert 10-bit wide ADC results into 8-bit wide
          // Clean target array
          for(i=0;i<(NCHANNELS*10/8);i++) ucAxes[i]=0;

          // Do bit manipulations
          // The trick with +0x0200 & 0x03ff is - we need negative 10-bit wide number
          for(i=0;i<(NCHANNELS*10);i++)
           {
            ucAxes[i/8] += (((((sADC_RAW[ptrADC_RAW+(i/10)] + 0x0200)&0x03ff)>> (i%10))  & 0x01) << (i%8));
           }
Karl
Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.
Kitsok
Цитата(Karl @ Feb 6 2007, 13:16) *
Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.


Ну вообще, PDA - штука двунаправленная, но конкретику надо смотреть в даташите и всяких примерах. Я не реализовывал двунаправленную передачу по SPI.
Karl
Цитата(Kitsok @ Feb 6 2007, 17:28) *
Цитата(Karl @ Feb 6 2007, 13:16) *

Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.


Ну вообще, PDA - штука двунаправленная, но конкретику надо смотреть в даташите и всяких примерах. Я не реализовывал двунаправленную передачу по SPI.


Спасибо, вроде разобрался. Заработало.
Kitsok
Цитата(Karl @ Feb 7 2007, 08:44) *
Спасибо, вроде разобрался. Заработало.

Добрый день!

А можете код показать? Удалось ли принимать данные с использованием PDC?
Karl
Цитата(Kitsok @ Feb 12 2007, 21:01) *
Цитата(Karl @ Feb 7 2007, 08:44) *

Спасибо, вроде разобрался. Заработало.

Добрый день!

А можете код показать? Удалось ли принимать данные с использованием PDC?


Да, передачу и прием по PDC организовать удалось. Проблемы, как оказалось, были не с PDC, а с некорректной работой с SPI.
Инициализация SPI:
Код
void SPI_ini(void)
{

    AT91F_SPI_Reset(AT91C_BASE_SPI);
    //delay_ms(5);
    // Инициализация SPI. Запустим на работу с частотой примерно 2,5 МГц
    // Конфигурация канала АЦП (2 канал)
    AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SPI);        // Тактирование на SPI
    
    AT91F_SPI_CfgMode(AT91C_BASE_SPI,
                    1<<0|                //Интерфейс работает в режиме ведущего.
                    0<<1|                //Фиксированный выбор корпуса внешнего периферийного устройства.
                    0<<2|                //PCSDEC: Декодирование выбора корпуса 0 = Периферийные устройства непосредственно подключены к выводам выбора корпуса.
                    0<<4|                //MODFDIS: Определение ошибки режима работы 0 =Определение ошибки режима работы запрещено.
                    0<<7|                // Зацикливание 0 - отключено
                    0xb<<16);                // PCS: Выбор корпуса периферии  Корпус 3
        
    AT91F_SPI_CfgCs(AT91C_BASE_SPI,2,
                    AT91C_SPI_BITS_8|            // 8 бит в пакете
                    400<<8|                        // Частота SPI в 20 наза меньше MCK    (необходимо не более 2,5 МГц)
                    2<<16|                        // Задержка перед выдачей тактовой частоты
                    0<<24|                        // Задержка между последовательными передачами данных
                    1<<1);


    AT91F_SPI_Enable(AT91C_BASE_SPI);
    AT91F_PDC_Open(AT91C_BASE_PDC_SPI);
}


Собственно работа:
Код
for(;;)

{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
        u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
    // Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
    while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
    ...
    ...    
    ...    
}
Kitsok
Спасибо большое!

Я так и думал, что в общем-то ничего сложного.
beer_warrior
Цитата
Собственно работа:
Код
for(;;)

{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
        u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
    // Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
    while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
    ...
    ...    
    ...    
}


Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.
Karl
Цитата(beer_warrior @ Feb 15 2007, 21:59) *
Цитата

Собственно работа:
Код
for(;;)

{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
        u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
    // Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
    while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
    ...
    ...    
    ...    
}


Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.


А Вы прочитайте внимательнее комментарии. Я выложил кусок тестовой програмки. Так проще разобраться в коде тем, кто будет смотреть. Реально у меня все на прерываниях.
Kitsok
Цитата(beer_warrior @ Feb 15 2007, 19:59) *
Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.


Ну почему-же нету кайфа.

Я например под FreeRTOS клепаю, так у меня, пока данные не готовы, будет отдаваться выполнение другим задачам wink.gif

Другой вопрос меня интересует, может доку не внимательно читал.

Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?
HARMHARM
Цитата(Kitsok @ Feb 16 2007, 10:46) *
Другой вопрос меня интересует, может доку не внимательно читал.

Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?

The SPI is a full duplex serial interface, designed to be able to handle multiple masters and slaves connected to a given bus.
Это иэ доки на LPC2194.
Одновременно сдвигаются и регистр приема, и регистр передачи.
Karl
Цитата(Kitsok @ Feb 16 2007, 13:46) *
Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?


SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.
Slonic
Подскажите, пожалуйста, можно ли использовать DMA в случае, когда мне необходимо принимать поток данных с 8-разрядной параллельной шины (быстрый АЦП)? Или это все работает только для внутренних переферийных устройств процессора?
Kitsok
Цитата(Karl @ Feb 16 2007, 12:53) *
SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.


А вот тут возникает интересный вопрос с ChipSelect - если в процессе передачи я меняю адрес устройства для приема, то что будет, старый PCS так и останется активным и новый PCS станет активным, или нет?
Karl
Цитата(Kitsok @ Feb 20 2007, 03:03) *
Цитата(Karl @ Feb 16 2007, 12:53) *


SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.


А вот тут возникает интересный вопрос с ChipSelect - если в процессе передачи я меняю адрес устройства для приема, то что будет, старый PCS так и останется активным и новый PCS станет активным, или нет?


Изменяемый выбор периферийного устройства позволяет вести буферизованную передачу данных с различными периферийными устройствами без перепрограммирования содержимого регистра управления режимом работы. Информация записывается в передающий регистр данных словом в 32-битной ширины, и содержит передаваемые данные и номер периферийного устройства, которому они предназначены. При использовании контроллера ПДП периферии (PDC) в этом режиме необходимы 32 - битные буферы, с хранением данных в LSB и полями PCS и LASTXFER в MSB. Интерфейс может управлять количеством бит данных, передаваемых по линиям MISO и MOSI за один сеанс передачи (8 или 16) с помощью регистров управления выбором корпуса. Если исходить из размера выделяемой под буфера памяти, это не самый оптимальный способ, но он предоставляет очень эффективные возможности для обмена данных с несколькими периферийными устройствами без занятия процессора.
Kitsok
Всем привет!

Вчера вперся в непонятную проблему, решения (кроме совсем тупого) пока не нашел.

Итак, вывод данных у меня сделан с помощью 74HC595, ввод - 74НС165.
Висят на одном CS, точноо, к '165 CS вообще не подключен.
В настройках CPOL=0, NCPHA = 1.

Делаю пока без PDC, потому что не могу элементарно найти глюк.

Отсылаю байт, жду, принимаю байт.
Отсылка работает нормально, '595 принимает байт, на выводах все, что нужно. А вот при приеме затесывается лишняя единичка в начале (LSB). Т.е. у '165 все входы подтянуты к земле, однако принимаю я 0х01.

Меняю CPOL=1, начинает работать ввод, но теряется один бит на выводе.

Учитался даташитов, не могу понять, что не так.

Тупое решение проблемы - раздельный вывод и ввод, т.е. менять CSR между циклом вывода и ввода. Но это значет, что прощай дуплекс, сначала 40 мс ждем пока все данные улетят в вывод, потом столько-же, пока данные прилетят со ввода.
Что делать - не понятно...
Dron_Gus
А обьясните мне глупому по PDC у SAM7. Предположим хочу использовать его для чтения DataFlash. Но ведь чтоб пришел байт, нужно и послать байт. Значит для чтения нужно "гнать" какие-нить левые данныеи и "туда"? И абсолютно безразлично какие? Т.е. я могу настроить блок PDC на передачу на тот же буфер, что и приемник, например?
Сергей Борщ
Цитата(Dron_Gus @ Feb 20 2007, 16:21) *
Значит для чтения нужно "гнать" какие-нить левые данныеи и "туда"? И абсолютно безразлично какие? Т.е. я могу настроить блок PDC на передачу на тот же буфер, что и приемник, например?
Да.


Цитата(Kitsok @ Feb 20 2007, 11:23) *
Отсылка работает нормально, '595 принимает байт, на выводах все, что нужно. А вот при приеме затесывается лишняя единичка в начале (LSB). Т.е. у '165 все входы подтянуты к земле, однако принимаю я 0х01.
Увы, так и есть. 595 защелкивает по фронту тактового импульса, 165 выдает первый бит по этому же фронту, т.е. уже после чтения в SPI. Вот и получается сдвиг на бит. К сожалению, в SPI нет режима, в котором данные выставляются по фронту а читаются по срезу. Можно включить инвертор в цепь CLK одного из регистров, может и поможет - надо помедитировать над времянкой.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.