Как стать автором
Обновить

Сканируем 35-мм киноплёнку дома (часть 3)

Уровень сложностиСредний
Время на прочтение30 мин
Количество просмотров586
 Сканер, о котором будет идти речь в статье
Сканер, о котором будет идти речь в статье

Предпоследняя часть из цикла статей о работе с форматом 35-мм и последняя часть, в которой описывается процесс разработки сканера

Часть 1

Часть 2

Привет Хабр! Представляю третью часть из своего цикла статей по теме оцифровки киноплёнок формата 35мм. Я прошёл уже большую часть своего пути, начиная от сурового колхоза из книг в первой статье и закончив прототипом во второй, который уже может выполнять базовый функционал. Но это ещё не всё, теперь надо устранить ряд мелких ошибок, которые всегда неизбежно появляются в таких проектах и сделать работу со сканером более user-friendly. На момент завершения 2-й части проект имел следующий вид:

Практически техногуро =)
Практически техногуро =)

Уже прошло 4 с лишним месяца с момента старта проекта, срок приличный по моим меркам, но завершать ещё рано, хотя уже пора по сроку(я заложил 6 месяцев расчётного срока). Моё изделие выполняет базовый функционал — протаскивает плёнку, фоткает кадры, сматывает плёнку, которая уже прошла сканирование, показывает на экранчике статистику, заняло стол своими потрохами. Но сканер в плане взаимодействия с пользователем довольно враждебен - нельзя на ходу подстраивать баланс под свойства фотоэмульсии, нельзя ставить на паузу, пользователь должен постоянно ходить и проверять как идёт процесс(сканер не остановится если плёнка закончится), что конечно же не "ок" и нужно исправлять, ну и ещё подумать о том, как бы запаковать лежащие рядом «кишки» в более компактный вид и в целом убрать всё это порно подальше. Эта часть уже будет более лайтовой, вся самая душнина была в предыдущей части а здесь будут доделки. По началу планировалось сделать эту часть последней, но читателей заинтересовал способ обработки полученного материала, поэтому будет ещё одна часть(скорее всего небольшая, минут на 10-15, но это неточно :))

По ходу прочтения этой части у вас могут появиться замечания, но не спешите! В конце будут мои собственные замечания к проекту, замечания читателей предыдущей частей, и возможно я и вы думаем одинаково.

Навигация по статье:

  1. Мелкая доработа привода ЛПМ

  2. Ещё одно ЧП

  3. Fail при попытке доработать макро-переходник с наскока

  4. Улучшаем цепочку питания

  5. Новый дисплей и кастомизация его корпуса

  6. Ленивое подключение контроллера

  7. Переводим камеру на постоянный источник питания

  8. Новая клавиатура и user-friendly управление

  9. Запаковка потрохов

  10. Стенки - прочь из кадра!

  11. Увеличиваем полезную площадь снимка

  12. Ускоряем сканирование(снова)

  13. Повышаем мощность подветски

  14. Датчик выпадения плёнки

  15. Шильдик и эпилог


Часть третья. Доработка и расширение функционала

Мелкая доработка привода ЛПМ

Привод ЛПМ имеет наиболее насыщенную историю исправлений и доработок, но всё равно даже в текущей конструкции есть что улучшать. На очереди устранение потенциального люфта шестерёнок с помощью установки подкладок в область пустот между шестернями и внешней стенкой:

Ещё одно ЧП

Вспомните видео из предыдущей части цикла. Оно длилось полторы минуты, что на самом деле мало, так как ресурса аккумулятора фотоаппарата хватало примерно на 3000 кадров и эпизод «Жертва любви» должен был поместиться вплоть до момента, когда девочка прогоняет собаку. И как это уже обычно со мной бывает, я по своей лени не стал предусматривать все возможные развития событий, что привело к возникновению нештатной ситуации, повлёкшей за собой остановку процесса

Взгляните на голубую направляющую стойку - всё началось с неё
Взгляните на голубую направляющую стойку - всё началось с неё

Этот раз уже без крайностей по типу «плёнка снова порвалась» — я отрегулировал ток двигателя(примерно на 2/3 от максимального для драйвера A4988), чтобы на случай очередного клина двигатель не начал ломать механизм. Причиной оказался перекос и последующий клин направляющего валика перед датчиком натяжения — причиной тому отвалившаяся с одной стороны «вставка» шарнирной опоры(не знаю насколько правильно я применил здесь термин, в гугле как-то размыто описано). Фото я не сделал, так как не смог быстро найти нужный ракурс и освещение, поэтому снова нарисую схему развития событий

Хронология развития событий в стиле "курица лапой"
Хронология развития событий в стиле "курица лапой"

Быстро нашёлся способ исправить это. Достаточно было добавить фиксирующий штифт позади опоры, который не даёт ей вылезать из валика, что тем не менее потребовало внести изменения в каркас и забросило ещё один камень в огород "старомодного" способа крепления.

Fail при попытке доработать макро-переходник с наскока

Теперь можно переключиться на работу с колхозным макропереходником. Маленькая площадь, занимаемая кадром с плёнки действует на меня как заноза в заднице, и чтобы я смог использовать больше полезной площади снимка, для этого мне надо поднять коэффициент увеличения переходника по возможности не внося дисторсий(ну или ограничившись очень слабыми), как это было с насадкой на смартфон(см первую часть – концовку), а значит размер линзы должен быть как минимум равен диаметру объектива зеркалки, то есть не меньше 60мм. Было бы хорошо достать ещё один Урал 1.5х, но таких к сожалению у меня больше не водилось. Эксперименты с увеличилкой Berlingo привели к тому что увеличилка была не столько увеличилкой сколько обычной стекляшкой, и вскрыл факт того что я всё также ведусь на яркие упаковки. Может я в чём-то не разбираюсь а может это текущий тренд на халтуру в производстве увеличительных стёкол — не мне судить

Справа - то самое хвалёное 3х увеличение(однако нет)
Справа - то самое хвалёное 3х увеличение(однако нет)

Улучшаем цепочку питания

В связи с неудачным стартом я пока откладываю доработку переходника на какое-то время до появления новых идей, и смотрю что дальше нужно сделать “по плану”. Питать прожорливую LED-подсветку от USB не самая лучшая идея, ровно как и работа зеркалки от аккумулятора с износом на 5 циклов зарядка-разрядка каждую сессию. Пора заняться проблемами источника питания. Мне нужно получить 3 напряжения — 5, 8 и 12 вольт. 5 вольт пойдёт на подсветку и плату контроллера(на самом контроллере 3.3 вольта со стабилизатора идёт, они будут использоваться для питания верхнего плеча делителей напряжения для АЦП), от 8 вольт будет запитана камера, 12 вольт пока что уходит на питание ключа для реле. Всё это нужно красиво и компактно разместить, так чтобы электроника не волочилась за проектором в случае, если его надо куда-то перенести или кому-то передать Один из способов сделать всё хорошо был найден в том что всю электронику можно разместить "на этажах" - DC-DC преобразователи на одном этаже, драйверы шаговых двигателей на втором, контроллер и его обвязка на третьем. Фотокарточка с корявыми подписями идёт в комплекте:

