Pull to refresh

Comments 24

Столы принесли, стулья поставили, салфетки раздали... А обед-то где?
Тьфу, драйвер-то как написать?

Я начинал так — смотрел на соседние драйвера и писал по аналогии =)

Как-то уж очень абстрактно и категорично...

Во-первых это не туториал явно. Здесь нет ничего о том, как получить конкретный результат.

Во-вторых заявления про "адекватность" драйвера, "должен быть интерфейс командной строки", они кхм... Смелые, наверное. Особенно учитывая расплывчатость термина. Кусок кода для AVR это тоже вполне себе драйвер (прослойка/абстракция между железкой и остальным кодом, даже без ОС), а там память то нужно и на что-то еще оставить, кроме драйвера. Да и UART отладочный один, и управляется он совсем в другом месте.

Во-вторых заявления про "адекватность" драйвера, "должен быть интерфейс командной строки", они кхм... Смелые, наверное. Особенно учитывая расплывчатость термина.

Вы не понимаете что такое интерфейс командной строки? Вот пояснительная записка
https://habr.com/ru/articles/694408/

Выше народ написал, что в статье не указано, как драйвер писать. Не согласен с этим. Как писать логику драйвера или запись spi регистров тут не написано, да. Но это и не драйвер. А вот как организовать нормальный, тестируемый и поддерживаемый HAL примерно объяснили.

Единственное, что слегка огорчает, это зацикленности на CLI отладке. В статье не расписано, как отлаживать драйверы периферии с жёстким таймингом, хотя легко можно было бы указать, что тот же blob регистров и счётчики для разной перифирии, чувствительной к таймингом, можно лить в комп непрерывно usart/swo/USB и разбирать уже там.

Судя по тексту статьи, драйвер предлагается писать только в прерываниях, потому что ни pool'ер, ни поток для FreeRTOS не упоминаются. Тоже не очень хорошо, вынуждает логику состояний держать в прерываниях или в пользовательском коде. Но для простых периферий в самый раз.

Если бы статья называлась "Архитектура хорошо поддерживаемого драйвера" - вопросов бы не возникло.

"Если бы статья называлась "Архитектура хорошо поддерживаемого драйвера" - вопросов бы не возникло." [2]
Предлагаю @aabzelпереименовать.

Текст выглядит обобщением конкретного опыта на глобальный уровень. Но я смотрю со своей колокольни, я писал драйвера для I2C/SPI/8 bit parallel дисплеев. Формально подходит под заголовок статьи, но там:

  • тяжело покрыть тестами, поскольку неработающий дисплей видно глазами, а вебкамеру для контроля теста не поставишь;

  • не особо требуется перечисление типов;

  • нет прерываний (хотя, такие дисплеи тоже сущестуют);

  • сырые регистры вообще непонятно зачем знать;

  • непонятно как и зачем делать "интерфейс командной строки";

  • хотел бы я знать "механизм проверки линка", когда забыли подать питание на подсветку дисплея;

    Вообще-то, разные классы драйверов обладают множеством сходных свойств внутри класса и различных между ними, а, значит, в первую очередь, важен общий API класса драйвера.

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


Очень просто.
Отобразить на дисплее qr код или DataMatrix Code и прочитать его модулем считывания qr кодов c UART интерфейсом, типа этого.

Если установленный код на графическом дисплее совпал с прочитанным из UART модуля, значит драйвер графического дисплея работает. Цена вопроса 2000руб.

Если подходить как к чужому драйверу - в свой проект драйвер из десяти файлов даже еще и с собственным mk файлом я бы не добавил. Слишком уж он связан с чужой программой и системой сборки.

Если же рассматривать статью "как я для наших проектов пишу драйвер для очередного чипа" - всё более-менее ок, единственное что не понравилось - много возможностей для опечаток при копипасте. Я предпочитаю за счет макропроцессора делать как-то так:

DECLARE_ENUM(DacLevel, 
 V_SET(DAC_LEV_CTRL_INTERNALY, 0x01)
 ...
 )

и это объявление сразу и объявляет enum и задает преобразование в строку (параметр типа DacLevel будет отображаться в гуи не как число 1, а сразу как "DAC_LEV_CTRL_INTERNALY") и соответствующие команды для его установки.

 в свой проект драйвер из десяти файлов ..... я бы не добавил. 

Делать отдельный *.h файл для перечисления типов данных это не блажь.

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

даже еще и с собственным mk файлом я бы не добавил

Чем Вам make - файл не угодил? Пусть лежит. Когда-н пригодится.

По программировав немного для pi pico проникся CMake. И синтаксис понятнее, чем у make и внешнюю библиотеку( тот же драйвер), можно подключить, указав ссылку на репозиторий.

CMake не настолько гибкий как make.

у меня через CMake собирается проект для нордиков на основе Zephyr (Visual Studio + VisualGDB плагин). Все бы ничего, кроме того что CMake скрипт генерируется автоматически и ему крайне не нравилось что имя проекта начиналось с символа a. Настолько не нравилось, что аж собирать ничего не хотел. А все потому что путь "../appCore/" он воспринимал как строку со служебным символом /a . Указывались бы пути в Windows как в линуксе, проблем бы не было.

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

Решение через VS + VisualGDB простое и работает хорошо (инструкция простая: nrf5340), нюансы работы связаны скорее с системой сборки самого зефира - ninja. Насколько я понял, именно ниндзя через питоновские скрипты формирует cmake файлы и дерево проекта.

Зато имеем IDE с адекватной подсветкой синтаксиса и нормальным автодополнением, плюс конфигурация проекта через удобные UI (а при первом запуске попробуй найди все нужные опции KConfig, если они разбросаны по десятку отдельных файлов). Плагин так же следит за репозиторием и всегда есть возможность легким кликом мыши переключится на другую версию SDK. Ну а с путями, достаточно было для критических директорий просто добавить символ '_' перед именем, аки в линуксе).

В общем на вкус и цвет все фломастеры разные)

Насколько я понял, именно ниндзя через питоновские скрипты формирует cmake файлы и дерево проекта.

Наоборот, cmake создаёт файлы для ninja. А .py скрипты создают всякие промежуточные target'ы для cmake

Обратите внимание как классно оформлена документация у Analog Device.
Детализация битовых полей в виде "облачков". Читается как комиксы.
Их технический писатель просто гений.

Я бы еще добавил что для каждого программного компонента стоит добавлять Kconfig файл. Это классический способоп передавать конфиги перед сборкой кода. Что-то типа этого


rsource "Kconfig.defaults"

menu "Utils"

menu "FIFO"

config FIFO_FRAME_SPLIT_NUM
	int "Number of blocks to make up one frame of audio data"
	default 10
	help
		Easy DMA in I2S requires two buffers to be filled before I2S
		transmission will begin. In order to reduce latency, an audio
		frame can be split into multiple blocks with this parameter. USB
		sends data in 1 ms blocks, so we need the split to match that.
		Since we set frame size to 10 ms for USB, 10 is selected as
		FRAME_SPLIT_NUM

config FIFO_TX_FRAME_COUNT
	int "Max number of audio frames in TX slab"
	default 3
	help
		FIFO_TX is the buffer that holds decoded audio data before it
		is sent to either I2S or USB

config FIFO_RX_FRAME_COUNT
	int "Max number of audio frames in RX slab"
	default 1
	help
		FIFO_RX is the buffer that holds uncompressed audio data coming
		from either I2S or USB

endmenu # FIFO

#----------------------------------------------------------------------------#
menu "Log levels"

config LOG_BOARD_VERSION_LEVEL
	int "Log level for the board/HW version"
	default 3

config LOG_CS47L63_LEVEL
	int "Log level for CS47L63"
	default 2

endmenu # Log levels
endmenu # Utils

Еще одно важное требование к драйверу.

Повторная инициализация драйвера или любого другого программного компонента не должна приводить к зависанию прошивки или иным ошибкам.

Вдогонку, ещё один атрибут:

Если есть функция, которая что-то устанавливает, то должна быть и функция, которая это что-то прочитывает. Проще говоря, у каждого setter(а) должен быть getter, подобно тому к в математике у каждой функции есть обратная функция.

Sign up to leave a comment.

Articles