Dubov 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 (изменено) · Жалоба Стало необходимо написать свой драйвер SPI устройства. Драйвер для работы из пользовательского пространства не годится, так как данные нужно принимать от АЦП по SPI в DMA буфер непрерывно(пропуски в данных недопускаются) со скоростью 100К семплов в секунду, чего нельзя добится из user space. Из пользовательского пространства я планирую только забирать блоками через драйвер. Для моей платформы есть файлы для работы с spi в папке /drivers/spi/ Вопрос: файлы в /drivers/spi - это всё что нужно для написания своего драйвера(модуля ядра)? Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные). В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу). Изменено 12 ноября, 2012 пользователем Dubov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Idle 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные). Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом. В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу). Они как правило есть, но не всегда на _все_ регистры. Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xor.kruger 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers. Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом. Они как правило есть, но не всегда на _все_ регистры. Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера). так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write(). Таким образом считать данные из ацп в пространстве пользователя нельзя, точнее можно, но данные будут не нерпрерывные, а спотреями, ввиду латентности операционки. Выход: написать драйвер "с нуля" на уровне ядра. как? пока сложно себе представляю... увидеть бы пример работы с SPI через DMA в драйвере. Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers. Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link подсистема iio - мутная вещь. По моему намного проще обращаться к регистрам как в standalone приложении. Как раз для SAM9260 писал именно "напрямую" через SPI DMA, взяв пример кода из проекта плейера (тоже без ос). Получилось просто и быстро: standalone процедура превратилась в драйвер уровня ядра. Тем более, в книге Linux Device drivers написано что на уровне ядра к регистрам можно обращаться напрямую. IIO выдумали якобы для унифицирования, но мне показалось там слишком всё усложнено.Тем более из общения с разработчиками драйверов из AD я так и не смог выяснить зачем они написали софтовое тактирование АЦП из юзерспейс. Это же идиотизм: тактировать промышленный АЦП клоком с непредсказуемым джиттером. Отвлёкся от темы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Idle 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 (изменено) · Жалоба так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write() а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит? Изменено 12 ноября, 2012 пользователем Idle Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит? Вообще планировалось сделать так: завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер). В приложении же отобразить буфер в юзерспейс и читать указатель, который возвращает драйвер. Иными словами: User mode SPI device driver support - не подходит. P.S. Возможно я чего-то не понимаю. Все реализации что я до этого видел предлагают править файл борды и активировать User mode SPI device driver support. Но это не подходит. а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит? и это как я понял, не DMA чтение Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Idle 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба Вообще планировалось сделать так: завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер). тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер создать в драйвере chardev и отдавать буфер при чтении из созданного файла как такой вариант? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 · Жалоба тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер создать в драйвере chardev и отдавать буфер при чтении из созданного файла как такой вариант?и(получается никаких spi_write()). Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра). Все примеры и статьи в интеренете только для вариантов когда пофиг когда данные придут. А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс. Со вторым я понимаю как разобраться... с первым - засада. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Idle 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 (изменено) · Жалоба Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра). нет, пишу как я бы это делал А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс. ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании драйвера? upd я понял - вы хотите зарядить dma на чтение по spi :) тогда линуксовая подсистема spi тут не нужна совсем, смотрите на реализацию мастера для своего SoC и пишите что нужно работа с регистрами происходит так же как и на bare metal - они в памяти работа с dma - через линуксовый api, с ним мне работать не приходилось Изменено 12 ноября, 2012 пользователем Idle Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 12 ноября, 2012 Опубликовано 12 ноября, 2012 (изменено) · Жалоба ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps. В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести. Как вариант, можно на ПЛИС сделать буфер, чтобы просто из юзерспейс забирать даные, но это за рамками топика. Вот и пытаюсь: 1) найти примеры для OMAP DMA SPI без ОС (standalone), чтобы оформить как модуль ядра. либо 2) какие-то средства линукса (api для DMA на уровне ядра) работа с регистрами происходит так же как и на bare metal - они в памяти я пока даже *.h-файл описания регистров для OMAP не нашёл. Настолько всё запутано в исходниках. Эх, у Atmela в этом было много проще и нагляднее (AT91SAM9260.h - и всё) Изменено 13 ноября, 2012 пользователем Dubov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alx2 0 13 ноября, 2012 Опубликовано 13 ноября, 2012 · Жалоба Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps. В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести. Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 13 ноября, 2012 Опубликовано 13 ноября, 2012 (изменено) · Жалоба Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой... хорошо, чтение - это просто метод забрать данные. Мне нужен метод положить данные в буфер. Причём в большой буфер. пока нашёл вот что: spi-omap2-mcspi.c как настроить на приём по DMA ума не приложу( Изменено 13 ноября, 2012 пользователем Dubov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Idle 0 13 ноября, 2012 Опубликовано 13 ноября, 2012 · Жалоба Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой... ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс программно через таймеры или delayed work это не получится т.к. слишком часто вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Dubov 0 13 ноября, 2012 Опубликовано 13 ноября, 2012 (изменено) · Жалоба ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс программно через таймеры или delayed work это не получится т.к. слишком часто вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды Именно так. Например подобное делал для SAM9, взяв пример из проекта wav-плейера. Там по DMA с флешки по SPI считывание происходило. //------------------------------------------------------------------------------ /// 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 драйверах всё так сложно? никогда не увидишь имён регистров прямых, всё через структуры структур структур и т.д. ? безопасность? универсальность? Изменено 13 ноября, 2012 пользователем Dubov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vshemm 0 13 ноября, 2012 Опубликовано 13 ноября, 2012 · Жалоба Простейший способ "в лоб" - это char device, внутри которого 2 буфера, в один пишем, другой готов к чтению из приложения через read(). При заполнении одного буфера DMA перезапускается на другой буфер. Либо "дмашить" в кольцевой буфер. Как сделать лучше - см. спеки на железо. Есть нюансы :) Во-первых, вам нужно перезапустить DMA за 10мкс (по прерыванию, например). Не факт, что вы успеете, т.к. если обращение к регистру занимает (к примеру) 1мкс, а вам нужно перезаписать 5шт - это уже 10мкс. Некоторые DMA умны настолько, что могут сами брать дескрипторы из памяти и закольцовываться по их списку. В этом случае перезапускать DMA нет необходимости. Если это не так и терять данные нельзя вообще - то вы близки к теоретическому пределу железа - опять же, см. спеки на железо. Во-вторых, при использовании своего char dev нужно убедиться, что никто через линуксовый SPI-фреймворк не обращается к SPI контроллеру (и через DMA фреймворк тоже). Что касается "сложности" драйверов в linux - это нормально :) На самом деле там все довольно просто - почти все разбито на слои и фреймворки для облегчения портируемости и расширения. Фактически, там используется ООП подход, со всеми вытекающими. В итоге вход в это хозяйство усложняется (и кажется странным для людей работавших до этого с МК осями/bare metal), но иначе никак. Вообще, такой подход характерен для всех не-tiny мультиплатформенных ОС. P.S. Есть фреймворк comedi, все еще стандарт де-факто для data acquisition. Но 100К семплов/с - немалая величина, и с ней оверхед фреймворка может стать неприемлемым. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться