реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Драйвер, написать самому
Dubov
сообщение Nov 12 2012, 10:21
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Стало необходимо написать свой драйвер SPI устройства. Драйвер для работы из пользовательского пространства не годится, так как данные нужно принимать от АЦП по SPI в DMA буфер непрерывно(пропуски в данных недопускаются) со скоростью 100К семплов в секунду, чего нельзя добится из user space. Из пользовательского пространства я планирую только забирать блоками через драйвер.
Для моей платформы есть файлы для работы с spi в папке /drivers/spi/

Вопрос:
файлы в /drivers/spi - это всё что нужно для написания своего драйвера(модуля ядра)?

Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные).
В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу).

Сообщение отредактировал Dubov - Nov 12 2012, 10:27
Go to the top of the page
 
+Quote Post
Idle
сообщение Nov 12 2012, 11:20
Сообщение #2


Местный
***

Группа: Участник
Сообщений: 351
Регистрация: 5-04-05
Пользователь №: 3 874



Цитата(Dubov @ Nov 12 2012, 14:21) *
Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные).

Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом.

Цитата(Dubov @ Nov 12 2012, 14:21) *
В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу).

Они как правило есть, но не всегда на _все_ регистры.

Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера).
Go to the top of the page
 
+Quote Post
xor.kruger
сообщение Nov 12 2012, 11:54
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 290
Регистрация: 17-08-08
Из: Чернигов
Пользователь №: 39 647



Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers.
Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 12 2012, 12:46
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(Idle @ Nov 12 2012, 15:20) *
Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом.


Они как правило есть, но не всегда на _все_ регистры.

Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера).

так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write(). Таким образом считать данные из ацп в пространстве пользователя нельзя, точнее можно, но данные будут не нерпрерывные, а спотреями, ввиду латентности операционки.

Выход: написать драйвер "с нуля" на уровне ядра.
как? пока сложно себе представляю... увидеть бы пример работы с SPI через DMA в драйвере.

Цитата(xor.kruger @ Nov 12 2012, 15:54) *
Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers.
Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link

подсистема iio - мутная вещь. По моему намного проще обращаться к регистрам как в standalone приложении. Как раз для SAM9260 писал именно "напрямую" через SPI DMA, взяв пример кода из проекта плейера (тоже без ос).
Получилось просто и быстро: standalone процедура превратилась в драйвер уровня ядра. Тем более, в книге Linux Device drivers написано что на уровне ядра к регистрам можно обращаться напрямую.

IIO выдумали якобы для унифицирования, но мне показалось там слишком всё усложнено.Тем более из общения с разработчиками драйверов из AD я так и не смог выяснить зачем они написали софтовое тактирование АЦП из юзерспейс. Это же идиотизм: тактировать промышленный АЦП клоком с непредсказуемым джиттером.

Отвлёкся от темы.
Go to the top of the page
 
+Quote Post
Idle
сообщение Nov 12 2012, 13:24
Сообщение #5


Местный
***

Группа: Участник
Сообщений: 351
Регистрация: 5-04-05
Пользователь №: 3 874



Цитата(Dubov @ Nov 12 2012, 16:46) *
так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write()

а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?

Сообщение отредактировал Idle - Nov 12 2012, 13:25
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 12 2012, 13:39
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(Idle @ Nov 12 2012, 17:24) *
а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?

Вообще планировалось сделать так:
завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер).
В приложении же отобразить буфер в юзерспейс и читать указатель, который возвращает драйвер.

Иными словами: User mode SPI device driver support - не подходит.

P.S. Возможно я чего-то не понимаю.

Все реализации что я до этого видел предлагают править файл борды и активировать User mode SPI device driver support. Но это не подходит.

Цитата(Idle @ Nov 12 2012, 17:24) *
а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?

и это как я понял, не DMA чтение
Go to the top of the page
 
+Quote Post
Idle
сообщение Nov 12 2012, 14:04
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 351
Регистрация: 5-04-05
Пользователь №: 3 874



Цитата(Dubov @ Nov 12 2012, 17:39) *
Вообще планировалось сделать так:
завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер).

тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер
создать в драйвере chardev и отдавать буфер при чтении из созданного файла
как такой вариант?
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 12 2012, 14:56
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



[quote name='Idle' date='Nov 12 2012, 18:04' post='1110736']
тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер
создать в драйвере chardev и отдавать буфер при чтении из созданного файла
как такой вариант?и(получается никаких spi_write()).
Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра).

Все примеры и статьи в интеренете только для вариантов когда пофиг когда данные придут. А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс. Со вторым я понимаю как разобраться... с первым - засада.
Go to the top of the page
 
+Quote Post
Idle
сообщение Nov 12 2012, 15:25
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 351
Регистрация: 5-04-05
Пользователь №: 3 874



Цитата(Dubov @ Nov 12 2012, 18:56) *
Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра).

нет, пишу как я бы это делал

Цитата(Dubov @ Nov 12 2012, 18:56) *
А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс.

ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании драйвера?


upd
я понял - вы хотите зарядить dma на чтение по spi sm.gif
тогда линуксовая подсистема spi тут не нужна совсем, смотрите на реализацию мастера для своего SoC и пишите что нужно
работа с регистрами происходит так же как и на bare metal - они в памяти
работа с dma - через линуксовый api, с ним мне работать не приходилось

Сообщение отредактировал Idle - Nov 12 2012, 15:26
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 12 2012, 19:17
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(Idle @ Nov 12 2012, 19:25) *
ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании


Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps.
В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести.

Как вариант, можно на ПЛИС сделать буфер, чтобы просто из юзерспейс забирать даные, но это за рамками топика.

Вот и пытаюсь:

1) найти примеры для OMAP DMA SPI без ОС (standalone), чтобы оформить как модуль ядра.
либо
2) какие-то средства линукса (api для DMA на уровне ядра)

Цитата(Idle @ Nov 12 2012, 19:25) *
работа с регистрами происходит так же как и на bare metal - они в памяти

я пока даже *.h-файл описания регистров для OMAP не нашёл. Настолько всё запутано в исходниках.
Эх, у Atmela в этом было много проще и нагляднее (AT91SAM9260.h - и всё)

Сообщение отредактировал Dubov - Nov 13 2012, 10:26
Go to the top of the page
 
+Quote Post
alx2
сообщение Nov 13 2012, 12:03
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091



Цитата(Dubov @ Nov 13 2012, 00:17) *
Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps.
В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести.

Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...


--------------------
Всего наилучшего,
Alex Mogilnikov
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 13 2012, 12:08
Сообщение #12


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(alx2 @ Nov 13 2012, 16:03) *
Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...

хорошо, чтение - это просто метод забрать данные.

Мне нужен метод положить данные в буфер. Причём в большой буфер.
пока нашёл вот что: spi-omap2-mcspi.c

как настроить на приём по DMA ума не приложу(

Сообщение отредактировал Dubov - Nov 13 2012, 12:09
Go to the top of the page
 
+Quote Post
Idle
сообщение Nov 13 2012, 16:14
Сообщение #13


Местный
***

Группа: Участник
Сообщений: 351
Регистрация: 5-04-05
Пользователь №: 3 874



Цитата(alx2 @ Nov 13 2012, 16:03) *
Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...

ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс
программно через таймеры или delayed work это не получится т.к. слишком часто
вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды
Go to the top of the page
 
+Quote Post
Dubov
сообщение Nov 13 2012, 17:59
Сообщение #14


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(Idle @ Nov 13 2012, 20:14) *
ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс
программно через таймеры или delayed work это не получится т.к. слишком часто
вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды

Именно так.
Например подобное делал для SAM9, взяв пример из проекта wav-плейера. Там по DMA с флешки по SPI считывание происходило.

CODE
//------------------------------------------------------------------------------
/// Use PDC for SPI data transfer.
/// Return 0 if no error, otherwise return error status.
/// \param pSdSpi Pointer to a SdSpi instance.
/// \param pData Data pointer.
/// \param size Data transfer byte count.
//------------------------------------------------------------------------------
unsigned char SDSPI_PDC_read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
AT91PS_SPI pSpiHw = pSdSpi->pSpiHw;
unsigned int spiIer;

// Enable the SPI clock
AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId);

// Disable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;

// Receive Pointer Register
pSpiHw->SPI_RPR = (unsigned long)pData;
// Receive Counter Register
pSpiHw->SPI_RCR = size;
// Transmit Pointer Register
pSpiHw->SPI_TPR = (unsigned long)dummy_ff_block;
// Transmit Counter Register
pSpiHw->SPI_TCR = size;

// spiIer = AT91C_SPI_RXBUFF;

// Enable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

while(! (pSpiHw->AT91C_SPI_SR & AT91C_SPI_ENDRX));
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_RXTDIS;
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_TXTDIS;
// Interrupt enable shall be done after PDC TXTEN and RXTEN
// pSpiHw->SPI_IER = spiIer;

return 0;
}


вобщем там свелось всё к умелому обращение с регистрами SPI_RPR, SPI_RCR, SPI_TPR, SPI_TCR.
Для OMAP не думаю что сложнее, но, зараза, не могу понять...


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

Сообщение отредактировал Dubov - Nov 13 2012, 18:01
Go to the top of the page
 
+Quote Post
vshemm
сообщение Nov 13 2012, 20:00
Сообщение #15


Частый гость
**

Группа: Участник
Сообщений: 167
Регистрация: 15-08-07
Пользователь №: 29 803



Простейший способ "в лоб" - это char device, внутри которого 2 буфера, в один пишем, другой готов к чтению
из приложения через read(). При заполнении одного буфера DMA перезапускается на другой буфер. Либо "дмашить"
в кольцевой буфер. Как сделать лучше - см. спеки на железо.

Есть нюансы sm.gif Во-первых, вам нужно перезапустить DMA за 10мкс (по прерыванию, например). Не факт, что вы
успеете, т.к. если обращение к регистру занимает (к примеру) 1мкс, а вам нужно перезаписать 5шт - это уже 10мкс.
Некоторые DMA умны настолько, что могут сами брать дескрипторы из памяти и закольцовываться по их списку.
В этом случае перезапускать DMA нет необходимости. Если это не так и терять данные нельзя вообще - то вы
близки к теоретическому пределу железа - опять же, см. спеки на железо.
Во-вторых, при использовании своего char dev нужно убедиться, что никто через линуксовый SPI-фреймворк не
обращается к SPI контроллеру (и через DMA фреймворк тоже).

Что касается "сложности" драйверов в linux - это нормально sm.gif На самом деле там все довольно просто - почти
все разбито на слои и фреймворки для облегчения портируемости и расширения. Фактически, там используется ООП
подход, со всеми вытекающими. В итоге вход в это хозяйство усложняется (и кажется странным для людей
работавших до этого с МК осями/bare metal), но иначе никак. Вообще, такой подход характерен для всех
не-tiny мультиплатформенных ОС.

P.S. Есть фреймворк comedi, все еще стандарт де-факто для data acquisition. Но 100К семплов/с - немалая величина,
и с ней оверхед фреймворка может стать неприемлемым.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd June 2025 - 05:42
Рейтинг@Mail.ru


Страница сгенерированна за 0.01508 секунд с 7
ELECTRONIX ©2004-2016