На самом деле разделение на 6.3 и 5В не будет, будет грубое запаралеливание 2х5в
На самом деле разделение на 6.3 и 5В не будет, будет грубое запаралеливание 2х5в

Во время конструирования платформ я не принял во внимание что провода между этажом с драйверами и этажом с преобразователями также нужно проложить, и следовательно не сделал для них каких-либо "каналов" внутри этажей, поэтому придётся их прокладывать снаружи что таки добавляет -5 к красоте, но сейчас это ещё можно простить — я пока сам не знаю как всё будет выглядеть в окончательном виде. Собираю, подключаю, по 7 раз всё проверяю и смотрю итог:

К счастью душа в виде белого дыма никого не покинула и бабах не произошёл
К счастью душа в виде белого дыма никого не покинула и бабах не произошёл

Новый дисплей

В связи с удачным пуском и созерцанием на горящие лампочки, я буду считать что всё хорошо и что всё работает как задумано. Как вы помните из предыдущей части, в качестве дисплея был выбран дисплей на HD44780 16х2. Он миниатюрный это плюс, но минус в нём то что издалека на него не посмотришь — под углом кристаллы пикселей дисплея сделают надписи нечитабельными, ну и бонусом придётся тратить несколько секунд выискивая глазами надписи. А это я тоже считаю недостатком, по моим представлениям, в понятие "user-friendly" также входит скорость и удобство поиска информации на средствах отображения. Это можно поправить, если увеличить контрастность дисплея, также это можно поправить если поменять технологию, которая лежит в основе дисплея. Что мне требуется от технологии? Ну чтобы дисплей был контрастным, чтобы он был побольше. Тогда встаёт выбор — либо дисплей должен быть на светодиодах, либо он должен быть вакуумно-люминесцентный(VFD). Недорогих светодиодных дисплеев я не видел, ну подразумевается конструктивно законченый модуль, который работает по технологии схожей с PnP, поэтому я сузил область поиска до дисплеев от кассовых аппаратов. Тут мне повезло – VFD дисплей за 500 рублей нашёлся на авито, и модель была BA-63 с питанием от 12В, емкость 2х20 знаков, производитель Wincor

Дисплей BA-63 в RS-232 исполнении
Дисплей BA-63 в RS-232 исполнении

Что важно — он может работать с CP866 страницей, а значит мне из коробки доступна псевдографика. Корпус дисплея пойдёт под замену, но старый выбрасывать не буду. Также я уже был осведомлён что внутри есть перемычки для настройки режима работы дисплея и это только добавляет поводов его сразу же разобрать. Брошюрка на дисплей доступна по этой ссылке: https://bankofmanuals.com/view/1175250/wincor-nixdorf-ba63-product-manual-25.html

Дисплей имеет набор зашитых в него кодовых страниц которые переключаются программно, и имеет возможность предварительной настройки режима работы с помощью перемычек, расположенных на печатной плате внутри. На 17й странице приводится описание перемычек JP1-JP5. Чтобы вы не выискивали табличку, приведу её здесь:

Перемычки или их комбинации

Состояние перемычек

Режим дисплея

JP1 + JP2

Разомкнут + разомкнут

9600 бит/сек

JP1 + JP2

Замкнут + разомкнут

4800 бит/сек

JP1 + JP2

Разомкнут + замкнут

2400 бит/сек

JP1 + JP2

Замкнут + замкнут

1200 бит/сек

JP3

Замкнут/Разомкнут

Контроль чётности откл/вкл

JP4

Замкнут/Разомкнут

Контроль по нечётным битам /контроль по чётным

JP5

Замкнут/Разомкнут

Самодиагностика/Нормальный режим

По умолчанию задана скорость 9600 8E1. Контроль чётности мне не нужен, но под честное слово я обещаю засылать верную инфу и соблюдать тайминги(зачем изобретать велосипед если можно просто включить USART), поэтому согласно этой табличке мне нужно замкнуть JP3, что я и сделал

Не так элегантно но... сойдёт :)
Не так элегантно но... сойдёт :)

Не очень красиво, но зато быстро. Дисплей общается по интерфейсу RS232. Из брошюрки также выясняю что мне доступны следующие пины: RTS, CTS, GND, RXT, TXD, +12V. Так как контроль чётности у меня отключён за ненадобностью и обещанием в духе "просто поверь мне", то первые два пина мне не нужны. Также я выясняю, что дисплей питается от вполне популярного напряжения в 12В а значит мне не нужно что-то там докручивать в блоке с DC-DC преобразователями, и я могу воспользоваться заранее настроенным преобразователем, который в данный момент питает ключи и релюшку. В сумме нужно 4 провода. Но вот ещё какой момент — дисплей общается по RS232 с уровнями, которые контроллер из коробки не обеспечивает, но и контроллер, не лыком шитый, предлагает USART с возможностями RTS/CTS которыми я не воспользуюсь(обещаюсь не косячить с данными, честное пионерское!), да и всё равно они заняты пином входа тактирования TIM1, а TIM1 является наиболее важным таймером и трогать его ни в коем случае нельзя, а значит нужно дополнить его преобразователем USART<—>RS232. Преобразователь понадобится самый простой — модуль для Arduino на MAX232, который обеспечивает необходимый минимум — RX, TX которые в кубе все ещё доступны так как висят на свободных пинах. Настраиваю пины в кубе

При активации USART CubeMX уже предлагает готовый вариант настроек, в данном случае ничего трогать не нужно
При активации USART CubeMX уже предлагает готовый вариант настроек, в данном случае ничего трогать не нужно

Обновляю проект и в коде, в начале main — появляется функция инициализации USART, её трогать не надо там всё хорошо. Теперь необходимо настроить дисплей и что-нибудь на него отправить. Как я раннее говорил — дисплей поддерживает мою любимую кодировку CP866, и в ней же я буду выводить всё. Ещё одно но — весь код я набираю в кодировке CP1251, а значит те надписи которые я захардкожу будут несовместимы по причине различия позиций символов кириллицы(я всё таки на русском пишу интерфейс) а значит надо воспользоваться функцией транскодирования, которая может реализована самым простым способом — табличкой:

const uint8_t charSet866[256] = {
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,  // 0x00...0x0F
  0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,  // 0x10...0x1F
  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,  // 0x20...0x2F
  0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,  // 0x30...0x3F
  0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,  // 0x40...0x4F
  0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,  // 0x50...0x5F
  0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,  // 0x60...0x6F
  0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,  // 0x70...0x7F
  0x89,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,  // 0x80...0x8F
  0xCD,0xCC,0xB9,0xDB,0xFB,0xFE,0xFD,0x00, 0x5E,0x7F,0xDB,0xB0,0x00,0x00,0x00,0x00,  // 0x90...0x9F 0x9A(Ль)->0xDB
  0xB5,0xC6,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 0xA0...0xAF
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF1,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,  // 0xB0...0xBF
  0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,  // 0xC0...0xCF
  0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,  // 0xD0...0xDF
  0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,  // 0xE0...0xEF
  0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,  // 0xF0...0xFF
};

