Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: SAM7X и SPI
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Terminator
Есть проект на основе FreeRTOS. Используются оба аппаратных SPI. Все операции через PDC. В драйвере два семафора, один для разделения ресурса SPI, второй для сообщения о прерывании.

Всё было хорошо, но как водится пришла пора делать новый проект. Отличается от старого более навороченным протоколом общения по SPI с FPGA (crc и подтверждение доставки). Конечно есть и другие отличия, но до них дело не дошло.

Вот код записи CPU->FPGA
CODE

static uint8_t mcb_buf_w[6];
static uint8_t mcb_buf_r[6];
uint8_t _MCB_Write(tDevSPI *spi, uint16_t addr, uint8_t data)
{
/*
uint8_t *buf;
buf = pvPortMalloc(5+5);
uint8_t *mcb_buf_w;
uint8_t *mcb_buf_r;
mcb_buf_r = &buf[0];
mcb_buf_w = &buf[5];
*/

uint8_t crc = 0;
uint8_t *b = mcb_buf_w;
uint8_t tmp;
tmp = (addr >> 8);
crc = CRC_FUNC(tmp, crc);
*b++ = tmp;

tmp = addr & 0xFF;
crc = CRC_FUNC(tmp, crc);
*b++ = tmp;

tmp = data;
crc = CRC_FUNC(tmp, crc);
*b++ = tmp;

*b++ = ~crc;

debug_array("mcb_write r", mcb_buf_r, 5);
debug_array("mcb_write w", mcb_buf_w, 5);
SPI_Read_Write(spi, mcb_buf_r, mcb_buf_w, 5);
debug_array("mcb_write d r", mcb_buf_r, 5);
debug_array("mcb_write d w", mcb_buf_w, 5);

uint8_t ret = mcb_buf_r[4];
//vPortFree(buf);
return ret;
}


В таком виде не работает, а если раскомментарить выделение и освобождение памяти из кучи, то начинает работать.
Объявление mcb_buf_r и mcb_buf_w за пределами функций я сделал просто так, проверял разные бредовые идеи.


Неработоспособность проявляется следующим образом: данные в FPGA прилетают со сдвигом, т.е. первый байт правильный, вместо второго третий и т.п., в конце вставляется случайный байт. Иногда вместо первого байта отправляется "0".

Всё уже перепробовал, и стеки увеличивал, расположение mcb_buf_r и mcb_buf_w в ОЗУ менял. Другую версию компилятора тоже пробовал. Игрался с оптимизацией. Ничего не помогает. Причём тот же самый драйвер висит на другом аппаратном SPI и через него идёт общение с внешней flash, ниразу никаких проблем небыло.

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

Компилятор gcc 4.3.3 собранный в gentoo тулзой crossdev. Пробовал на "старой" версии, собранной руками 4.3.1

Код драйвера SPI
CODE

typedef struct {
uint8_t hw_init;
// семафор прерывания
xSemaphoreHandle sem;
// семафор для доступа к SPI
xSemaphoreHandle spi_sem;

// выбранный CS
int8_t cs;

xQueueHandle queue;
AT91PS_SPI spi;
} tSPI;

typedef struct {
// аппаратный SPI
tSPI *hw;
// номер аппаратного CS
uint32_t cs;
// требуемая скорость
uint32_t speed;
uint32_t period;
} tDevSPI;

static void spi_isr(tSPI *spi)
{
*AT91C_AIC_IVR = 0;
tSPITask _task;
portBASE_TYPE res = pdFALSE;
unsigned int stat = spi->spi->SPI_SR & spi->spi->SPI_IMR;

if (stat & AT91C_SPI_ENDTX)
{
portBASE_TYPE tres = xQueueReceiveFromISR(spi->queue, &_task, &res);
if (tres == pdTRUE)
{
// новая порция данных
spi->spi->SPI_TNPR = (uint32_t)_task.buf;
spi->spi->SPI_TNCR = _task.count;
}
else
{
// данные кончились будем ждать окончания передачи
spi->spi->SPI_IDR = AT91C_SPI_ENDTX;
spi->spi->SPI_IER = AT91C_SPI_TXEMPTY;
}
}
else
{
spi->spi->SPI_IDR = stat;
// выключаем PDC
spi->spi->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;

// сигналим задаче
xSemaphoreGiveFromISR(spi->sem, &res);
}

AT91C_BASE_AIC->AIC_EOICR = 0;

if (res)
{
portYIELD_FROM_ISR();
}
}

// обработчик прерывания
static void __attribute__((naked))
SPI0_ISR(void)
{
portSAVE_CONTEXT();
spi_isr(&all_spi[0]);
portRESTORE_CONTEXT();
}



