Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: LPC1111, тормоза SPI
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
esaulenka
Копаюсь с модулем SSP0 на контроллере LPC1111.


Частота камня - 48 МГц, частота SPI - 12 МГц, задержки между байтами 1,17 мкс.
Нажмите для просмотра прикрепленного файла


Частота камня - 36 МГц, частота SPI - 18 МГц, задержки между байтами 1,23 мкс.
Нажмите для просмотра прикрепленного файла



код простейший, никаких прерываний:
Код
uint8_t SendByteSPI( uint8_t data)
{
    LPC_SSP0->DR = data;
    while( LPC_SSP0->SR & BIT(4) )    // SPI0 busy
    ;
    return LPC_SSP0->DR;
}

#define SPI_WriteByte(data)        SendByteSPI(data)
#define SPI_ReadByte()            SendByteSPI(0x00)

uint8_t Mem_ReadByte( uint32_t aunMemAddr )
{
    uint8_t        ucByteReadMem;
    ACTIVE_SPI();
    SPI_WriteByte( 0x03 );            // Read Data Bytes
    SPI_WriteByte( (uint8_t)(aunMemAddr >> 16 ) );
    SPI_WriteByte( (uint8_t)(aunMemAddr >> 8 ) );
    SPI_WriteByte( (uint8_t)(aunMemAddr ) );
    ucByteReadMem    = SPI_ReadByte();
    DEACTIVE_SPI();    
    return ucByteReadMem;
}


Сталкивался кто-нибудь?..
Rst7
Заглянуть в продуцируемый компилятором код не позволяет религия?
esaulenka
Почему же, заглядывал. Надо было сразу написать, что там ничего интересного не найдено.

Код
                  SendByteSPI PROC