Также я не нашёл готовой библиотеки под STM32 для дисплея BA63(быстрое гугление ничего не показало), но зная что дисплей работает в системе команд VT100, я написал примитивный драйвер для работы с ним из под STM32, исходник драйвера можно посмотреть на GitHub. Проверить работу драйвера можно путём написания простенького кода:

BA63_ClearVFD();
BA63_SetCP(866);
BA63_SetPos(0,0);
uint8_t helloBa1[21] = "STM32F100 USART 9600\0";
uint8_t helloBa2[17] = "T\270\274yp \276p\270\263e\277! =)0";
BA63_SendString(helloBa1);
BA63_SetPos(0,1);
BA63_SendString(helloBa2);

Подключаем дисплей, собираем проект, прошиваем и смотрим:

Товарищ Тимур, вы в телевизоре!
Товарищ Тимур, вы в телевизоре!

С пол-пинка дисплей заработал и вывел надпись(всё таки провода немного плохо контачат). А теперь сравните контрастность надписей с предыдущим используемым дисплеем:

Не нужно что-то комментировать, чтобы видеть результат налицо
Не нужно что-то комментировать, чтобы видеть результат налицо

Первый плюс VFD это значительно увеличеный угол обзора — можно смотреть под любым углом, так как у него нет кристаллов, которые ориентированы лицом только в одну сторону, ну и второй его плюс это размер знакоместа что позволяет читать буквы с бОльшего расстояния(мне теперь не обязательно приближаться вплотную чтобы что-то увидеть) и повышает скорость поиска и удобство чтения информации. Дальше предстоит часа полтора неспешного переноса вывода строк с HD44780 на новый экран — нет смысла показывать листинг так как в коде произошла просто замена функций с префикса HD44780 на префикс BA63 с небольшим дописыванием шаблонов и обновлением позиций координат — и результат готов

Прелести CP866 видны уже сразу. Не надо ломать голову придумывая свои знаки, если можно воспользоваться готовыми! Теперь пора снова сделать дисплей презентабельным после его извлечения из корпуса. Например, можно вернуть его в старый корпус, докупить стойку, смонтировать на стойку и как-то прикрепить например над блоком привода ЛПМ и тем самым сильно сузить потенциал доработки блока, а можно проявить креативность и придумать что-то своё. Я сильный, потому что играю на баратруме под пиво, так что пойду собственным путём — изготовлю свой вариант корпуса для VFD дисплея с блекджеком и дамами лёгкого поведения. В процессе изготовления также необходимо перенести тонированную пластину оргстекла на окно и придумать какое-нибудь крепление винтами несмотря на то что я дисплей вытаскивать после не планирую. На разработку первого и сразу удачного варианта дисплея ушло несколько часов, но тем не менее первая попытка сразу стала успешной:

И ещё около 20 часов на печать всех компонентов. Корпус состоит из трёх компонентов — «ванночки» с местом под тонированное оргстекло, ободка и задней стенки. В ободке присутствует вырез под провода извне — можно увидеть на скриншоте. Сборка прошла не без проблем, однако я допустил мелкие огрехи при расчёте длины корпуса и пришлось снова взять на себя роль слесаря похабного мелкосерийного производства где-то на Урале чтобы всё вместилось без перекосов и конструкция собралась:

Ленивое подключение контроллера

Отмечаем успех и идём дальше =) Как я раньше говорил — в проекте используется отладочная плата STM32VL-Discovery, я с радостью бы сделал свою печатную плату но у меня на тот момент были некоторые проблемы с печатью фотошаблонов для фоторезистивного метода(ЛУТ не люблю) изготовления были проблемы с химией для гальваники(создание переходных отверстий), и я решил что можно как-нибудь по колхозному приспособить имеющуюся отладочную плату, и например это можно осуществить, если сделать что-то типа самодельной кроватки представив, что плата является одной большой микросхемой. Из загашника были извлечены какие-то огрызки фольгированного текстолита и, взглянув на них, я быстро понял, что их мне хватит, ряд пинов не будет воткнут в кроватку, но это легко исправить переназначением в кубе. Также можно сэкономить на плёнке для струйной печати воспользовавшись свободным местом на черновике

За основу был взят один из фотошаблонов для другого проекта, но я не знаю уместно ли его выкладывать т.к Хабр всё таки в первую очередь для IT
За основу был взят один из фотошаблонов для другого проекта, но я не знаю уместно ли его выкладывать т.к Хабр всё таки в первую очередь для IT

Процесс травления оказался несколько запарным из-за уже выдохшегося раствора хлорного железа, но тем не менее вытравить, просверлить и запаять панельки мне удалось:

Как я раннее и говорил - часть пинов отладочной платы осталась висеть, но проблема пустяковая и не стоит отдельного абзаца
Как я раннее и говорил - часть пинов отладочной платы осталась висеть, но проблема пустяковая и не стоит отдельного абзаца

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

Переводим камеру на постоянный источник питания

Смотрим дальше по списку проблем: что делать с питанием камеры? В последний раз я проводил эксперимент с оцифровкой запитывая зеркалку от аккумулятора, которого хватает примерно на 3000 кадров. Напомню что в одной бобине примерно 14500 кадров(на самом деле колеблется от 14200 до 15150), а значит на одну бобину мне нужно провести не меньше 5 циклов зарядки. На 10 бобин уже 50 циклов. От такого абуза аккумулятор очень быстро скажет пакеда, и поэтому нужно сменить цепочку питания с автономной на источник постоянного питания. В интернете есть готовые предложения, но я их даже не рассматривал, потому что ни одно мне не подойдёт по одной или двум следующим причинам

  • Дорого-богато(я по сути плачу за кусок пластика с парой контактов и чипом себестоимостью 20-40 рублей)

  • Долго ждать а время подгоняет так как на некоторых моих бобинах прогрессировал уксусный синдром, о котором я кратко поговорил во вступлении в первой части

Касательно второго пункта замечание довольно критично. Так как я не являюсь каким-либо киноархивистом, то условия хранения киноплёнки у меня далеко не идеальные, хотя я и пытаюсь приблизить их к таковым насколько это возможно. В следствии такого у меня начинает довольно быстро прогрессировать уксусный синдром, про который я рассказывал в прологе первой части. Взгляните на одну из бобин, как она изменилась за 5 месяцев:

Архивистам достаточно этого фото, чтобы понять всю серьёзность ситуации в моём случае(68 выпуск на фото)
Архивистам достаточно этого фото, чтобы понять всю серьёзность ситуации в моём случае(68 выпуск на фото)

В связи с такими неотложными обстоятельствами, времени у меня особо нет и нужно быстро искать собственное решение, которое однако пришло внезапно(приснилось) — фотоаппарат питается от аккумулятора, так? Аккумулятор можно заменить, так? А если взять и сделать муляж аккумулятора, но с возможностью подвести провода извне? Таким образом я понял что не надо переплачивать огромные шекели за то, что я смогу изготовить за 50 рублей(утрировано). Выходит сначала надо узнать распиновку аккумулятора, но залезать в интернет не было необходимости, так как всё то нужно уже было написано на корпусе аккумулятора. Итак, аккумулятор для зеркалки имел следующие пины(я подпишу если понял назначение пина)

  1. «+» — VDD, напряжение составляет от 7.4 до 8.2 вольт в заряженном состоянии

  2. «D» — Данные от контроллера аккумулятора

  3. «C» — Общий провод для данных?

  4. «T» — Термометр для контроля температуры аккумулятора

  5. «-» — VSS