void SPI_Read_Write(tDevSPI *dev, void *read_buf, void *write_buf, uint32_t count)
{
SPI_Take(dev);
DBGSTR("SPI_Read_Write\n");
debug_array("read_buf", read_buf, count);
debug_array("write_buf", write_buf, count);

// чтобы сбросить флаг приёма байта, возможно оставленный прошлой
// передачей
uint8_t tmp = dev->hw->spi->SPI_RDR;
(void)tmp;

if (dev->hw->cs != dev->cs)
SPI_CS_Sel(dev);

// готовим PDC
dev->hw->spi->SPI_RPR = (uint32_t)read_buf;
dev->hw->spi->SPI_RCR = count;

dev->hw->spi->SPI_TPR = (uint32_t)write_buf;
dev->hw->spi->SPI_TCR = count;


// включаем PDC
dev->hw->spi->SPI_PTCR = AT91C_PDC_RXTEN;
dev->hw->spi->SPI_PTCR = AT91C_PDC_TXTEN;

uint32_t rpr = dev->hw->spi->SPI_RPR;
uint32_t tpr = dev->hw->spi->SPI_TPR;
uint32_t rcr = dev->hw->spi->SPI_RCR;
uint32_t tcr = dev->hw->spi->SPI_TCR;

DBG("read buf %x cnt %x\n", rpr, rcr);
DBG("write buf %x cnt %x\n", tpr, tcr);

// разрешаем прерывание
dev->hw->spi->SPI_IER = AT91C_SPI_RXBUFF;

// ждём семафор
xSemaphoreTake(dev->hw->sem, portMAX_DELAY);

rpr = dev->hw->spi->SPI_RPR;
tpr = dev->hw->spi->SPI_TPR;
rcr = dev->hw->spi->SPI_RCR;
tcr = dev->hw->spi->SPI_TCR;

DBG("end read buf %x cnt %x\n", rpr, rcr);
DBG("end write buf %x cnt %x\n", tpr, tcr);

debug_array("end read_buf", read_buf, count);
debug_array("end write_buf", write_buf, count);

SPI_Give(dev);
}

aaarrr
Цитата(Terminator @ Jul 20 2009, 14:47) *
Неработоспособность проявляется следующим образом: данные в FPGA прилетают со сдвигом, т.е. первый байт правильный, вместо второго третий и т.п., в конце вставляется случайный байт. Иногда вместо первого байта отправляется "0".

А TPR в конце куда указывает?

И что-то странное в вашем драйвере:
Код
    // разрешаем прерывание
    dev->hw->spi->SPI_IER = AT91C_SPI_RXBUFF;


Код
static void spi_isr(tSPI *spi)
{
    ....
    unsigned int stat = spi->spi->SPI_SR & spi->spi->SPI_IMR;
    if (stat & AT91C_SPI_ENDTX)
    {
    ...
    }
    else
    {
        spi->spi->SPI_IDR = stat;
        // выключаем PDC
        spi->spi->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
        // сигналим задаче        xSemaphoreGiveFromISR(spi->sem, &res);
    }
    ...
}


Т.е. получается разрешили прерывание по приему, получили один буфер и выключили PDC?
Terminator
Цитата(aaarrr @ Jul 20 2009, 18:40) *
А TPR в конце куда указывает?

В конец буфера на передачу, как и должно быть.

Цитата
И что-то странное в вашем драйвере:
Код
    // разрешаем прерывание
                  dev->hw->spi->SPI_IER = AT91C_SPI_RXBUFF;

Код
...
                // выключаем PDC
                  spi->spi->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
                  // сигналим задаче        
                xSemaphoreGiveFromISR(spi->sem, &res);
              }
              ...
          }


Т.е. получается разрешили прерывание по приему, получили один буфер и выключили PDC?


Да. Прерывание RXBUFF случается когда оба буфера на приём заполнены.


До выше описанной проблемы, при чтение по SPI я запускал PDC на приём и передачу указывая ему один и тот же буфер. Так что функция SPI_Read_Write - это очередная проверка бредовой идеи sad.gif

Попутный вопрос. Сразу после запуска PDC я вывожу RPR, RCR, TPR, TCR и вижу что TCR уменьшился на 2 а RCR остался неизменным. У SPI есть свой FIFO? Это нормальное поведение?
Dron_Gus
На какую ширину у Вас настроен SPI? В даташите не нашел (сильно не искал), но не может это быть проблеммой с выравниванием? Тем более Вы говорите, что счетчик уменьшается на 2. Похоже ДМА пытается забрать слово с невыравненого адресса и затыкается.
Terminator
Цитата(Dron_Gus @ Jul 21 2009, 12:22) *
На какую ширину у Вас настроен SPI? В даташите не нашел (сильно не искал), но не может это быть проблеммой с выравниванием? Тем более Вы говорите, что счетчик уменьшается на 2. Похоже ДМА пытается забрать слово с невыравненого адресса и затыкается.


Весь обмен байтами. Проблем выравнивания быть не должно.
Dron_Gus
Слейва выбираете вручную? Покажите инициализацию СПИ.
Terminator
Цитата(Dron_Gus @ Jul 21 2009, 12:43) *
Слейва выбираете вручную? Покажите инициализацию СПИ.

Да, в коде функции есть SPI_CS_Sel

Код
     uint32_t ttmp = ((uint32_t)2 << 24) | ((uint32_t)2
             << 16) | AT91C_SPI_BITS_8 | 2 << 8 | AT91C_SPI_NCPHA;

     dev->hw->spi->SPI_CSR[0] = ttmp;
     dev->hw->spi->SPI_CSR[1] = ttmp;
     dev->hw->spi->SPI_CSR[2] = ttmp;
     dev->hw->spi->SPI_CSR[3] = ttmp;
    
     uint32_t d_id;
     tfunc ff;
     if (spi_num == 0)
     {
         d_id = AT91C_ID_SPI0;
         ff = SPI0_ISR;
     }
     else
     {
         d_id = AT91C_ID_SPI1;
         ff = SPI1_ISR;
     }

     // подаём тактовую на SPI
     AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << d_id);

     // подключаем ноги SPI
     // MISO вроде подключать не надо ((unsigned int) AT91C_PA16_MISO0)
     uint32_t pins;
     if (spi_num == 0)
     {
         pins = (((uint32_t)AT91C_PA16_MISO0)
                 | ((uint32_t)AT91C_PA17_MOSI0)
                 | ((uint32_t)AT91C_PA18_SPCK0));

         AT91C_BASE_PIOA->PIO_PPUER = AT91C_PA16_MISO0;
         AT91C_BASE_PIOA->PIO_ASR = pins;
     }
     else
     {
         pins = (((uint32_t)AT91C_PA24_MISO1)
                 | ((uint32_t)AT91C_PA23_MOSI1)
                 | ((uint32_t)AT91C_PA22_SPCK1));

         AT91C_BASE_PIOA->PIO_PPUER = AT91C_PA24_MISO1;
         AT91C_BASE_PIOA->PIO_BSR = pins;
     }

     AT91C_BASE_PIOA->PIO_PDR = pins;
     AT91C_BASE_PIOA->PIO_MDDR = pins;


     // конфигурим железо
     spi->spi->SPI_CR = AT91C_SPI_SWRST;

     // SPI_CS_Sel(tDevSPI *dev)
     uint32_t tmp = ~((1 << 16) << dev->cs);
     dev->hw->spi->SPI_MR = SPI_MODE | (tmp & AT91C_SPI_PCS); // set PCS
     dev->hw->cs = dev->cs;

     // SPI_Set_Speed(tDevSPI *dev)
         uint32_t tmp = ((uint32_t)period << 24) | ((uint32_t)period
             << 16) | AT91C_SPI_BITS_8 | speed << 8 | AT91C_SPI_NCPHA;

     dev->hw->spi->SPI_CSR[dev->cs] = tmp;
  
     dev->hw->spi->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
     dev->hw->spi->SPI_TNCR = 0;
     dev->hw->spi->SPI_TCR = 0;
     dev->hw->spi->SPI_RNCR = 0;
     dev->hw->spi->SPI_RCR = 0;

     // конфигурим AIC
     AT91F_AIC_ConfigureIt(d_id, interrupt_level,
             AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ff);

     AT91F_AIC_EnableIt(AT91C_BASE_AIC, d_id);

     spi->spi->SPI_IDR = 0xFFFFFFFF;
     spi->spi->SPI_CR = AT91C_SPI_SPIEN;

     SPI_CS_Sel(dev);
Dron_Gus
Функция SPI_CS_Sel(dev); не затирает битик MSTR: Master/Slave Mode?
Terminator
Цитата(Dron_Gus @ Jul 21 2009, 13:11) *
Функция SPI_CS_Sel(dev); не затирает битик MSTR: Master/Slave Mode?


Нет
Код
#define SPI_MODE    (AT91C_SPI_MSTR | AT91C_SPI_PS_FIXED | AT91C_SPI_MODFDIS)
static void
SPI_CS_Sel(tDevSPI *dev)
{
     //DBG("SPI_cs %d\n", dev->cs);
     uint32_t tmp = ~((1 << 16) << dev->cs);
     dev->hw->spi->SPI_MR = SPI_MODE | (tmp & AT91C_SPI_PCS); // set PCS
     dev->hw->cs = dev->cs;
}


Скажу ещё раз. Обмен с flash at45db321c идёт через этот же драйвер и никаких проблем не замечено.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.