Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: AT91SAM960 - не работает SPI
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Linux
Pasa
Процессор AT91SAM9260

Из документации ядра 2.6.28 взял пример работы с SPI - spidev_test.c
В конфигурации ядра сделал все как описывали на форуме. Все подключилось.
В /dev появилось устройство spidev. Пример заработал - обращения к драйверу проходят.
Передача вроде должна идти.А вот данных осцилографом не вижу.

Распечатал на консоль значения портов и регистров SPI - режимы вроде установлены верно.
А передачи нет. При этом датафлэш висящая на SPI работает отлично, и когда идет
обращение к ней, то осцилографом все прекрасно видно.

Подскажите в чем тут загвоздка?
vmp
Нет только данных или тактов тоже?
Pasa
Цитата(vmp @ Nov 21 2009, 13:27) *
Нет только данных или тактов тоже?


ни тактов ни данных. А вот при установке режима чипселект реагирует:

1 вариант: mode =0;

2 вариант: mode |= SPI_CS_HIGH;

С разработчиком схемы сидели вдвоем и смотрели установленные биты в регистрах портов и SPI.....все вроде нормально


-------------------------------------------------------------------------------
{
mode =0;
bits = 8;
speed = 400000;
delay=10;


fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");

/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");

ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");

/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");

ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");

/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");

ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");

printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);


transfer(fd);
}

нигде по ошибке не выходит, я так понимаю что с драйвером общение нормально идет

----------------------------------------------------------------------
и transfer отрабатывает нормально....ошибку не пишет

static void transfer(int fd)
{
int ret;
uint8_t tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE(tx)] = {0, };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};

for (ret = 0; ret < ARRAY_SIZE(tx); ret++)
rx[ret] = 1;


ret = ioctl(fd, SPI_IOC_MESSAGE(10), &tr);
if (ret == 1)
pabort("can't send spi message");

for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}







Как был приемный буфер заполнен моими 1 , так и остается с 1....ну понятно....реального обмена то нет....А флэшка работает и посмеивается надо мной....Я в тупике....
vmp
Тут уже ничего не подскажу - проблема явно не с железом, а с софтом, так что надо пытать специалистов по линуксу.
Pasa
Может кто подскажет точку входа и в каком файле искать

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

попробую лог на консоль....черз printk.....вдруг поможет
Pasa
Эксперименты привели к следующему: вызовы write и read прекрасно работают. А если через ioctl вызывать spidev_ioctl(для полнодуплексного обмена), то из драйвера SPI (atmel_spi.c) идет возврат с ошибкой опций устновленного протокола. И естественно передачи никакой нет. По исходнику посмотрел - там где анализ на нулевую скорость передачи и ноль бит в слове. В уровень spidev.c структура с параметрами доходит нормально(параметры ставятся в верхнем юзерском слое в самой проге). А потом где-то теряются/затираются/обнуляются и до дравера SPI не доходят. Вот счас пробую проследить всю цепочку...

Мда....вот уж не ожидал таких плясок с бубном....не покидает ощущение, что ну не может быть таких ошибок в ядре и я не совсем правильно пользуюсь вызовами ioctl(). Но ведь все взято из примера в документации которая идет с самим ядром. Хотя с другой стороны в этом самом примере сходу нашел одну описку/опечатку/ошибку.

Может кто сталкивался с таким и подскажет что-нибудь дельное? Уж больно не хочется лезть в потроха ядра, править его и с ужасом думать о следущих "танцах", когда придется браться за сокеты и шину I2C
lamination
Код
#include <stdio.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

int main(){
    
    int fd;
    int status;
    unsigned char u8_ret;
    unsigned int u32_ret;
    struct spi_ioc_transfer    xfer[2];
    unsigned char buf[10];
    
    fd = open("/dev/spi1.1",O_RDWR);
    if (fd == -1){
        printf("error on open device,%d\n",fd);
        return -1;
    }
    
    status = ioctl(fd,SPI_IOC_RD_MODE,&u8_ret);
    printf("RD_MODE(%d) :0x%x,",status,u8_ret);
    status = ioctl(fd,SPI_IOC_RD_LSB_FIRST,&u8_ret);
    printf("LSB_FIRST(%d) :0x%x,",status,u8_ret);
    status = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD,&u8_ret);
    printf("BITS_PER_WORD(%d) :0x%x,",status,u8_ret);
    status = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ,&u32_ret);
    printf("MAX_SPEED_HZ(%d) :0x%x\n",status,u32_ret);
    
    u8_ret = 8;
    status = ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&u8_ret);
    printf("set BITS_PER_WORD(%d) :0x%x\n",status,u8_ret);

    u32_ret = 1000000;
    status = ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ,&u32_ret);
    printf("set MAX_SPEED_HZ(%d) :0x%x\n",status,u32_ret);    
    
    memset(xfer, 0, sizeof xfer);
    memset(buf, 0, sizeof buf);     
    
    buf[0] = 0xaa;
    xfer[0].tx_buf = (__u64) buf;
    xfer[0].len = 1;
    
    xfer[1].rx_buf = (__u64) buf;
    xfer[1].len = 4;     

    status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer);
    printf("ioctl return %d\n",status);
    printf("buf[0]=%d, buf[1]=%d, buf[2]=%d\n",buf[0],buf[1],buf[2]);
    
    return 0;    
}


В board-sam9260ek.c :
Код
.....
static struct spi_board_info ek_spi_devices[] = {
....
    {    /* SPI device 1,CS 0*/
        .modalias    = "spidev",
        .chip_select    = 0,
        .max_speed_hz    = 15 * 1000 * 1000,
        .bus_num    = 1,
    },    
    {    /* SPI device 1, CS 1*/
        .modalias    = "spidev",
        .chip_select    = 1,
        .max_speed_hz    = 15 * 1000 * 1000,
        .bus_num    = 1,
    },    
    {    /* SPI device 1, CS 2*/
        .modalias    = "spidev",
        .chip_select    = 2,
        .max_speed_hz    = 15 * 1000 * 1000,
        .bus_num    = 1,
    },    
    {    /* SPI device 1, CS 3*/
        .modalias    = "spidev",
        .chip_select    = 3,
        .max_speed_hz    = 15 * 1000 * 1000,
        .bus_num    = 1,
    },        
....
}


Ядро версии 2.6.30

Если работают read и write, то вероятней всего вы забыли сделать memset(xfer, 0, sizeof xfer);
Pasa
to lamination:

Ваш пример сразу заработал. Огромное спасибо.

В примере из документации по ядру main() организована по другому. Хотя там структура на передачу вроде инициализируется.
Попробую найти где же там допущены неточности из-за которых видимо этот пример и не работает. Вообщем даже интерсно....покопаюсь.

Еще раз огромное спасибо. Похоже дело сдвинулось с мертвой точки
Pasa
Итак, небольшое резюме по итогам работы с SPI для AT91SAM9260(ядро 2.6.28):

Начну с конца, с драйвера шины SPI (файл atmel_spi.c)
В функции atmel_spi_transfer() есть такой код:

if (xfer->bits_per_word || xfer->speed_hz) {

dev_dbg(&spi->dev, "no protocol options yet\n");
return -ENOPROTOOPT;
}

xfer->bits_per_word и xfer->speed_hz собственно и есть те самые параметры, которые
передаются в драйвер из пользовательского приложения, где инциализируется
структура spi_ioc_transfer. Если эти параметры не обнулить(всю структуру нулить не
обязательно, достаточно только этих двух) то мы и нарываемся на "отлуп" в виде
"no protocol options yet". Для чего это сделано я не разбирался. Вроде логичным было-бы
сделать наооборот - считать ошибкой когда параметры передачи не установлены или сброшены в ноль.
Ведь эти самые структуры spi_ioc_transfer можно паковать в массивы и подсовывать драйверу
для передачи через вызов ioctl(fd, SPI_IOC_MESSAGE(кол-во мессаг), адрес_массива_структур);
При этом была бы теоретическая возможность каждую мессагу слать с собственными параметрами
передачи(понятно, что для этого нужно было-бы драйверу перепрограммировать аппаратные регистры
контроллера SPI). Возможно это невозвожно(пардон за тавталогию) или есть какие-либо причины мне неизвестные.
А может я просто до конца не разобрался. Но сделано так, как сделано.