Наличие пинов по центру сразу усложняет задачу по изготовлению муляжа аккумулятора так как это означает что сразу, при включении, камера начнёт спрашивать АКБ «ты ваще кто?» и не получив ответа скорее всего начнёт бунт и откажется работать. Мерзкий подход, Nikon, какая разница от чего питается ваш продукт? Вы получили свой доход как только вашу камеру купили. Ну не буду растягивать это, потушу свою задницу из огнетушителя и буду пытаться дальше решить проблему. Так, то что я знаю что камера меня пошлёт при попытке вставить что-то своё это даже лучше, ведь я уже знаю что мне нужно поискать альтернативную прошивку, или прошивку с нужным мне патчем. И даже нашёлся сайт, который располагает коллекцией “модов” к прошивке: https://simeonpilgrim.com/nikon-patch/nikon-patch.html, к сожалению он уже не развивается, но пока ещё жив

Последней поддерживаемой версией прошивки для моей камеры является 1.03, но на самом сайте Nikon нет архива прошивок, а значит её нужно доставать из сторонних источников с чем проблем не возникло. Возвращаюсь на сайт и смотрю список изменений мода, где главной для меня оттуда является строка: “Non-brand Battery” — то, что нужно! Разумеется я сильно рискую прошивая свою камеру такими прошивками, но без риска жизнь не такая интересная. Прошивка прошла безболезненно, разве что нужно настройки выставить заново. Теперь, когда я слегка прикрутил капризность камеры, я могу вернуться к конструированию корпуса имитатора аккумулятора, и таки я его сделал

Как можно заметить, муляж хорошо вошёл в отсек
Как можно заметить, муляж хорошо вошёл в отсек

Теперь можно слепить контакты из того что нашлось под рукой, но я уже начал понимать что конструкцию нужно слегка переделывать под плату контроллера так как мои самодельные контакты скорее всего через пару-тройку раз разобьются, но тем не менее проверить как работает муляж всё же стоит. Перед подключением к камере всё было тщательно проверено — соблюдение полярности, наличие нужного напряжения, проверена работа преобразователя на нагрузке в 1.5А и когда всё было проверено, и я был уверен что нигде не налажал — был дан зелёный свет и камера была запитана через мой "переходник":

Первые секунды после подачи питания были самыми волнительными...
Первые секунды после подачи питания были самыми волнительными...

Камера от моего муляжа заработала сразу и проработала 15 минут, пока я её не выключил. В LiveView режиме камера потребляла 1.2 ампера, нагрев преобразователя был в районе 50 градусов, в общём всё штатно и беспокоиться казало бы не о чем. Но муляж позже мне пришлось разобрать по причине плохой надёжности самодельных контактных площадок, вместо муляжа я приспособил готовый аккумулятор — вытащил банки и просто приделал провода. По хорошему, нужно внести изменения в корпус муляжа чтобы адаптировать конструкцию под готовую плату, но пока что отложу(филамент подходит к концу, слабое оправдание но всё же) Если в будущем перепадёт мёртвый банками аккум, то я восстановнлю раздолбанный рабочий АКБ, а пока что банки отправляются на хранение(перед разбором разрядил аккумулятор до примерно 50%). Смотрим по списку проблем дальше и видим что он уже уменьшился до нескольких пунктов. Дальше у меня — клавиатура. Но перед тем как заняться ей, надо продемонстрировать насколько улучшился вид электронной части до и после начала переделки

Ну просто небо и земля
Ну просто небо и земля

Новая клавиатура и user-friendly управление

Пора заняться клавиатурой. Во второй части я написал код опроса клавиатуры, но тогда я пользовался мембранной клавиатурой, которая из раза в раз становилась всё хуже, кнопки всё хуже нажимались и я от неё решил отказаться, чтобы поберечь свои нервные клетки в пользу более традиционного исполнения — клавиатуру на обычных механических кнопках. Раз пошла такая подводка, то пора вернуться в Keil и дополнить функционал. Как вам известно, на текущий момент настройка цвета доступна только при начальном запуске и в дальнейшем её изменить, скажем так, нельзя. Ну, исправим это и вспомним картинку маппинга кнопок. На данный момент свободны все сервисные кнопки — весь столбец PC4 и кнопка звёздочки. Можно сделать кнопку звёздочки тоже сервисной. Я и правда не знаю какое ей ещё применение можно придумать, но пусть будет сервисной и тогда на неё же повесим функцию настройки подсветки. Код настройки из цикла в main выносим в отдельную функцию, пусть она будет называться void setupRGB(void), по возвращённому значению которой контроллер будет понимать что настройка прошла успешно. Далее будет ещё два упоротых листинга

Листинг доработанной функции main

char template[20] = “ R:000 G:000 B:000 ”;//Шаблон для настройки цвета
char readyMsg[20] = “       \241OTOB        “;	//Сообщение о готовности к работе
char framesTemplate[16] = “Ka\343po\263:         “;//Шаблон для счётчика кадров
char eiMsgUpper[20] = “      \250pep\263a\275o      “;//Мессага если работа прервана юзером
char eiMsgBottom[20] = “     O\276epa\277opo\274     “;//Вторая строка
char msgPaused[20] = "        \250A\251\244A       ";//Сообщение о паузе

struct keyInfo{
bool pressed;
uint8_t keyType;
}keyInf;

bool service = 0;//Сервисные функции
int pausePending = 0;//Запрос на паузу
//0 - нет запроса
//1 - пауза запрошена в обработчик прерывания
//2 - обработчик прерывания подтвердил и поставил паузу, подсветка работает
//3 - подсветка отключена

charCodeStr[4];//Для отображения уровня цвета
uint8_t RGB[3] = {0x00,0x00,0x00};//Массив с данными цвета

const uint8_t visualBrackets[4] = {0,6,12,18};//Позиции для вставки декоративного визуала
const uint8_t RGBPos[3] = {3,9,15};//Позиции на дисплее для уровня цвета
uint8_t digitPos = 0;//Счётчик записанных разрядов

uint8_t colorType = 0;	//0 – R, 1 – G, 2 – B;

int main(void)
{
	//Прописывать GPIO_init тут не нужно, это уже есть в коде, я опущу часть кода с автогеном куба
	//Чистим структуру
	keyInf.pressed = 0;																																			//Кнопка не была нажата
	keyInf.keyType = 0;																																			//На 0 кнопки не биндим
	BA63_ClearVFD();
	BA63_SetCP(866);
	BA63_SetPos(0,0);
	BA63_SendString(template);
	
	//С подготовительной частью покончено, теперь надо запустить сканирование клавиатуры
	HAL_TIM_Base_Start_IT(&htim7);																													//Запускаем TIM7, который будет каждые 200 мс генерировать прерывание по которому контроллер пробежится по клавиатуре
	uint16_t colorComponentCode = 0;																												//В процессе обрежем до 8 бит
	
	if(setupRGB())//Запускаем настройку подсветки
	{
		BA63_SetPos(0,1);
		BA63_SendString(readyMsg);
		//Проверяем нажатие кнопки, пусть запускать процесс будет кнопка PA0(есть на плате)
		if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET)
		{
			BA63_SetPos(1,0);//Ставим курсор и выводим шаблон
			BA63_SendString(framesTemplate);
			BA63_SetPos(1,12);//Печатаем статичный символ плёнки
			BA63_SendString('/001');
			//Запускаем таймер и выходим из цикла
			HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
			while(1)
			{
				//Проверяем что юзер вызвал настройку подсветки
				if(keyInf.pressed && keyInf.keyType == 12 && service)
				{
					//Тут результат проверять не будет
					setupRGB();
				}
			}
		}
	}
}

И теперь пропишем новую функцию для настройки подсветки:

Листинг функции setupRGB()

void setupRGB(void)
{
	//Начинаем заполнение полей
	while(1)
	{
		if(keyInf.pressed && keyInf.keyType > 0 && keyInf.keyType <= 10 && digitPos < 3)//Если юзер кнопку нажимал и это кнопка цифры, то зайдём внутрь условия иначе игнор
		{//Кнопка нуля висит на ИД 10
			keyInf.keyType == 10 ? colorComponentCode = addDigit(colorComponentCode, 0) : colorComponentCode = addDigit(colorComponentCode, keyInf.keyType); 
			++digitPos;	//Прибавляем количество записанных разрядов
			keyInf.pressed=0;//Скидываем флаг чтобы не было повторного срабатывания
			keyInf.keyType = 0;//Скидываем ИД кнопки
		}//Если юзер просто нажал кнопку решетки то переходим ниже
		else if(keyInf.pressed && keyInf.keyType == 0 && colorType < 3)	//Проверяем что нажата кнопка "#"
		{
			switch(colorType)//Добавляем декорации
			{
				case 0://[Rxxx]G000 B000 
				{
					BA63_SetPos(0,visualBrackets[0]);
					BA63_SendString("[");
					BA63_SetPos(0,visualBrackets[1]);
					BA63_SendString("]");
					BA63_SetPos(0,visualBrackets[2]);
					BA63_SendString(" ");
					BA63_SetPos(0,visualBrackets[3]);
					BA63_SendString(" ");
					break;
				}
				case 1:// R123[Gxxx]B000 
				{
					BA63_SetPos(0,visualBrackets[0]);
					BA63_SendString(" ");
					BA63_SetPos(0,visualBrackets[1]);
					BA63_SendString("[");
					BA63_SetPos(0,visualBrackets[2]);
					BA63_SendString("]");
					BA63_SetPos(0,visualBrackets[3]);
					BA63_SendString(" ");
					break;
				}
				case 2:	// R123 G234[Bxxx]
				{
					BA63_SetPos(0,visualBrackets[0]);
					BA63_SendString(" ");
					BA63_SetPos(0,visualBrackets[1]);
					BA63_SendString(" ");
					BA63_SetPos(0,visualBrackets[2]);
					BA63_SendString("[");
					BA63_SetPos(0,visualBrackets[3]);
					BA63_SendString("]");
					break;
				}
			}//Если больше 255 то пишем 255
			colorComponentCode > 255 ? RGB[colorType] = 255 : RGB[colorType] = colorComponentCode;		
			ARGB_FillRGB(RGB[0],RGB[1],RGB[2]);
			ARGB_Show();	//Обновляем отображаемый цвет
			sprintf(charCodeStr, "%03d", RGB[colorType]);//Ничего умнее в голову не пришло
			BA63_SetPos(RGBPos[colorType],0);	//Перемещаем указатель на позицию для записи
			BA63_SendString(charCodeStr);	//Записываем циферку уровня цвета
			++colorType;//Перемещаем указатель на элемент массива
			digitPos = 0;//Сброс количества записанных разрядов
		}
		else if(colorType == 3)
		{
			for(uint8_t _pos = 0; _pos < 5; ++_pos)
			{
				BA63_SetPos(0,visualBrackets[_pos]);//Перемещаем курсор на экране
				BA63_SendString("/000");//Прописываем пользовательский символ(тут просто заполняется всё знакоместо)
			}
			return;
		}
	}
}

Пользователю добавлена возможность на лету изменять настройки баланса белого. Гипотетически функцию можно доработать чтобы например перед запуском цвет подсветки менялся после каждого раза когда в массив RGB записывается значение, а во время работы только после полного прохода по массиву, но я пока не представляю как это сделать правильно. Но это на потом, проверим как код себя ведёт на практике:

Но это ещё не всё. Допустим у пользователя не хватает емкости карты памяти чтобы за один раз отснять всю бобину, или сканер работает в жилой комнате и время позднее а значит что шум уже недопустим и пользователь захочет поставить процесс на паузу. Раньше я бы сказал что это невозможно(ну-ну), но так как теперь я перевёл всё управление на контроллер, то возможности у меня практически безграничны и ограничиваются лишь моей фантазией и поющем романсы скилом в кодинге. Прикрутим фичу паузы — добавим магическую переменную pausePending и будем проверять её значение, если оно 0, то пауза не запрошена и значит пока работаем дальше, но если пользователь запросил паузу то возобновлять подачу ШИМ импульсов не будем, зададим значение переменной 2 и просто выйдем из функции.

Доработка TIM1_UP_TIM16_IRQHandler

if(pausePending == 0)
{
  HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);//Запускаем генерацию ШИМ
}
else
{
  pausePending = 2; //Если зашли сюда то значит пауза запрошена и просто подтверждаем
  HAL_Delay(500);//Защита от двойного срабатывания если юзер удерживает кнопку
  BA63_SetPos(1,0);
  BA63_SendString(msgPaused);
  return;
}

Но помимо захода в паузу, можно ещё добавить возможность щёлкать подсветкой чтобы не выжигать эмульсию ярким светом, для удобства переключение подсветки можно повесить на ту же кнопку что и пауза, а возобновлять можно той же кнопкой что и запускает сканирование, при этом если подсветка выключена в момент возобновления — она будет зажжена тем цветом который записал пользователь.

Листинг main(фрагмент):

