Перейти к содержанию
    

SPI на BF537 + uClinux

Разбираюсь потихоньку с uClinux на BF537 (STAMP).

Никак не въеду каким образом воспользоваться существующими в uClinux драйверами SPI.

Интерфейс тривиальный, но как с ним работать из под uC - не ясно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну неужели никто SPI не заводил под uClinux'oм ?

Тут вроде спецы есть кто Blackfin'oм занимаются.

А то получается что linux запустили и все, радуемся :)...

Подскажите пожалуйста, как вы это делали.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В своё время написал свои, пользовался Даташитом и драйверами которые лежали в uC может сейчас стало получше с драйверами но тогда было мрачно :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уважаемый Mihail Gluhowchenko писал вам в личку, но видимо письмо не дошло...

Вобщем, мне пока удалось сделать драйвер символьного устройства SPI, который работает по прерываниям. Сделал очередь FIFO как на отправку так и на прием данных. Долго маялся с прерываниями пытаясь понять как правильно использовать SPIN блокировку.

Ну, да ладно, вот только частота следования пакетов данных меня не радует, всего то 340 КГц получилась, и это при максимальном битрейте. Т.е. там между посылками получается разрыв раза в 3 превышающий длительность передачи 8 бит данных.

Вот и хотелось бы посмотреть как работает встроенный в uClinux драйвер SPI, только как им правильно воспользоваться я не знаю...

Если вы такое делали, поделитесь пожалуйста или подскажите как надо оформить соответствующие секции драйвера: init, exit, ioctl, read, write...

 

С уважением, Александр...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уважаемый Mihail Gluhowchenko писал вам в личку, но видимо письмо не дошло...

Вобщем, мне пока удалось сделать драйвер символьного устройства SPI, который работает по прерываниям. Сделал очередь FIFO как на отправку так и на прием данных. Долго маялся с прерываниями пытаясь понять как правильно использовать SPIN блокировку.

Ну, да ладно, вот только частота следования пакетов данных меня не радует, всего то 340 КГц получилась, и это при максимальном битрейте. Т.е. там между посылками получается разрыв раза в 3 превышающий длительность передачи 8 бит данных.

Вот и хотелось бы посмотреть как работает встроенный в uClinux драйвер SPI, только как им правильно воспользоваться я не знаю...

Если вы такое делали, поделитесь пожалуйста или подскажите как надо оформить соответствующие секции драйвера: init, exit, ioctl, read, write...

 

С уважением, Александр...

Задача у меня была рассовывать пакетики небольшого размера по контроллерам. Неставил вообще задачу оптимизации.

Рекомендовал бы вам посмотреть в сторону DMA а именно сделать из них буфер дескрипторов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рекомендовал бы вам посмотреть в сторону DMA а именно сделать из них буфер дескрипторов.

Да, пожалуй к этому все и идет...

У меня задача - плисину прогружать Virtex4, ну и потом с ней дальше общаться.

Я просто не хочу изобретать велосипед, а нучиться "кататься" на том что уже есть. Если вы пользовали встроенные драйвера SPI, подскажите пожалуйста как вы это делалли ? Исходы ваших драйверов остались у вас ?

Подскажите пожалуйста как надо оформить соответствующие секции своего драйвера: init, exit, ioctl, read, write... с использованием SPI драйверов поставляющихся в пакете uClinux.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разбираюсь потихоньку с uClinux на BF537 (STAMP).

Никак не въеду каким образом воспользоваться существующими в uClinux драйверами SPI.

Интерфейс тривиальный, но как с ним работать из под uC - не ясно.

Пытался приспособить встроенный драйвер для непрерывного чтения с АЦП. Плюнул и написал свой через ДМА.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пытался приспособить встроенный драйвер для непрерывного чтения с АЦП. Плюнул и написал свой через ДМА.

А можно хоть одним глазком взглянуть на реализацию.

Я вот со своим драйвером все парюсь никак его корректно работать заставить не могу. Сейчас пока без DMA, реализована очередь и отправка новых данных по прерыванию. Вобщем то пока что не весело запихиваю 8 байт в очердь, наружу выплевывается только 6, да и данные там совсем не те что я отправляю. Вставлял дебаговый printk... в прерывание - он мне четко говорит то что 8 байт были отправлены.

Код драйвера выложу завтра...

Просто я только начинаю разрабатывать драйвера, до этого никогда ничего подобного под Linux писать не приходилось, и то как корректно обрабатывать прерывания я толком не знаю, в основном опираюсь на другие исходы...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А можно хоть одним глазком взглянуть на реализацию.