Поэтому всем, кто как и я будет юзать пример работы с SPI, взятый из документации ядра, файл spidev_test.c:
перед вызовом
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

сделайте как минимум
tr.speed_hz = 0;
tr.bits_per_word = 0;

Без этого будете получать "no protocol options yet" .

И еще одна описка/опечатка:
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret == 1)
pabort("can't send spi message");

Привильно будет
if (ret < 0)
т.к. в ret при успешной передаче будет возвращаться ОБЩЕЕ число переданных/принятых
байт во ВСЕЙ пачке(массиве) структур, которые мы подготовили к моменту вызова

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); // здесь передается ОДНО сообщение по SPI

Если сделать ret = ioctl(fd, SPI_IOC_MESSAGE(5), array_st_mess_spi) - то будет передано
5 сообщений из массива структур array_st_mess_spi, которые мы должны предварительно подготовить,
а именно - проинициализировать указатели на приемо/передающие буфера, длину передачи и ОБНУЛИТЬ
speed_hz и bits_per_word. И в ret вернется ОБЩЕЕ число переданных байт всех 5 посылок.

В случае неудачи у меня было ret < 0 .

Вот собственно и все. На сегодняшний момент SPI работает отлично, никаких проблем отмечено пока не было.
Все вышесказанное сугубо ИМХО, если ошибся где - поправьте. Надеюсь это поможет остальным избежать граблей,
на которые наступил я.

Еще раз всем спасибо за помощь и внимание.
coolzero
Добрый день. я новичёк в линуксе, начал разбираться с платой SK-AT91SAM9XE512.

"Из документации ядра 2.6.28 взял пример работы с SPI - spidev_test.c
В конфигурации ядра сделал все как описывали на форуме. Все подключилось."

Не подскажите попобробнее, как работать с примером spidev_test ?
Может ткнёте, а где описывалось на форуме , чтобы всё подключилось ?
Что надо сделать, чтобы в /dev появились файлы у-в spi ?

Пробовал создать - mknod /dev/spi0 c 153 0 , что то не очень создаются ...
Пробовал скомпилить spidev_test - на выходе a.out , загружаю на плату
линукс запускать его не хочет ...
A.t.a.m.a.n
Цитата(Pasa @ Nov 27 2009, 18:58) *
Итак, небольшое резюме по итогам работы с SPI для AT91SAM9260(ядро 2.6.28):

Начну с конца, с драйвера шины SPI (файл atmel_spi.c)
В функции atmel_spi_transfer() есть такой код:

if (xfer->bits_per_word || xfer->speed_hz) {

dev_dbg(&spi->dev, "no protocol options yet\n");
return -ENOPROTOOPT;
}
xfer->bits_per_word и xfer->speed_hz собственно и есть те самые параметры, которые
передаются в драйвер из пользовательского приложения, где инциализируется
структура spi_ioc_transfer. Если эти параметры не обнулить(всю структуру нулить не
обязательно, достаточно только этих двух) то мы и нарываемся на "отлуп" в виде
"no protocol options yet".


Обновим тему.
Я юзаю плату Atmel NGW100, Linux версия 2.6.27.6
Там код atmel_spi.c отличается, но всё равно есть ошибки.

Если в spidev_test.c оставить следующий код как есть
tr.speed_hz = speed;
tr.bits_per_word = bits;
При bits = 16, передача/приём у меня проходили без ошибок, но первые 4 бита каждого 2-го байта (элемента массива) в приёмном буфере оставались нулевыми !

если обнулить переменные
tr.speed_hz = 0;
tr.bits_per_word = 0;
код отрабатывает как ожидалось

Кроме этого если использовать буфер с 2х байтными данными то необходимо откорректировать строчку
tr.len = ARRAY_SIZE(tx) * sizeof(tx[0]);

Вобщем достаточно глюкавый пример и/или интрефейс.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.