//Проверяем нажатие кнопки паузы и при этом отсутствует пауза
if(keyInf.keyType == 15 && service && pausePending == 0)
{
	pausePending = 1;//Запрашиваем паузу
}
else if(keyInf.keyType == 15 && service && pausePending == 2)
{
	pausePending = 3;//Переключаем флаг
	ARGB_FillRGB(0x00,0x00,0x00);
	ARGB_Show();	//Вырубаем подсветку
	HAL_Delay(200);//Задержка от двойного срабатывания
}
else if(keyInf.keyType == 15 && service && pausePending == 3)
{
	ARGB_FillRGB(RGB[0],RGB[1],RGB[2]);
	ARGB_Show();	//Врубаем подсветку
	pausePending = 2;//Переключаем флаг
	HAL_Delay(200);//Задержка от двойного срабатывания
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET && pausePending != 0)
{
	if(pausePending == 3)
	{
		//Врубаем подсветку
		ARGB_FillRGB(RGB[0],RGB[1],RGB[2]);
		ARGB_Show();	//Врубаем подсветку
	}
	pausePending = 0; //Сбрасываем флаг
	BA63_SetPos(1,0);//Ставим курсор и выводим шаблон
	BA63_SendString(framesTemplate);
	BA63_SetPos(1,12);//Печатаем статичный символ плёнки
	BA63_SendString('/001');
	//Запускаем таймер и выходим из цикла
	HAL_Delay(200);
	HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
}

Теперь настал черёд проверить как работает новая фича. Прошиваем, запускаем и смотрим:

Работает как задумано! Теперь процесс можно ставить на паузу, выключать и идти спать. Хорошей опцией было бы выключать всякие вентиляторы и дисплей, но описывать её здесь отдельно не имеет смысла так как вся опция опишется двумя вызовами HAL_GPIO_WritePin. Но что ещё такого можно придумать? Ну, пусть у меня будет дебаг-режим! Включаться он будет зажатием кнопки PA0 при подаче питания и на дисплей будут выводиться такие параметры, как например версия прошивки, показания АЦП, и ещё что-нибудь. Давайте дополним код:

Фрагмент листинг main:

if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET)
{	//Если кнопка удерживается при запуске то заходим сюда
	//Заходим в дебаг
	char adcText[5] = "A\341\250:";
	char inputText[6] = "BBO\340:";
	char fwVer[9] = "BREL_0.9";
	char levelChar[4];
	BA63_SetPos(0,0);
	BA63_SendString(adcText);
	BA63_SetPos(1,1);
	BA63_SendString(fwVer);
	BA63_SetPos(1,13);
	BA63_SendString(inputText);//Закидываем шаблоны на экран
	while(1)
	{
		ADC_Select_Channel(ADC_CHANNEL_1);
		HAL_ADC_Start(&hadc1);
		HAL_ADC_PollForConversion(&hadc1,10);
		HAL_ADC_Stop(&hadc1);//Выбираем канал, запускаем АЦП, обрезаем значение до 10 бит
		uint16_t level = (uint32_t)HAL_ADC_GetValue(&hadc1)/4;
		sprintf(levelChar, "%04d", level);//Конвертируем в человеко-читаемый формат
		BA63_SetPos(0,4);
		BA63_SendString(levelChar);
	}
}

Хабр пока что не располагает опцией встраивания shorts-видео, поэтому на этот раз я просто оставлю ссылку: https://www.youtube.com/shorts/sJj_DLNIFcE

Повторим сканирование того же выпуска что и прошлый раз, но на этот раз с того момента когда слетел направляющий валик и посмотрим на результат, ещё раз:

Запаковка потрохов

Сканирование идёт хорошо и даже отлично, но последняя проблема по прежнему не решена — все ещё напоминает о себе факт что у меня используется всего-лишь где-то 40% от всей площади фотографии кадра и хотелось бы как-то улучшить это, но сначала надо довести текущее дело до конца — убрать гурятинку, запаковать кишки и запрятать их от глаз подальше. В принципе всё можно запихнуть под раздающую бобину так как это на данный момент единственное свободное место — пространство под принимающей бобиной уже занято датчиком. Вытекает задача уместить всё в пространстве примерно 15х15х20 см с которой я успешно справляюсь + решаю проблему с нагревом микросхем драйверов — добавляю возможность вставить два вентилятора(поставил один т.к второй потерялся где-то):

В процессе сборки родился монстрик(слева), которого я успешно причесал, выходил и запрятал чтобы не кошмарить люд:

Кошмар на улице Хабров
Кошмар на улице Хабров

Сканер всё больше приобретает тот вид, который показан на КДПВ — у меня всё более менее уложено компактно, камера питается от постоянного источника питания, хоть там не так всё хорошо получилось, управление полностью на контроллере, плёнка не собирается в кучу рядом со сканером а укладывается обратно в бобину, одним словом — красота, однако осталось ещё немного работы — доработка макро-переходника, и по декоративной части — продумать более нормальный и простой способ подключения питания, ну и поработать над дизайном клавиатуры — все кнопки и переключатели выглядят, как лапы моего монстра, что выглядывает из под бобины и ждёт кого бы схватить за палец. Начну с того, что придумаю как разместить все кнопки и клавиатуру красиво. Сначала я не знал как будет лучше, и думал что особой разницы не будет(в то время у меня дисплеем ещё был миниатюрный HD44780). В одно время у меня было 7 пятниц на неделе — я думал всё организовать над приводом ЛПМ, потом думал что всё будет прикреплено к стойке, которая держит кадровое окно, ещё позже думал что нужно будет менять доску на другую но лень победила – я принял решение что клавиатура будет рядом с тем местом, на которое мне удастся прикрепить дисплей. Дисплей, как можно посмотреть в статье выше — обосновался на опоре, поддерживающей раздающую бобину, ну значит и клавиатура будет там же, и под шумок можно и сконструировать панельку с выключателем и разъемом питания:

Панель сразу получилась хорошо и то верно — там практически не надо думать — ничего кроме выключателя и разъёма питания да и расположена панель явно не со стороны лица проекта, а вот клавиатура другое дело — на неё всё таки люди будут чаще смотреть и её дизайн играет более важную роль

У клавиатуры все ещё не совсем презентабельный вид. Да, это лучше чем просто висящие потроха, но ведь в будущем мне наверняка захочется сделать наклейки для кнопок обозначающие функции кнопок, или может я переделаю конструкцию в более закрытый и стильный вид, короче говоря — надо дать возможность для будущих манёвров. Делаю крышку, кнопки и прикрываю это всё это безобразие

Посмотрите на внешний вид клавиатуры, переключатель и красную кнопку и включите фантазию про ракеты =)
Посмотрите на внешний вид клавиатуры, переключатель и красную кнопку и включите фантазию про ракеты =)

Стенки - прочь из кадра!

Теперь остался предпоследний рывок. Возвращаемся обратно к истокам к стойке с окном и камерой. Как я уже говорил, сейчас площадь матрицы камеры используется неэффективно — только ~40% её площади и ещё примерно ~40% площади это фотографии стенок окна — использование нерационально и требует пересмотра. Также ещё нужно внести некоторые изменения в модуль подсветки — нужно скосить стенки под плёнкой так как из-за некоторых особенностей нашего мира одновременно строго перпендикулярно по всей площади не посмотришь и отходя в сторону от центра будешь всё больше отклоняться. И из-за такого при попытке стабилизации кадра по отверстию перфорации я прямо за отверстием наблюдаю фрагмент стенки, который ухудшает контрастность дырки перфорации для и без того прозрачной плёнки и это в свою очередь ухудшает возможности по стабилизации. Как это выглядит и исправляется наглядно показано в очередном рисунке из паинта:

Увеличиваем полезную площадь снимка