;;;47     uint8_t SendByteSPI( uint8_t data)
000000  4903              LDR      r1,|L2.16|
;;;48     {
;;;49         LPC_SSP0->DR = data;
000002  6088              STR      r0,[r1,#8]
                  |L2.4|
;;;52         while( LPC_SSP0->SR & BIT(4) )
000004  68c8              LDR      r0,[r1,#0xc]
000006  06c0              LSLS     r0,r0,#27
000008  d4fc              BMI      |L2.4|
;;;53         ;
;;;59         return LPC_SSP0->DR;
00000a  6888              LDR      r0,[r1,#8]
00000c  b2c0              UXTB     r0,r0
;;;60     }
00000e  4770              BX       lr
                          ENDP
                  |L2.16|
                          DCD      0x40040000


Код
                  Mem_ReadByte PROC
;;;171    //
;;;172    uint8_t Mem_ReadByte( uint32_t aunMemAddr )
000000  b5f8              PUSH     {r3-r7,lr}
;;;173    {
;;;174        uint8_t        ucByteReadMem;
;;;175        
;;;176        ACTIVE_SPI();
000002  2405              MOVS     r4,#5
000004  4e0d              LDR      r6,|L6.60|
000006  0724              LSLS     r4,r4,#28
000008  4605              MOV      r5,r0                ;173
00000a  6126              STR      r6,[r4,#0x10]
00000c  6126              STR      r6,[r4,#0x10]
00000e  2700              MOVS     r7,#0
000010  6127              STR      r7,[r4,#0x10]
;;;177    
;;;178        SPI_WriteByte( 0x03 );            // Read Data Bytes
000012  2003              MOVS     r0,#3
000014  f7fffffe          BL       SendByteSPI
;;;179        SPI_WriteByte( (uint8_t)(aunMemAddr >> 16 ) );
000018  0228              LSLS     r0,r5,#8
00001a  0e00              LSRS     r0,r0,#24
00001c  f7fffffe          BL       SendByteSPI
;;;180        SPI_WriteByte( (uint8_t)(aunMemAddr >> 8 ) );
000020  0428              LSLS     r0,r5,#16
000022  0e00              LSRS     r0,r0,#24
000024  f7fffffe          BL       SendByteSPI
;;;181        SPI_WriteByte( (uint8_t)(aunMemAddr ) );
000028  b2e8              UXTB     r0,r5
00002a  f7fffffe          BL       SendByteSPI
;;;182        ucByteReadMem    = SPI_ReadByte();
00002e  2000              MOVS     r0,#0
000030  f7fffffe          BL       SendByteSPI
;;;183    
;;;184        DEACTIVE_SPI();    
000034  6127              STR      r7,[r4,#0x10]
000036  6126              STR      r6,[r4,#0x10]
;;;185        
;;;186        return ucByteReadMem;
;;;187    }
000038  bdf8              POP      {r3-r7,pc}
;;;188    
                          ENDP
Rst7
QUOTE
Надо было сразу написать, что там ничего интересного не найдено.


Поганенько, конечно. uint8_t замените на uint_fast8_t да заинлайньте SendByteSPI. Но что-то мне кажется, что полста тактов оверхеда там не наберется. Чуть позже гляну в ман на проца, может чего подскажу еще.
Dron_Gus
Код
while( LPC_SSP0->SR & BIT(4) )    // SPI0 busy

я бы заменил на
Код
while( (LPC_SSP0->SR & BIT(2)) == 0 )    // SPI0 busy

Т.е. ждал бы прихода байта, а не IDLE состояния.
Сергей Борщ
QUOTE (Dron_Gus @ Dec 2 2011, 11:36) *
я бы заменил
А я бы проверял TNF и эту проверку на готовность к передаче поставил перед записью в DR.
Dron_Gus
Цитата(Сергей Борщ @ Dec 2 2011, 13:31) *
А я бы проверял TNF и эту проверку на готовность к передаче поставил перед записью в DR.

Если слать побайтно и ждать приема ответного байта, то можно обойтимь одной проверкой.
esaulenka
Уже второй день сижу, экспериментирую... Ну да ладно, пятница sm.gif

Закономерности выявить не удалось.
При частоте ядра 48 и 36 МГц разница незначительная, а при снижении до 24 МГц задержка возрастает до 1,8 мкс.

Цитата(Rst7 @ Dec 1 2011, 20:47) *
uint8_t замените на uint_fast8_t да заинлайньте SendByteSPI.

Сделал так, получил 0,9 мкс задержки.

Цитата(Dron_Gus @ Dec 2 2011, 12:36) *
Т.е. ждал бы прихода байта, а не IDLE состояния.

И ещё одна десятая микросекунды в выигрыше. Почему, мне не ясно...


При выкидывании проверки вообще получаем промежуток между клоками 80 наносекунд. Только, разумеется, ничего не работает ;-)


Цитата(Сергей Борщ @ Dec 2 2011, 13:31) *
А я бы проверял TNF и эту проверку на готовность к передаче поставил перед записью в DR.

Идею не понял. А читаемые данные как достать? Там же FIFO, всё перепутается...
Rst7
QUOTE
При выкидывании проверки вообще получаем промежуток между клоками 80 наносекунд. Только, разумеется, ничего не работает ;-)


Ну и отлично. Пихаете 4 байта без проверок, и выгребаете все из RX, пока RNE=1 - последний байт будет Вашим.

CODE
uint_fast8_t Mem_ReadByte( uint_fast32_t aunMemAddr )
{
    uint8_fast8_t        ucByteReadMem;
    ACTIVE_SPI();
    LPC_SSP0->DR = 0x03;
    LPC_SSP0->DR = aunMemAddr >> 16;
    LPC_SSP0->DR = aunMemAddr >> 8;
    LPC_SSP0->DR = aunMemAddr;
              LPC_SSP0->DR = 0; //Это для выгребания последнего байта.
    while(LPC_SSP0->SR & (1<<2)) ucByteReadMem=LPC_SSP0->DR;
    DEACTIVE_SPI();    
    return ucByteReadMem;
}


Типа такого.
esaulenka
Не взлетит sad.gif
Запихать в FIFO 5 байт получается быстрее, чем он их передаёт. Соответственно, переходим к циклу выгребания, и возвращаем какой-то неверный байт.

Но за идею спасибо, в итоге закончилось следующим:
Код
uint8_t Mem_ReadByte( uint32_t aunMemAddr )
{
    ACTIVE_SPI();
    LPC_SSP0->DR = 0x03;
    LPC_SSP0->DR = aunMemAddr >> 16;
    LPC_SSP0->DR = aunMemAddr >> 8;
    LPC_SSP0->DR = aunMemAddr;
    LPC_SSP0->DR = 0;
    while (LPC_SSP0->SR & BIT(4));
    DEACTIVE_SPI();    
    LPC_SSP0->DR;
    LPC_SSP0->DR;
    LPC_SSP0->DR;
    LPC_SSP0->DR;
    return     LPC_SSP0->DR;
}


При попытке свернуть чтение в цикл получаем неплохие тормоза...
Rst7
QUOTE
При попытке свернуть чтение в цикл получаем неплохие тормоза...


А зачем в цикл сворачивать?
esaulenka
Неверно выразился. Сначала была комбинация из
Код
while (LPC_SSP0->SR & BIT(4));
while(LPC_SSP0->SR & (1<<2))
   ucByteReadMem=LPC_SSP0->DR;

А потом я второй цикл развернул, стало заметно быстрее.

В итоге: было 7 мкс, стало 4,5. С одной стороны - неплохо. А с другой - я два дня страдал фигнёй, ускорив первичную инициализацию аж на 30 миллисекунд. Всё остальное время обмен небольшой, пользователь потерпит.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.