I. Вводная
Пытаюсь отладить работу аппарантого SPI в режиме slave, на борде SK-MX53-OEM.
Написал простенький драйвер (модуль в kernel-space), который по прерыванию читает данные из RX FIFO (SPI) и сохраняет их в мегабайтный буфер, т.к. FIFO больно маловат, всего 64 байта.
Прерывание назначил по признаку заполнения RX FIFO 24 байтами (т.е. как только получил 24 байта - генерит прерывание). Это дает хороший запас от переполнения FIFO (24 байта далеко от 64).
Драйвер создает девайс /dev/spislave и по команде read выдает данные из буфера.
Сделал простенькую програмку, которая читает /dev/spislave.
II. Суть проблемы
Все работает нормально (данные идут без потерь) пока программа жрет все 100% ресурсов, т.е. тупо сидит в while(1) цикле и постоянно опрашивает /dev/spislave.
Как только я добавляю в цикл nanosleep(1 наносек), чтобы разгрузить проц, то сразу идут потери данных, прерывание генерится реже чем положено.
iii. Вопрос
Не может ли быть так, что nanosleep включает какой-то режим энергосбережения который вырубает клоки на аппаратном SPI и в результате я теряю данные ?
В описании nanosleep ничего про это не нашел, но других объяснений тоже пока нет.
Я что-то не пойму, если буфер на мегабайт - зачем его все время вычитывать и как чтение из памяти может влиять на прерывания ? Где-то у вас ошибка драйвере.
если вы хотите задержку в 1нс, то вряд ли у вас это получится в линуксе. у меня 9g45, на ней в поставляемом стартеркитом линуксе разрешение таймера было установлено в 10 000 000 нс (10 мс), естественно, выйти быстрее из нанослипа не получалось. после пересборки ядра с таймером высокого разрешения 10 мс превратились в 1нс, и дело пошло бодрее - задержка при выходе из нанослипа стала 20-30 мкс, это на стандартном ядре, без RT-патчей, там, видимо, эту цифру можно еще снизить.
посмотрите cat /proc/timer_list, если в поле Resolution у вас стоит не 1 нс, то вы не сможете быстро выходить из нанослипа
1. Моя задача как раз в том, чтобы не нужно было все время вычитывать из этого мегабайтного буфера, а обращаться туда изредка, чтобы не нагружать проц. И именно это у меня пока не получается.
2. Я убрал nanosleep и вместо него поставил просто пачку пустых циклов - так все работает норм. Даже если ставлю пустые циклы на 500 милисек, то драйвер все успешно накапливает в буфере и потом через 500 милисек выдает (через read устройства /dev/spislave) накопившиеся данные без ошибок.
Это говорит о том, что данные по прерыванию переносятся в мегабайтный буфер нормально, без всяких пропусков и переполнений RX FIFO (который всего 64 байта).
Картинка входа и выхода из прерываний выглядит нормально, вот так:
(здесь единица - вход в прерывание SPI, ноль - выход).
Когда ставлю nanosleep то получается так:
(бледные импульсы это те которые с переменным джиттером, то есть их появление плавает во времени)
3. Выходит, что nanosleep почему-то мешает драйверу нормально работать.
Почему - для меня пока вопрос открытый...
Почитайте про средства синхронизации в ядре Linux и забудьте про переменные которые вы используете (static char locked;). Например тут http://www.ibm.com/developerworks/ru/library/l-linux-synchronization/
Сделайте например 2 буфера - тогда вам вообще не нужно будет разруливать доступ к указателям - пока заполняете 1, второй доступен для чтения и наоборот. Реализуйте ф-цию poll в драйвере по заполнению одного из буферов - тогда вам не надо постоянно вычитывать буфер в юзерспейс, система передаст управление poll/select в юзерспейс как только очередной буфер будет готов для чтения.
Потом как получится - уберите чтение FIFO в прерывании и примените DMA, после этого сделайте не 2 а побольше буферов и менеджер памяти как в v4l2 :) и добавьте поддержку mmap в драйвере. В общем надо знать какая задача - непрерывный поток принимать или еще отвечать надо что-то по spi.