Можно наглядно сравнить, после такого исправления отверстия перфорации гораздо лучше подвергаются обработке алгоритмов стабилизации так как свет между плёнкой и отверстием больше ничего не перекрывает. Теперь наконец-то займусь проблемой маленькой картинки кадра на фотографиях. Как я показывал в прошлой части статьи — в конструкции макропереходника используется всего одна линза, и та — бытовая увеличилка чей коэффициент увеличения явно недостаточен а значит надо брать либо стекло с более мощным увеличением, либо ставить второе. Толстые и большие стёкла я наврятли найду, а вот сделать комбо из двух линз послабее — это реальнее. Разбираю переходник и начинаю всё по новой, всё также эмпирическим методом но с добавлением дополнительного стекла(обошлось в 300 рублей с мешка):

На фото уже подобрано оптимальное расстояние между стёклами. Теперь покажу сравнительную пару из двух фотографий как это повлияло на занимаемую полезную площадь(увеличилась с 40 до примерно 70%):

Также это изменение требует смены ориентации камеры. Если раньше картинка относительно камеры лежала на боку, то теперь чтобы края поместились в фотографию кадр должен лежать уже “по нормальному”.

Ускоряем сканирование(снова)

Также я ещё могу оптимизировать скорость сканирования под особенности зеркальной камеры. Несмотря на то, что она довольно таки высокая по сравнению с первыми опытами ещё в первой части — есть куда ускоряться. Сейчас задержка захардкожена значением 500 мс, но надо принять во внимание что камера зажигает лампочку не только когда включена, а вообще в любом случае чтобы сообщить что сейчас камера занята записью на карту памяти, занята размонтированием носителя при выключении, занята ещё при любой другой I/O операции и этим можно воспользоваться если принять за факт что лампа может участвовать в обратной связи, но с оговоркой что лампа зажигается не сразу, а только после завершения отработки зеркалом, но чтобы не вычислять значения можно поступить по другому — контроллер в любом случае начнёт проверять датчик быстрее чем камера закончит работу зеркалом, и “фронт” обратной связи можно использовать как сигнал на запуск привода ЛПМ, который отработает и вызвав прерывание с заходом в цикл с условным таймаутом в 5 секунд на ожидание завершения записи. Таким образом я наиболее эффективно оптимизирую затраты времени снизив время, которое требуется на сканирование всей бобины

Бенчмарк скорости на 15000 кадров с оптимизацией и без
Бенчмарк скорости на 15000 кадров с оптимизацией и без

Вносим изменение в конструкцию блока крепления камеры добавляя туда лапку с фоторезистором, который будет служить в роли обратной связи от камеры. Фоторезистор подключается к контроллеру по такой же схеме как и датчик натяжения — через плечо делителя напряжения на вход свободного(2-го) канала АЦП:

Выглядит топорно, но меня устраивает
Выглядит топорно, но меня устраивает

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

Листинг с доработкой функции хандлера прерывания от таймера:

#define FRONT_THRESHOLD 40
//...
inline bool checkFront(void)
{	
	ADC_Select_Channel(ADC_CHANNEL_2);//Выбираем канал с фоторезистором, запускаем АЦП
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1,10);
	HAL_ADC_Stop(&hadc1);//Выбираем канал, запускаем АЦП, обрезаем значение до 6 бит
	uint16_t level = (uint32_t)HAL_ADC_GetValue(&hadc1)/64;
	if(level > FRONT_THRESHOLD)
		return 1;
	else
		return 0;
	//В зависимости от результата возвращаем либо 1 либо 0. Дополнительно порог
	//порог можно настроить подстроечником
}
void TIM1_UP_TIM16_IRQHandler(void)
{
	HAL_TIM_PWM_Stop(&htim15, TIM_CHANNEL_1);//Первым делом останавливаем генерацию ШИМ
	static uint16_t frameCnt;//То как с этой строчкой обойдётся компилятор оставим это компилятору, будем считать что он зануляет при инициализации
	char framChar[6];//Аналогично примеру из main - для конвертации в человеко-читаемый формат
	_HD44780_SetPos(1,13);
	_HD44780_SendChar('/002');	//Ставим значок часов(сканер занят снимком)
	
	//Если камера занята предыдущим снимком то берём паузу с таймаутом 5 секунд
	if(checkFront())
	{
		for(volatile uint16_t wait = 0; wait < 5000; ++wait)
		{
			if(!checkFront())
			{
				//Когда камера заканчивает работу с IO то выходим
				break;
			}
			HAL_Delay(1);
		}
	}
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
	HAL_Delay(50);//Удержание сигнала чтобы камера успела среагировать
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
	
	//Если фронт не обнаружен то заходим в ожидание с таймаутом 5 секунд
	if(!checkFront())
	{
		for(volatile uint16_t wait = 0; wait < 5000; ++wait)
		{
			if(checkFront())
			{
				//Когда обнаруживается фронт то считаем что процесс съемки завершён
				break;
			}
			HAL_Delay(1);
		}
	}
	
	++frameCnt;	//Приращиваем кадр
	sprintf(framChar, "%05d", frameCnt);//Конвертируем в человеко-читаемый формат
	_HD44780_SetPos(1,7);//Передвигаем курсор с оглядкой на шаблон
	_HD44780_SendStr(framChar);//Печатаем счётчик кадра
	if(pausePending == 0)
	{
		_HD44780_SetPos(1,13);
		_HD44780_SendChar('/003');	//Ставим значок стрелки
		HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);//Запускаем генерацию ШИМ
	}
	else
	{
		pausePending = 2; //Если зашли сюда то значит пауза запрошена и просто подтверждаем
		HAL_Delay(500);//Защита от двойного срабатывания если юзер удерживает кнопку
		BA63_SetPos(1,0);
		BA63_SendString(msgPaused);
		return;
	}
}

Повышаем мощность подсветки

В качестве завершающего штриха в доработке этого блока можно увеличить мощность подсветки подключив вторую рамку, что позволит в настройках понизить чувствительность ISO до значений 100-200 что в свою очередь снизить уровень шумов от матрицы, ну или задать более высокие значения диафрагмы(вплоть до 20-25) для увеличения глубины резкости при невозможности подстроить расстояние между кадровым окном и объективном фотоаппарата(например если требуемое расстояние лежит вне допустимого диапазона, задаваемого регулирующими платформу гайками)

x2 к мощности и x2 к силе мерцания! Бедные эпилептики...
x2 к мощности и x2 к силе мерцания! Бедные эпилептики...

Так как нагрев от двух рамок уже достаточно сильный, то помимо риска перегреть сами светодиоды создаётся конвекционный поток, способный захватывать летающие в воздухе частицы пыли и пронося их через отверстия перфорации, накапливать на нижней стороне увеличительного стекла делая поверхность загрязнённой уже через 3-5 минут работы. Эта проблема решается довольно просто — сбоку ставится вентилятор который будет выполнять принудительное охлаждение снижая среднюю температуру внутри блока подсветки до примерно 30-35 градусов, хотя саму проблему загрязнения это не решает, но позволяет отсрочить момент когда накопившаяся на стекле грязь начнёт портить качество снимков.

