Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: глобальные грабли с spi1 в lpc214x
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
diwil
Уважаемые,

Я тут наткнулся на следующие грабли -
запускаю СПИ1 в режиме мастера - CPOL = 1, CPHA = 1,
разрешаю прерывания - SSPIMSC = 8 (прерывания, когда очередь на передачу на половину пуста).

Стартую СПИ1 так -
1. очищаю приемную очередь:
while( SSPSR & (1<<2)) __dummy = SSPDR;

2. Записываю 1 байт в очередь
SSPDR = *out_buff++;

3. Разрешаю прерывания.

Осциллом смотрю на все ноги. На ногу MISO подаю данные, которые знаю.
Однако ножку SSEL1 дергаю сам. Вижу следующее -
Посылается один байт. Вызывается обработчик прерывания.

В обработчике прерывания я
1. проверяю - есть ли данные в приемной очереди :
while(SSPSR & (1<<2)) *in_buff++ = SSPDR;

2. Набиваю очередь на передачу
while(SSPSR & (1<<1)) SSPDR = *out_buff++;

3. Естественно, контролирую количество байт, но это не затрагивает обращение к переферийным регистрам.


Теперь собственно ГРАБЛИ:
1. В обработчике прерывания бит RNE (receive FIFO not empty) никогда не бывает установлен в единицу. А это значит, что ничего не принято - а это не понятно почему. Тем не менее, при запуске СПИ1 в приемной очереди есть данные всегда.

2. Передающая очередь никогда не бывает размером больше 2х - т.е. цикл
while(SSPSR & (1<<1)) SSPDR = *out_buff++;
выполняется не более 2х раз!!! (размер очереди - 8)


мож кто сталкивался с подобным?

Заранее благодарствую
zltigo
Честно прочитал четыре раза. После процесса иницализации и 'стартую 1 2 3' не смог ничего понять из дальнейших действий :-(
С 213x/4x работаю из "проблем" с FIFO только одна - нет возможности сбросить его содержимое без тупого вычитывания.
Все испробованые режимы соответствуют документации. Возможные грабли весьма интересуют посему просьба описать более ясно ситуацию - попробую повторить.
diwil
Цитата(zltigo @ Sep 30 2006, 01:20) *
Честно прочитал четыре раза. После процесса иницализации и 'стартую 1 2 3' не смог ничего понять из дальнейших действий :-(
С 213x/4x работаю из "проблем" с FIFO только одна - нет возможности сбросить его содержимое без тупого вычитывания.
Все испробованые режимы соответствуют документации. Возможные грабли весьма интересуют посему просьба описать более ясно ситуацию - попробую повторить.


Грабли такие -
1. Размер очереди на передачу - по документации она == 8, у меня ее размер, вычисленный путем проверки флага в SSPSR получается не более 2х.
2. Флаг наличия данных в приемной очереди никогда не бывает равен 1. Т.е. такое впечатление, что очередь всегда пуста.
zltigo
Цитата(diwil @ Oct 2 2006, 11:02) *
Грабли такие -

Не совсем понятны условия в которых сие проявляется. В меру своего понимания описанного явления попробую что-то подобное сочинить (просто самому интересно) и проверить.
diwil
Цитата(zltigo @ Oct 2 2006, 13:08) *
Цитата(diwil @ Oct 2 2006, 11:02) *

Грабли такие -

Не совсем понятны условия в которых сие проявляется. В меру своего понимания описанного явления попробую что-то подобное сочинить (просто самому интересно) и проверить.


Код такой -
Код
extern char *in, *out;

void __irq spp_isr(void)
{
   while(SSPSR & (1<<2) )
     *in++ = SSPDR;

   while(SSPSR & (1<<1))
     SSPDR = *out++;
}

void ssp_start(char *inb, char *outb)
{
   int dummy;
   in = inb;
   out = outb+1;

     while(SSPSR & (1<<2) )
       dummy = SSPDR;

   SSPIMSC = 12;
   SSPDR = *outb;
  
}


В обработчике прерываний первый цикл никогда не выполняется, второй - запихивает по два байта в SSPDR.
zltigo
Урвал минутку, посмотрел.
Для начала, так:
Код
  
while(SSPSR & (1<<1))
     SSPDR = *out++;

Делать нельзя, дело в том, что обращение к периферии очень медленное (не менее 8 тактов на обращение) и на приличных скоростях SPI SSPDR удается в общем случае загружать медленне, чем он разгружается :-(
Даже простейший вариант
SSPDR = i++;
Успевает (проверено электроникой) даже на 15Mhz "залить" в 8ми элементное FIFO 32!!! байта до его "переполнения" Глубина FIFO действительно 8 элементов и при приеме из 32 переданных вычитываются первые 8.
Эксперимент проводился на 2148 без индекса 01 - возможно с 01 картина будет несколько иная.
Так что заливать придется, например, с огдядкой :-( на переполнение приемного FIFO.
diwil
хм...

байты по СПИ1 передаются правильно... смотрел осциллом и на другой конец приходят прально.

если читать приемный регистр "руками", то они в нем тоже правильные оказываются...
не понятки - почему не выставляется флаг что приемная очередь не пуста?

тогда ладно... допустим он по прерываниям не будет работать...
а если сделать как в филипсовском примере? (платы нет под рукой)
zltigo
Цитата(diwil @ Oct 2 2006, 14:46) *
хм...

байты по СПИ1 передаются правильно... смотрел осциллом и на другой конец приходят прально.

У меня для начала однозначно захлебывается уже передача.

Цитата
тогда ладно... допустим он по прерываниям не будет работать...

Почему не будет! будет!
А что Вы хотели сказать (ну хотя-бы HEX, если биты влом описать) этим
SSPIMSC = 12;
Выбирая прерывания при полупустых обеих FIFO
зачем оба-то, тем более для мастера?

Цитата
а если сделать как в филипсовском примере?

Не читал "примеры" - давно "завязал" :-( c чтением.


Я честно говоря просто не могу понять конечную цель, чего надо достигнуть???

Давайте так:
1. Master - это понятно.
2. На какой скорости?
3. Обмен пакетный? Одиночными фреймами?
4. Нарезка SSELом на фреймы нужна?
diwil
Цитата(zltigo @ Oct 2 2006, 16:09) *
Давайте так:
1. Master - это понятно.
2. На какой скорости?
3. Обмен пакетный? Одиночными фреймами?
4. Нарезка SSELом на фреймы нужна?


1. Мастер и только мастер
2. PCLK/2 : pclk = 14.7456/2 MHz итого сторость СПИ1 около 1МГц
3. нет. хочу передавать и принимать (одновременно) данные от 37 до 535 байт. при этом, на время всей передачи SSEL должен быть низким.
4. нет
zltigo
Цитата(diwil @ Oct 2 2006, 16:02) *
2. PCLK/2 : pclk = 14.7456/2 MHz итого сторость СПИ1 около 1МГц

"Скорость" SPI таки в битах, посему ~7,5MHz, что является достаточно быстрым и попадает в неприятную зону, когда уже не успеть (практически это в Вашем случае 128 тактов процессора с момента полупустого FIFO)забивать и вычитывать FIFO со скоростью обеспечивающей отсутствие аппаратного дергания SSEL. SSEL - софтом дергать надо будет.
Кроме того это где-то надо думать использовать/не использовать прерывание - ибо прерывание опять будет приходить через 128 тактов - и накладные расходы на обслуживание будут немалые.
Ответ на вопрос по использованию прерывания зависит от прочих системных решений.
Цитата
3. нет. хочу передавать и принимать (одновременно) данные от 37 до 535 байт. при этом, на время всей передачи SSEL должен быть низким.

Т.е. в какой-то момент времени в буфере передачи появляетя порция от 37 до 535 байт и это является сигналом к началу процесса.
Принятые байты складываются в приемный буфер и все разборки "потом".

Самый тупой вариант SSEL софтом управляемый, байты запихиваются в FIFO и со сдвигом на несколько засунутых в FIFO байт считываются. Прерывания от SPI не используются. Времени занимает много, но при разрешенных прерываниях и/или наличии операционки все будет работать с минимальными суммарными накладными расходами.

Набросать такой вариант? Или с прерываниями, если нужно обеспечить более высокую равномерность работы с SPI?
diwil
Цитата(zltigo @ Oct 2 2006, 18:58) *
Самый тупой вариант SSEL софтом управляемый, байты запихиваются в FIFO и со сдвигом на несколько засунутых в FIFO байт считываются. Прерывания от SPI не используются. Времени занимает много, но при разрешенных прерываниях и/или наличии операционки все будет работать с минимальными суммарными накладными расходами.


Пасибо большое.
добрался до платы. Разобрался.
Было:
1. скорость СПИ1 (в битах) около 2МГц
2. За время цикла записи числа в ФИФУ она успевает вычищаться и весь блок передается в одном цикле обработчика прерывания.
3. аппаратный SSEL1 успевает дернуться после записи каждого байта в фифу.
4. И да, когда заканчиваются данные на запись, то начинается чтение из приемной фифы. В результате я там имею первые 8 байт.

Похоже, что прерывание отменяются, ибо макс. внутренний буфер (>4K) , будет накапливаться за 16 мс, что допустимо.

еще раз спасибо
Дима
zltigo
Простейший вариант без прерывания.
На частоте SPI ~7.5MHz и ниже
FIFO не успевает опустошаться и SSEL не дергается, как и требовалось.
Если ~15MHz, то уже не успевает загрузка+разгрузка SSEL дергается - надо
софтово управлять, как и в случае разрешенных прерываний.

Код
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
int init_SPI1( void )
{
    // Prescaler = 2 (Maximum Clock for Master)
    SSPCPSR_bit.CPSDVSR =  2;

    // Configure Pin Connect Block
    PINSEL1_bit.P0_17 = 0x2;       // SCK1
    PINSEL1_bit.P0_18 = 0x2;    // MISO1
    PINSEL1_bit.P0_19 = 0x2;    // MOSI1
    PINSEL1_bit.P0_20 = 0x2;    // SSEL1

    SSPCR0_bit.FRF    =   0;             // SPI Mode
    SSPCR0_bit.DSS    =  (8-1);         // 8bit Transfer
    SSPCR0_bit.CPOL =     1;             // Polarity
    SSPCR0_bit.CPHA =     1;            // Phase
    SSPCR0_bit.SCR     =  (4-1);        // Divider

    // Device selected as master
    SSPCR1_bit.MS      = 0;                // Master
    SSPCR1_bit.SSE  = 1;                // Global Enable

      for( int i = 0; i < 8; i++ )
      {    ulong dummy = SSPDR;         // Clear the RxFIFO
          dummy = dummy;
    }

    return( 0 );    // Ok
}


//---------------------------------------------------------------------------
BYTE ex_tx_buf[37] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29
,30,31,32,33,34,35,36};
BYTE ex_rx_buf[37];
#define EX_TX_BYTE {SSPDR = *tx_ptr++; nn--;}
#define EX_RX_BYTE *rx_ptr++ = SSPDR

// Experiment
//---------------------------------------------------------------------------
void SPI1_experiment( int size )
{
int nn=size;    
BYTE *tx_ptr = ex_tx_buf;
BYTE *rx_ptr = ex_rx_buf;
register BYTE stat;

    memset( rx_ptr, 0xFF, size );

//    ENTER_CRITICAL();
//    {
      
    do
    {  // Забиваем FIFO до упора - хорошо для прерывистого процесса
                 if( (stat = SSPSR) & SSPSR_TNF )
            EX_TX_BYTE;
        if( stat & SSPSR_RNE )    
            EX_RX_BYTE;
    }
    while( nn );
// Вычитывание хвостика FIFO
    do
    {   if( (stat = SSPSR) & SSPSR_RNE )    
            EX_RX_BYTE;
    }
    while( stat & SSPSR_BSY );
// И возможного последнего байта    
         if( SSPSR & SSPSR_RNE )    
        EX_RX_BYTE;

//    }
//    LEAVE_CRITICAL();
      
    bprintf_str( "RX:");
    for( nn = 0; nn<size; nn++ )
        bprintf_str( "%02X ", ex_rx_buf[nn] );        
    bprintf( "end" );          
                      
}
diwil
Цитата(zltigo @ Oct 2 2006, 21:33) *
Простейший вариант без прерывания.
На частоте SPI ~7.5MHz и ниже
FIFO не успевает опустошаться и SSEL не дергается, как и требовалось.


Благодарю примного.
это, конечно же работает... с небольщой оговоркой -
1. частота процессора 14.7456МГц (не надо больше и из соображений требований к потреблению тока)
2. За время записи второго байта в фифу (SPI ~ 2МГц) первый успевает из нее вылететь.

Поэтому мне видится наиболее подходящим следующий алгоритм:
(псевдо)

// очистить приемную очередь... потом:

SSPDR = *tx_ptr++;
tx_cnt--;

do {
// можно проверить есть ли место в передающей фифе. если нет - подождать. без этого тоже работает
SSPDR = *tx_ptr++;
*rx_cnt++ = SSPDR; // наверное нужно проверить rxne флаг, но вроде и без этого работает
} while(--tx_cnt) ;

// и в конце дочистить приемную очередь
zltigo
Цитата(diwil @ Oct 3 2006, 10:26) *
1. частота процессора 14.7456МГц (не надо больше и из соображений требований к потреблению тока)
2. За время записи второго байта в фифу (SPI ~ 2МГц) первый успевает из нее вылететь.

1.Что-то у Вас не то с оптимизаций/компилятором - на частоте процессора в ~15MHz и ~2MHz (точнее где-то 1,84...) SPI контролер успевает загружать FIFO без пауз. Мой пример компилировался IAR, ARM Mode, максимальная оптимизация по скорости.

Цитата(diwil @ Oct 3 2006, 10:26) *
SSPDR = *tx_ptr++;
tx_cnt--;

do {
// можно проверить есть ли место в передающей фифе. если нет - подождать. без этого тоже работает
SSPDR = *tx_ptr++;
*rx_cnt++ = SSPDR; // наверное нужно проверить rxne флаг, но вроде и без этого работает
} while(--tx_cnt) ;

Это то, что я имел ввиду когда:
Цитата
байты запихиваются в FIFO и со сдвигом на несколько засунутых в FIFO байт считываются.


Но:
2. Если у Вас "успевает вылететь", то этому уже ничем не поможешь в случае если количество за раз передаваемых байт превышает размер FIFO :-(.
diwil
Цитата(zltigo @ Oct 3 2006, 11:48) *
Но:
2. Если у Вас "успевает вылететь", то этому уже ничем не поможешь в случае если количество за раз передаваемых байт превышает размер FIFO :-(.


это тоже мне странно... похоже скорость доступа к переферии оч маленькая.
компилю ГЦЦ - скомпилиный код почти идеальный (я бы так же на ассемблере написал).

Дело, наверное, в том, что скорость переферии у меня 14.7456/4 (это экономит миллиамперы).
Похоже, что
str rX, [rY], где rY ссылается на переферию занимает 2 такта переферии, а не процессора.

буду разбираться.
пасиб
zltigo
Цитата(diwil @ Oct 3 2006, 11:06) *
Дело, наверное, в том, что скорость переферии у меня 14.7456/4 (это экономит миллиамперы).

Об этом не говорилось :-) - у меня полная скорость. Если выдатите ВСЕ делители/множители/waitstates, то можно будет поэспериментировать...
Цитата
это тоже мне странно... похоже скорость доступа к переферии оч маленькая.
...
str rX, [rY], где rY ссылается на переферию занимает 2 такта переферии, а не процессора.

Маленькая :-( я уже писал. И про 8 тактов процессора в Вашем случае = "2 тактам периферии" - тоже.
diwil
получилось так:
Код
void
spi1_transfer(uint8_t *outb, uint8_t *inb, int n)
{
  int __dummy;
  uint8_t *in = inb;
  uint8_t *out = outb;

  FIO0SET = 1<<20; // just in case
  // clear spi1 input fifo
  while(SSPSR & (1<<2))
    __dummy = SSPDR;

  FIO0CLR = 1 << 20;
  // send one char
  SSPDR = *out++;
  n--;

  do
  {
    // hang until tr fifo is full
    while((SSPSR & (1<<1)) == 0);

    SSPDR = *out++;

    // check if rx data available
    if (SSPSR & (1<<2))
      *in++ = SSPDR;

  }
  while(--n);

  while(SSPSR & (1<<2))
    *in++ = SSPDR;

  // wait until busy...
  while(SSPSR & (1<<4));

  FIO0SET = 1<<20;
}


передача 536 байт занимает 2864 мкс. Должно быть - 2326мкс.
сорее всего связано с некоторыми задержками и дерганием руками за SSEL1 а так же прерываниями UATRa во время работы SPI1.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.