Это тоже мой первый драйвер, есть недостатки, но он работает. За основу взял драйвер из более старой версии uClinux, немного переделал его под свои нужды. В новых версиях uClinux уже совсем другой драйвер, который имеет мало общего со старым.

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/blackfin.h>
#include <asm/dma.h>
#include <asm/bfin5xx_spi.h>

#include <linux/spi/spi.h>

/* definitions */

#define SPI_BUF_LEN        1404
#define SPI_REGSIZE        16

#define SPI_MAJOR          252   /* experiential */
#define SPI0_MINOR         0

#define SPI_DEVNAME        "spi"
#define SPI_INT0NAME       "spiint0"  /* Should be less than 19 chars. */

#define BFIN_SPI_DEBUG  1

#ifdef BFIN_SPI_DEBUG
#define PRINTK(args...) printk(args)
#else
#define PRINTK(args...)
#endif

typedef struct Spi_Device_t
{
    int     opened;
    int     nonblock;
    int     master;
    int     bdrate;
    int     channel; /* only valid in master mode */
    int     polar;
    int     phase;
    int     outenable;
    int     irqnum;
    int     byteorder;  /* 0: MSB first; 1: LSB first; */
    int     recvopt;    /* 0: Discard packet if Rxbuffer is full;
                           1: Flush Rxbuffer if it is full; */
    uint8_t *bufptr;
    uint8_t *rxbuf;
    uint64_t count;
    uint64_t count_read;
    dma_addr_t addr;
    wait_queue_head_t* rx_wq;
}spi_device_t;

/* Globals */
/* We must declare queue structure by the following macro. 
* firstly declare 'wait_queue_head_t' and then 'init_waitqueue_head' 
* doesn't work in 2.4.7 kernel / redhat 7.2 */
static DECLARE_WAIT_QUEUE_HEAD(spirxq0);

static spi_device_t spiinfo;

#define DEFINE_SPI_REG(reg, off) \
static inline u16 read_##reg(void) \
    { return *(volatile unsigned short*)(SPI0_REGBASE + off); } \
static inline void write_##reg(u16 v) \
    {*(volatile unsigned short*)(SPI0_REGBASE + off) = v;\
    __builtin_bfin_ssync();}

DEFINE_SPI_REG(CTRL, 0x00)
DEFINE_SPI_REG(FLAG, 0x04)
DEFINE_SPI_REG(STAT, 0x08)
DEFINE_SPI_REG(TDBR, 0x0C)
DEFINE_SPI_REG(RDBR, 0x10)
DEFINE_SPI_REG(BAUD, 0x14)
DEFINE_SPI_REG(SHAW, 0x18)

#define START_STATE ((void*)0)
#define RUNNING_STATE ((void*)1)
#define DONE_STATE ((void*)2)
#define ERROR_STATE ((void*)-1)

static irqreturn_t spi_adc_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
    clear_dma_irqstat(CH_SPI);
    spiinfo.count++;
    spiinfo.recvopt = 1;
    wake_up_interruptible(&spirxq0);
    return IRQ_HANDLED;
}

static ssize_t spi_adc_read(struct file *filp, char *buf, size_t count, loff_t *pos)
{
    int ierr;

    /* Wait for data available */
    if (spiinfo.count - spiinfo.count_read <= 1)
    {
        ierr = wait_event_interruptible(spirxq0, spiinfo.recvopt);
        if (ierr)
            /* waiting is broken by a signal */
            return ierr;
    }
    else
        if (spiinfo.count - spiinfo.count_read > 30)
        {
            printk("spi_adc_read: %d\n", (uint32_t) (spiinfo.count - spiinfo.count_read));
            return -EINVAL;
        }
    spiinfo.recvopt = 0;

    spiinfo.bufptr = spiinfo.rxbuf + SPI_BUF_LEN * ( spiinfo.count_read & 0x1F );
    memmove(buf, spiinfo.bufptr, SPI_BUF_LEN);
    spiinfo.count_read++;
    return SPI_BUF_LEN;
}

static int spi_adc_ioctl(struct inode *inode, struct file *filp, uint cmd, unsigned long arg)
{
    switch (cmd)
    {
        case CMD_SPI_SET_BAUDRATE:
            write_BAUD((uint16_t) arg);
            break;
        default:
            return -EINVAL;
    }
    return 0;

}

static int spi_adc_open(struct inode *inode, struct file *filp)
{
    char intname[20];
    int minor = MINOR (inode->i_rdev);

    /* SPI 0 ? */
    if(minor != SPI0_MINOR) return -ENXIO;

    if(spiinfo.opened)
        return -EMFILE;
    
    /* Clear configuration information */
    memset(&spiinfo, 0, sizeof(spi_device_t));

    if(filp->f_flags & O_NONBLOCK)
    spiinfo.nonblock = 1;
   
    spiinfo.rx_wq = &spirxq0;
    
    spiinfo.opened = 1;
    spiinfo.phase = 1;
    spiinfo.recvopt = 0;
    spiinfo.bdrate = SPI_DEFAULT_BARD;
    spiinfo.count = 0;
    spiinfo.count_read = 0;
    
    strcpy(intname, SPI_INT0NAME);
    spiinfo.irqnum = SPI0_IRQ_NUM;

    write_STAT(BIT_STAT_CLR);
    disable_dma(CH_SPI);
    clear_dma_irqstat(CH_SPI);
    spiinfo.rxbuf = dma_alloc_coherent(NULL, SPI_BUF_LEN << 5, &spiinfo.addr, GFP_DMA);
    spiinfo.bufptr = NULL;

    bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x7c00);
    __builtin_bfin_ssync();
    bfin_write_PORTF_FER(bfin_read_PORTF_FER() & 0xFFBF);
    __builtin_bfin_ssync();
    bfin_write_PORTFIO_DIR(bfin_read_PORTFIO_DIR() | 0x0040);
    __builtin_bfin_ssync();
    /*bfin_write_PORTG_FER(bfin_read_PORTG_FER() & 0xFFCF);
    __builtin_bfin_ssync();
    bfin_write_PORTGIO_DIR(bfin_read_PORTGIO_DIR() | 0x0030);
    __builtin_bfin_ssync();
    bfin_write_PORTGIO_SET(bfin_read_PORTGIO() | 0x0030);
      __builtin_bfin_ssync();*/
    write_BAUD(256);
    write_FLAG(0xFD02);
    write_CTRL(0x0000);

    set_dma_start_addr(CH_SPI, (unsigned long) spiinfo.rxbuf);
    set_dma_x_count(CH_SPI, SPI_BUF_LEN);
    set_dma_x_modify(CH_SPI, 1);
    set_dma_y_count(CH_SPI, 32);
    set_dma_y_modify(CH_SPI, 1);
    dma_enable_irq(CH_SPI);
    set_dma_config(CH_SPI, WNR | WDSIZE_8 | DI_EN | DMAFLOW_AUTO | DI_SEL | DMA2D);
    enable_dma(CH_SPI);

    write_CTRL(CFG_SPI_DMAREAD | (CFG_SPI_ENABLE << 14));
    bfin_write_PORTFIO_SET(bfin_read_PORTFIO() | 0x0040);
    __builtin_bfin_ssync();

    return 0;
}

static int spi_adc_release (struct inode *inode, struct file *filp)
{
    write_CTRL(0x0000);
    bfin_write_PORTFIO_CLEAR(bfin_read_PORTFIO() | 0x0040);
    __builtin_bfin_ssync();
    /*bfin_write_PORTGIO_CLEAR(bfin_read_PORTGIO() | 0x0030);
      __builtin_bfin_ssync();*/

    write_STAT(BIT_STAT_CLR);
    disable_dma(CH_SPI);
    dma_disable_irq(CH_SPI);
    clear_dma_irqstat(CH_SPI);
    dma_free_coherent(NULL, SPI_BUF_LEN << 5, spiinfo.rxbuf, spiinfo.addr);

    spiinfo.opened = 0;

    return 0;
}


static struct file_operations spi_fops = {
    owner:      THIS_MODULE,
    read:       spi_adc_read,
    ioctl:    spi_adc_ioctl,
    open:       spi_adc_open,
    release:    spi_adc_release,
};


#ifdef MODULE
    int init_module(void)
#else 
    int __init spi_init(void)
#endif /* MODULE */
    {
        int result;

        printk("spi0: INIT\n");
        result = register_chrdev(SPI_MAJOR, "spi", &spi_fops);
        if (result < 0) 
        {
        printk(KERN_WARNING "spi0: can't get minor %d\n", SPI_MAJOR);
        return result;
        }

        if(request_dma(CH_SPI, "BF53x_SPI_DMA") < 0)
        {
            PRINTK("Unable to request BlackFin SPI DMA channel\n");
            return -ENODEV;
        }

        set_dma_callback(CH_SPI, (void*)spi_adc_dma_irq_handler, &spiinfo);
        dma_disable_irq(CH_SPI);

        return 0;
    }   

#ifdef MODULE
    void cleanup_module(void)
#else
    void spi_uninit(void)
#endif /* MODULE */
    {
        unregister_chrdev(SPI_MAJOR, SPI_DEVNAME);
        printk("<1>Goodbye spi \n");
    }

module_init(spi_init);
module_exit(spi_uninit);

Изменено пользователем Kekek

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Большое спасибо, буду разбираться.

Вот то, что у меня сейчас получилось.

Драйвер SPI для режима master/slave, поддерживается передача данных как 8, так и 16 битными словами.

bfin_spi.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...