Датчик выпадения плёнки

Так, с модулем покончено, но как быть когда плёнка закончится? Как сканер узнает что вот именно сейчас нужно остановиться? А пока никак, и это исправляется добавлением в конструкцию датчика выпадения плёнки, который представит из себя “кнопку” на основе микрика, нажимаемую плёнкой под натяжением. Пока сохраняется натяжение плёнки микрик находится в нажатом положении и тем самым контроллер считает что всё в порядке, можно работать. Когда плёнка заканчивается либо возникает какая-то проблема с подмоткой на принимающую бобину, она выпадает из тракта в результате чего пропадает натяжение и микрик размыкается что даёт сигнал контроллеру о том что либо бобина закончилась, либо с трактом по которому проходит плёнка что-то не так и это в свою очередь останавливает процесс. Такая вот простая мера безопасности

Работу этого датчика реализуем в хандлере прерывания void TIM1_TRG_COM_TIM17_IRQHandler(void), так как запускать ещё один таймер ради такого слишком расточительно:

Листинг с доработкой хандлера прерывания:

char eiFinishMsg[20] = "      \244ABEP\254EHO     ";

static uint16_t fails;
if(!HAL_GPIO_IsInputPinSet(GPIOB, HAL_GPIO_PIN_9))
	{
		if(HAL_GPIO_IsInputPinSet(GPIOB, HAL_GPIO_PIN_9))
		{
			fails=0;
		}
		else
		{
			if(fails>700)
			{
				//Отправляем надпись "Завершено"
				BA63_SetPos(0,0);
				BA63_SendString(eiFinishMsg);
				
				//Останавливаем периферию, насовсем
				HAL_TIM_Base_Stop(&htim4);
				HAL_TIM_Base_Stop(&htim7);
				HAL_TIM_Base_Stop_IT(&htim1);
				HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
				HAL_TIM_PWM_Stop(&htim15, TIM_CHANNEL_1);
				return;
			}
			++fails;
		}
	}При таком раскладе сканер остановится примерно через 3.5 секунды после срабатывания датчика выпадения плёнки, задержка нужна так как привод который управляет катушкой работает неравномерно что может на короткое время провоцировать срабатывание датчика. Это был последний этап в разработке проекта. В качестве завершения печатается шильдик с коротким названием модели сканера, номера модели и типа плёнки, которую он может принять:

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

Шильдик и эпилог

Это был последний этап в разработке проекта. В качестве завершения печатается шильдик с коротким названием модели сканера, номера модели и типа плёнки, которую он может принять:

Когда не знаешь кой применить тебе шрифт - фигачь ГОСТ!
Когда не знаешь кой применить тебе шрифт - фигачь ГОСТ!

А теперь всех, кто дочитал до этих строк хочу поздравить с завершением этого долгого и сложного пути разработки. Нужно признать что это первый мой проект такой сложности и я в процессе создания невероятно много косячил везде где только можно, так как много чего приходилось изучать на ходу, но тем не менее мне удалось собрать работающий экземпляр, который уже оцифровал несколько бобин, и в дальнейшем может быть причёсан, улучшен мной или кем-то из энтузиастов. Весь процесс создания прототипа, по которому я написал цикл статей занял по времени 6 месяцев с учетом доделок. Также, как и было обещано в комментариях к предыдущей части, публикую исходники для желающих собрать проектор или почерпнуть/внести какую-либо идею https://github.com/anrej0705/rmv2plus Прошивка будет опубликована позже так как требуется её рефакторинг и испытание. Финальная сборка находится в файле проектор1.SLDASM, она же годится в качестве “мануала” по сборке. На момент написания третьей части скажу что я не обладаю толком информацией как всё правильно организовать в сборке чтобы всё красиво крутилось, шевелилось поэтому файл сборки больше подходит в роли мебели, но его достаточно чтобы увидеть что куда вставляется и крепится. В папке STL находится готовая для загрузки в слайсер комплектуха.

Ориентировочная смета на изготовление: 5 катушек ABS по 600-700 рублей, увеличилка Урал(~1500 на авито), увеличительное стекло x5(не помню лот, но он был на мешке), зеркальная камера Nikon D3200(8000-9000 рублей), примерно 100-250 рублей электроэнергии и до полутра-двух недель времени на печать всех деталей.

А теперь пройдусь по замечаниям к моему циклу статей от меня и от читателей, это важно:

  1. Самопал макро-переходника можно заменить на объектив Индустар-61л/з с макрокольцом под М42.

  2. Также рекомендуется отказаться от использования зеркальных камер в виду быстрого износа затвора при данном применении в пользу беззеркальных, например Nikon 1J5 или Fujifilm X-A3.

  3. Дисплей покупателя BA63 можно заменить на аналогичный. Обратите внимание — BA63 распространяется как в RS232 так и в USB исполнении!

  4. Также можно убрать конвертер USART<—>RS232 выпаяв соответствующую микросхему на плате BA63(её пины звонятся прямо от RTS/CTS, RX/TX).

  5. На собственном опыте не рекомендую применять WA2812 и им подобные светодиоды с ШИМ подсветкой так как они вызывают мерцание, улавливаемое камерой. Автор предлагает использовать аналоговую RGB подсветку с управлением через трёхканальный ЦАП.

  6. Также в статье было упомянуто, что два DC-DC преобразователя грубо запараллелены, чего делать не рекомендуется — лучше подключить оба к нагрузке через резисторы например на 0.5 ома.

  7. STM32 можно заменить на Arduino, но нужно учесть, что вам потребуется как минимум два хардварных ШИМ-генератора и 2 таймера.

  8. Упомянутый драйвер BA63 написан в минимальном исполнении и без труда может быть доработан и дополнен.

  9. При разработке дизайна блока управления была допущена грубая ошибка, которая приведет к затруднению охлаждения DC-DC преобразователей, в следствии чего последние могут нагреваться до 125 градусов. Рекомендуется как минимум один вентилятор с потоком воздуха, направленным перпендикулярно ориентациям DC-DC преобразователей.

  10. Не выставляйте ток двигателя ЛПМ привода больше чем на 2/3 от максимума(на драйвере A4988), иначе это может спровоцировать разрушение стыка валиков и шестерни несмотря на то, что применяется ацетоновая склейка.

  11. Также советую не применять HAL, несмотря на то что он удобен — он довольно таки тяжелый по объему кода

На Хабре есть ограничение на длину статьи, из-за чего придётся писать ещё 4ю часть, которая будет посвящена уже процессу обработки отсканированного материала на примере отрезков из вступления и окончания. Работа будет вестись в программах Davinci Resolve — сборка кадров в видеоряд, цветокор и финальный рендер, AEO-Light — программное сканирование звука, iZotope RX11 — поверхностная обработка звука, чистка артефактов и шума.


Продолжение следует…

Если вам понравилась статья, вы можете отблагодарить автора:

ЮМани 410012072475999

Если у вас есть какие-то вопросы насчёт проекта, или идеи - напишите пожалуйста в комментариях

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+8
Комментарии0

Публикации

Истории

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань