После того, как SWD-интерфейс физически присоединён, нужно понять, как через него подключиться отладчиком к микроконтроллеру. Для работы с JTAG- и SWD-адаптерами можно использовать OpenOCD: будучи правильно сконфигурирована, эта программа подсоединится к аппаратному адаптеру и откроет сетевой порт, на котором будет ожидать подключения от GDB, реализуя протокол gdb extended-remote.
Чтобы объяснить OpenOCD, с чем ей нужно работать, создадим файл
и запустим openocd:
Теперь можно запустить gdb (а точнее, придётся установить gdb-multiarch, поскольку ваш компьютер, скорее всего, тоже использует систему команд x86_64, а не 32-битный ARM) и подключиться к OpenOCD:
В примере выше
На 32-битном ARM инструкции могут иметь длину как 2, так и 4 байта, и как можно догадаться по примеру выше, мимо начала "второй с конца" инструкции я промахнулся. Попробуем ещё раз:
Вот, теперь больше похоже на правду.
#моргаем_светодиодом
Чтобы объяснить OpenOCD, с чем ей нужно работать, создадим файл
nanodap.cfg с таким содержимым:# тип адаптера, с которым будем работать
adapter driver cmsis-dap
# имеющаяся у меня реализация cmsis-dap представляется компьютеру
# как USB-устройство с VendorID 0xc251, ProductID 0xf001
cmsis_dap_vid_pid 0xc251 0xf001
transport select swd
source [find target/stm32f4x.cfg]
и запустим openocd:
$ sudo openocd -f nanodap.cfg
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: SWO-UART supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x2ba01477
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Теперь можно запустить gdb (а точнее, придётся установить gdb-multiarch, поскольку ваш компьютер, скорее всего, тоже использует систему команд x86_64, а не 32-битный ARM) и подключиться к OpenOCD:
>>> target extended :3333
Remote debugging using :3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0800343e in ?? ()
>>> x/10i $pc-8
0x8003436: mcr2 7, 5, pc, cr10, cr13, {7} @ <UNPREDICTABLE>
0x800343a: @ <UNDEFINED> instruction: 0xfacc42b8
=> 0x800343e: beq.n 0x80034aa
0x8003440: uxtb.w r10, r9
0x8003444: bl 0x80009d4
0x8003448: mov r7, r0
0x800344a: cmp.w r10, #1
0x800344e: bne.n 0x80034aa
0x8003450: ldrb.w r1, [r8]
0x8003454: ldrh.w r0, [r8, #2]
В примере выше
>>> — это приглашение gdb, а $pc — обозначение регистра, указывающего на текущую инструкцию: program counter. x/10i $pc - 8 означает "отобрази мне содержимое памяти (x), начиная с адреса, заданного выражением $pc - 8, при этом интерпретируй содержимое как инструкции (i) в количестве 10 штук".На 32-битном ARM инструкции могут иметь длину как 2, так и 4 байта, и как можно догадаться по примеру выше, мимо начала "второй с конца" инструкции я промахнулся. Попробуем ещё раз:
>>> x/10i $pc-6
0x8003438: bl 0x80009d4
0x800343c: cmp r0, r7
=> 0x800343e: beq.n 0x80034aa
0x8003440: uxtb.w r10, r9
0x8003444: bl 0x80009d4
0x8003448: mov r7, r0
0x800344a: cmp.w r10, #1
0x800344e: bne.n 0x80034aa
0x8003450: ldrb.w r1, [r8]
0x8003454: ldrh.w r0, [r8, #2]
Вот, теперь больше похоже на правду.
#моргаем_светодиодом
Как видно из карточки платы на stm32-base, красный светодиод намертво подключен к питанию, а вот синим мы может управлять через GPIO ножку C13.
Теперь нужно найти документ, описывающий, какие регистры управляют GPIO на данном конкретном микроконтроллере, и куда они отображены в памяти. В моём случае это RM0368 — 847-страничный reference manual, но целиком читать мы его не будем: интересующие нас сейчас части — это 2.3 Memory map, из которой мы узнаём, что регистры, управляющие GPIOC, отображаются, начиная с адреса 0x40020800, и дальше рекомендуется свериться с разделом 8.4.11 для подробностей. Поскольку я не знаком с использованием GPIO на этом микроконтроллере, от краткого содержания промотаю немного назад, на начало раздела 8.4 GPIO registers. Отсюда мы узнаём, что регистр GPIOC_ODR (output data register) находится по смещению 0x14 среди прочих GPIOC_* регистров, и в нём нас интересует 13-й бит, а верхнюю половину менять не следует.
Что ж, отладчик всё ещё остановлен где-то посреди оригинальной прошивки, которая мигает светодиодом. Значит, вывод GPIO уже правильно настроен и, наверное, можно помигать светодиодом в ручном режиме, записывая правильные значения в регистр. Итак, сейчас светодиод не горит, а в регистре лежит
Как видно, 13-й бит выставлен в 1, а старшие биты, которые не следует менять — все нули. Попробуем переключить: 13-й бит выставим в 0, а все остальные и так нули:
Хоба, зажглось!
А теперь потухло! Значит, 0 — светодиод горит, 1 — не горит. Здесь я неявно подразумеваю, что на 32-битной платформе тип
#моргаем_светодиодом
Теперь нужно найти документ, описывающий, какие регистры управляют GPIO на данном конкретном микроконтроллере, и куда они отображены в памяти. В моём случае это RM0368 — 847-страничный reference manual, но целиком читать мы его не будем: интересующие нас сейчас части — это 2.3 Memory map, из которой мы узнаём, что регистры, управляющие GPIOC, отображаются, начиная с адреса 0x40020800, и дальше рекомендуется свериться с разделом 8.4.11 для подробностей. Поскольку я не знаком с использованием GPIO на этом микроконтроллере, от краткого содержания промотаю немного назад, на начало раздела 8.4 GPIO registers. Отсюда мы узнаём, что регистр GPIOC_ODR (output data register) находится по смещению 0x14 среди прочих GPIOC_* регистров, и в нём нас интересует 13-й бит, а верхнюю половину менять не следует.
Что ж, отладчик всё ещё остановлен где-то посреди оригинальной прошивки, которая мигает светодиодом. Значит, вывод GPIO уже правильно настроен и, наверное, можно помигать светодиодом в ручном режиме, записывая правильные значения в регистр. Итак, сейчас светодиод не горит, а в регистре лежит
>>> x/x 0x40020814
0x40020814: 0x00002000
Как видно, 13-й бит выставлен в 1, а старшие биты, которые не следует менять — все нули. Попробуем переключить: 13-й бит выставим в 0, а все остальные и так нули:
>>> set *(int*)0x40020814 = 0
Хоба, зажглось!
>>> set *(int*)0x40020814 = 0x2000
А теперь потухло! Значит, 0 — светодиод горит, 1 — не горит. Здесь я неявно подразумеваю, что на 32-битной платформе тип
int будет считаться 32-битным, а short, наверное, 16-битным. К сожалению, никакого бинарника с содержательной отладочной информацией в gdb сейчас не загружено, поэтому он не знает об изысках вроде uint16_t.#моргаем_светодиодом
Ну и, наконец, напишем минимальную программу. Для её компиляции я буду использовать clang (пока что программа будет только на ассемблере, но пусть так...) и ld.lld для линковки. Во-первых, я лучше знаком с этим тулчейном, а главное, если у вас на компьютере есть clang, значит почти наверняка он умеет работать с кодом для Armv7 и кучи других популярных наборов инструкций, а в случае gcc всё-таки понадобилась бы armv7-специфичная сборка. Конечно, в реальном проекте всё равно будет нужна libc и прочие библиотеки и заголовочные файлы, поэтому в любом случае придётся искать правильный полноценный тулчейн вместе с sysroot, но у нас же сейчас цель — написать свой велосипед полностью с нуля, поэтому можно обойтись первым попавшимся clang-ом и эквивалентами binutils из LLVM для просмотра, что же у нас в итоге лежит внутри ELF-файла.
Для начала прикинем, что придётся писать в ассемблерном файле помимо самих инструкций. Для этого попросим
Часть этого кода можно с натяжкой считать "лишними деталями". Плюс то, что начинается с
#моргаем_светодиодом
Для начала прикинем, что придётся писать в ассемблерном файле помимо самих инструкций. Для этого попросим
clang сгенерировать ассемблерный код (-S) для Armv7-M (-target armv7m-unknown-none — так называемый target triple, например, на обычном десктопе он бы был x86_64-linux-gnu) и напечатать его в терминал (-o -) для максимально простой программы на C — настолько простой, что clang прочитает её прямо со стандартного ввода (-x c -). Более того, я эту программу напишу прямо в командной строке через перенаправление ввода (<<< ...):$ clang -target armv7m-unknown-none -S -x c - -o - <<< 'int f(void) { return 42; }'
clang: warning: unknown platform, assuming -mfloat-abi=soft
clang: warning: unknown platform, assuming -mfloat-abi=soft
clang: warning: unknown platform, assuming -mfloat-abi=soft
.text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu cortex-m3
.eabi_attribute 6, 10 @ Tag_CPU_arch
.eabi_attribute 7, 77 @ Tag_CPU_arch_profile
.eabi_attribute 8, 0 @ Tag_ARM_ISA_use
.eabi_attribute 9, 2 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access
.eabi_attribute 17, 1 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 0 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "-"
.globl f @ -- Begin function f
.p2align 2
.type f,%function
.code 16 @ @f
.thumb_func
f:
.fnstart
@ %bb.0:
movs r0, #42
bx lr
.Lfunc_end0:
.size f, .Lfunc_end0-f
.cantunwind
.fnend
@ -- End function
.ident "Ubuntu clang version 19.1.1 (1ubuntu1)"
.section ".note.GNU-stack","",%progbits
.addrsigЧасть этого кода можно с натяжкой считать "лишними деталями". Плюс то, что начинается с
@ — расставленные компилятором комментарии.#моргаем_светодиодом
Изначально я предполагал, что получу код, который можно загрузить сразу после сброса, и всё заработает, но похоже, что нужно читать подробности по Reset and clock control, а это уже отдельная тема. Поэтому пока напишу лишь про загрузку кода "на ходу" в уже проинициализированный контроллер. Как оказалось, для этого в gdb есть удобная команда load, которой можно передать ELF-файл, и gdb его вгрузит по указанным в файле виртуальным адресам.
Теперь нужно этот ELF как-то получить. А что для этого нужно? Содержимое секций — это в моём случае машинный код, тут, вроде, всё понятно: создаю entry.S, пишу туда ассемблерные инструкции, дописываю немного служебных директив вроде
Судя по тому, что указано в таблицах в разделе 2.4 Boot configuration, в каком бы мы режиме ни загрузились, embedded SRAM всегда начинается с адреса 0x20000000 — строчка
мы сначала превращаем
... и #моргаем_светодиодом ! Фух, на сегодня всё :)
Теперь нужно этот ELF как-то получить. А что для этого нужно? Содержимое секций — это в моём случае машинный код, тут, вроде, всё понятно: создаю entry.S, пишу туда ассемблерные инструкции, дописываю немного служебных директив вроде
.text и .p2align, и готово. В объектном файле, сгенерированном компилятором (здесь — entry.o) есть несколько секций: таблица строк с именем .strtab, секция с описанием релокаций, которые линкер должен применить к коду (но это ещё одна отдельная тема, а в финальном бинарнике в нашем случае их остаться всё равно не должно) и т. д. Также есть секция .text, содержащая машинный код, который "ну, допустим, по нулевому адресу". А откуда берутся адреса загрузки в финальном файле? Тут на помощь придёт другой тип "исходника": linker script. В первом приближении это описание, как взять пачку входных файлов и перекомпоновать секции оттуда, чтобы получить выходной файл. Поскольку мне хочется сделать максимально простой пример, скрипт получился такой:SECTIONS {
. = 0x20000000;
.text : { *(.text) }
/DISCARD/ : { INPUT_SECTION_FLAGS(SHF_ALLOC) *(*) }
}Судя по тому, что указано в таблицах в разделе 2.4 Boot configuration, в каком бы мы режиме ни загрузились, embedded SRAM всегда начинается с адреса 0x20000000 — строчка
. = 0x20000000 присваивает это значение "счетчику" текущего адреса выходного файла. Далее говорится собрать секции с именем .text из всех входных файлов (*(.text)) и сформировать из этого секцию .text результата. А всё остальное, что могло бы быть загружено в память (в отличие от таблицы строк и прочей "справочной" информации) — выкинуть (discard): плата, конечно, недорогая, так что градус паранойи поменьше, да и при записи во всякие однократно записываемые "фьюзы", наверняка, есть какая-нибудь защита от дурака, но всё-таки лучше не грузить что попало куда попало, если это не планировалось. При помощи командclang -target thumbv7m-unknown-none -c entry.S
ld.lld -o image.elf -T image.lds entry.o
мы сначала превращаем
entry.S в бинарник entry.o, а потом при помощи image.lds, приведённого выше, формируем image.elf. Собираем, загружаем с помощью gdb:>>> load image.elf
Loading section .text, size 0x32 lma 0x20000000
Start address 0x00000000, load size 50
Transfer rate: 400 bits in <1 sec, 50 bytes/write.
>>> set $pc=0x20000000
>>> continue
... и #моргаем_светодиодом ! Фух, на сегодня всё :)
Между прочим, 31 марта (то есть за день до 1 апреля) празднуется международный день бэкапа!
Раз уж сегодня День Дурака, похвастаюсь своими недавним достижением из рубрики "я сделяль": колпачок для разъёма XT60, отлитый из термоклея. По моему, идея повторить призматическую форму разъёма, изготовив опалубку из малярного скотча — это достаточно, ну эээ... празднично. Не то, чтобы такой разъём был необходим, чтобы запитать Orange Pi 5 (это как раз тот самый #маленький_но_гордый_сервер), но просто я уже задолбался экспериментировать со способами уменьшить падение напряжения (нет, на OPi5 всё нормально с питанием через Type-C, просто захотелось избавиться от мешанины кабелей).
Disclaimer: если что, я ни в коем случае не призываю подобное повторять. Тем более там, где разъёмы на десятки ампер действительно необходимы. Просто конкретно мне конкретно в этом случае такой способ показался допустимым (колпачка в комплекте не было, надеть термоусадку и не усадить её при пайке было проблематично, а сооружать комок хитро подсунутой изоленты не хотелось).
Disclaimer: если что, я ни в коем случае не призываю подобное повторять. Тем более там, где разъёмы на десятки ампер действительно необходимы. Просто конкретно мне конкретно в этом случае такой способ показался допустимым (колпачка в комплекте не было, надеть термоусадку и не усадить её при пайке было проблематично, а сооружать комок хитро подсунутой изоленты не хотелось).
Захотел купить более удобное жало для паяльника, решил загуглить, что именно подразумевается под обозначением "900M-T-I" (запрос содержал конкретно эту строку без других слов), а гугл мне предложил помочь с математическими задачками (что бы под этим ни подразумевалось). Такой формат "быстрых ответов" я увидел впервые.
Из примера на cppreference:
class ThreadsafeCounter
{
mutable std::mutex m; // The "M&M rule": mutable and mutex go together
int data = 0;
// ...
}
🔥3
На Алиэкспрессе каталог каждого продавца может быть сгруппирован по категориям, и вот сразу у нескольких продавцов радиодеталей обнаружилась загадочная категория с названием "повел". В принципе, иногда даже "технические" аббревиатуры хорошо звучат, например, "мосфет", а уж "маркетинговый" термин тем более будет красивым. Хоть я и не планировал покупать загадочные по́велы, но интересно же, что так назвали — наверное, какую-нибудь мудрёную и современную микросхему, подающую питание на что-нибудь ещё более заковыристое и высокотехнологичное — ну, там, от power и electronics...
Почему-то в этой категории были просто какие-то светодиоды. Ну, думаю, может особый тип для каких-нибудь умных светодиодных лент... А потом до меня дошло: светодиод — это же грамматическая форма свинца: lead / led / LED. Ну а в названии категории потерялись точки над "ё".
Почему-то в этой категории были просто какие-то светодиоды. Ну, думаю, может особый тип для каких-нибудь умных светодиодных лент... А потом до меня дошло: светодиод — это же грамматическая форма свинца: lead / led / LED. Ну а в названии категории потерялись точки над "ё".
❤2
Загадка: что может содержать файл с именем proton-pack.c в ядре Linux?
Ответ:противодействие уязвимостям типа Spectre 😀
Пояснение в комментарии в "шапке":"If there's something strange in your neighbourhood, who you gonna call?"
Ответ:
Пояснение в комментарии в "шапке":
В копилку научного юмора и искусства в целом: Dance Your PhD 2025. Если обладатели главного приза кажутся слишком шумными, то вот, например, красивый спокойный клип про лазерное охлаждение атомов до сверхнизких температур что бы это ни значило :)
YouTube
Laser Cooling & Ultracold Atoms | Dance Your PhD 2025 [Physics & AI/Quantum Winner]
Lasers are awesome. Not only because they look cool, but also because they are extremely useful to science. It might sound counter-intuitive, but to make the coldest things that exist, possibly in the entire universe, we need lasers. By carefully tuning the…
Обновил телефон (с Самсунга обратно на Моторолу и прекрасно себя чувствую), обновил ОС на телефоне. После обновления до Android 15 захотел поставить из F-Droid уже упоминавшийся на этом канале SatStat, да не тут то было: оказалось, что в Android 15 слишком старые приложения поставить нельзя (документация). Ну, то есть, не совсем так: во-первых, судя по всё той же странице документации, гайки начали закручивать ещё в Android 14. Во-вторых, поставить приложение всё же можно — с компьютера.
Итак, что я наблюдаю в F-Droid: плашка "Это приложение было разработано для старой версии Андроид..." (что пока ещё ничего не означает: у самого приложения F-Droid, через которое я смотрю каталог, в собственной карточке такая же плашка и ничего) и сказано, что совместимых с моим устройством версий нет. Кнопки "установить", соответственно, тоже. Проверяю предположение: из F-Droid на телефоне перекидываю через любой мессенджер ссылку на приложение себе же в браузер на компьютере. Из списка версий выбираю какую-нибудь посвежее, скачиваю APK-файл и пробую установить через ADB (добавлены переводы строк, чтобы не так сильно растягивать текст по горизонтали):
Ага, то самое изменение из Android 15. По совету из всё той же документации добавляю опцию
Работает! Едва ли я бы хотел таким способом ставить что-то из Google Play — хотя бы потому, что не совсем понятно, откуда брать "эталонный" APK, который точно-точно без дополнительных троянов, да и при наличии альтернатив, возможно, стоит поискать какое-нибудь менее заброшенное приложение. Но к программам из репозитория* F-Droid лично у меня доверия больше, а правильный APK можно без лишних плясок с бубном скачать по удобной ссылке. В общем, опцияудобная полезная, но использовать нужно на свой страх и риск: здесь никто за вас новому apk подпись не проверит.
* Почему важно уточнение именно о "репозитории" F-Droid — потому что существуют сторонние репозитории в том же формате, их так же можно добавлять в официальный клиент F-Droid, но это будут уже совершенно независимые, не контролируемые и не проверяемые авторами F-Droid источники ПО.
#android
Итак, что я наблюдаю в F-Droid: плашка "Это приложение было разработано для старой версии Андроид..." (что пока ещё ничего не означает: у самого приложения F-Droid, через которое я смотрю каталог, в собственной карточке такая же плашка и ничего) и сказано, что совместимых с моим устройством версий нет. Кнопки "установить", соответственно, тоже. Проверяю предположение: из F-Droid на телефоне перекидываю через любой мессенджер ссылку на приложение себе же в браузер на компьютере. Из списка версий выбираю какую-нибудь посвежее, скачиваю APK-файл и пробую установить через ADB (добавлены переводы строк, чтобы не так сильно растягивать текст по горизонтали):
$ adb install /tmp/com.vonglasow.michael.satstat_30600.apk
Performing Streamed Install
adb: failed to install /tmp/com.vonglasow.michael.satstat_30600.apk:
Failure [INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target
at least SDK version 24, but found 23]
Ага, то самое изменение из Android 15. По совету из всё той же документации добавляю опцию
--bypass-low-target-sdk-block:$ adb install --bypass-low-target-sdk-block /tmp/com.vonglasow.michael.satstat_30600.apk
Performing Streamed Install
Success
Работает! Едва ли я бы хотел таким способом ставить что-то из Google Play — хотя бы потому, что не совсем понятно, откуда брать "эталонный" APK, который точно-точно без дополнительных троянов, да и при наличии альтернатив, возможно, стоит поискать какое-нибудь менее заброшенное приложение. Но к программам из репозитория* F-Droid лично у меня доверия больше, а правильный APK можно без лишних плясок с бубном скачать по удобной ссылке. В общем, опция
* Почему важно уточнение именно о "репозитории" F-Droid — потому что существуют сторонние репозитории в том же формате, их так же можно добавлять в официальный клиент F-Droid, но это будут уже совершенно независимые, не контролируемые и не проверяемые авторами F-Droid источники ПО.
#android
Кстати, похоже, что в Андроиде есть разрешения, которые в принципе доступны и без рутования телефона, но выдаваться должны через ADB, а не обычный пользовательский интерфейс (тут, впрочем, нужно оговориться, что я совсем не Андроид-разработчик, и, например, не вполне уверен, что можно заявлять о неком стандартном интерфейсе управления правами, хотя в документации я и вижу достаточно чёткое разделение на normal, runtime a.k.a. dangerous permissions и ряд других типов).
Например, в репозитории F-Droid есть приложение LogFox, которое при первом запуске просит выдать права через ADB:
После чего при начале записи логов система переспросит уже на экране устройства, хотим ли мы позволить приложению читать "глобальные" логи. Если разрешили, можно видеть, например, такое:
Или вот, например, SaverTuner: при первом запуске ждёт, пока ему не выдадут разрешение:
После чего позволяет выбирать один из нескольких профилей экономии, а внутри профиля — переключать большое количество параметров из серии "разрешить анимации", "отложить полное резервное копирование" и т.д.
Лично я для себя пока не нашёл "полезный в быту" пермишн и приложение, его использующее, но сам факт существования таких разрешений кажется мне полезным знанием.
#android
Например, в репозитории F-Droid есть приложение LogFox, которое при первом запуске просит выдать права через ADB:
adb shell pm grant com.f0x1d.logfox android.permission.READ_LOGS
После чего при начале записи логов система переспросит уже на экране устройства, хотим ли мы позволить приложению читать "глобальные" логи. Если разрешили, можно видеть, например, такое:
libPowerHal: [setClusterFreq] system_server: sysfs_freq set cpu freq: 1750000 -1 1850000 -1
Или вот, например, SaverTuner: при первом запуске ждёт, пока ему не выдадут разрешение:
adb shell pm grant s1m.savertuner android.permission.WRITE_SECURE_SETTINGS
После чего позволяет выбирать один из нескольких профилей экономии, а внутри профиля — переключать большое количество параметров из серии "разрешить анимации", "отложить полное резервное копирование" и т.д.
Лично я для себя пока не нашёл "полезный в быту" пермишн и приложение, его использующее, но сам факт существования таких разрешений кажется мне полезным знанием.
#android
Первого апреля я уже хвастался колпачком для разъёма XT60, отлитым из термоклея. И вот недавно история получила не менее первоапрельское продолжение... Как я уже говорил, столь суровый разъём требовался, чтобы подключить одноплатник, питающийся от пяти вольт, без значительного падения напряжения на разъёме, потому что со штекерами-бочонками я к тому моменту уже намучился. Но кроме одноплатника линии 5 и 12 вольт шли ещё и на "метеостанцию" на ESPHome, роутер и ethernet-свич, поэтому мешанина проводов была та ещё. Я захотел хоть как-то это смотать и убрать в обычную электромонтажную распределительную коробку, а чтобы упростить себе перекоммутацию в стеснённых условиях, повесить эту коробку на край столешницы при помощи примерно таких крючков с Алиэкспресса.
Начитавшись отзывов, мол, "крючки хорошие, но мне пришли без липучек", осмотрел то, что прислали мне. С грустью отметил, что у меня тоже просто крючки, и стал лепить на двусторонний скотч. А они, заразы, ещё и как из вредности к скотчу не липнут... Ладно, к распределительной коробке с горем пополам прилепил на термоклей (который, разумеется, тоже отваливался, но я упорный, я через край чисто механически прихватил, а потом долго подрезал ножом, чтобы нигде не клинило...). Но не буду же я к столу ответную часть тоже на термоклей лепить... Тут я вспомнил про сантехнический скотч, решил сделать из него кольцо клеевой стороной наружу и хоть как-то уже прилепить ответную часть крючка.
Отклеил от мотка скотча небольшой участок, приложил туда крючок... а он отваливается!!! Да вы что, издеваетесь, что же это за крючки такие, что их даже чудо-скотч не берёт?!? И тут-то я пригляделся и заметил, что на скотче осталась прозрачная плёночка... 🤦♂️ В общем, всё нормально: честь сантехнического скотча восстановлена, а я усвоил урок, что если тебе кажется, что некая поверхность отваливается от двустороннего скотча, термоклея и прочих нормальных липучек, словно специально спроектирована, чтобы легко отваливаться от любого клея, то возможно, тебе не кажется. После снятия защитной плёнки "предустановленный" клеевой слой крючка благополучно прилип, куда следовало.
Начитавшись отзывов, мол, "крючки хорошие, но мне пришли без липучек", осмотрел то, что прислали мне. С грустью отметил, что у меня тоже просто крючки, и стал лепить на двусторонний скотч. А они, заразы, ещё и как из вредности к скотчу не липнут... Ладно, к распределительной коробке с горем пополам прилепил на термоклей (который, разумеется, тоже отваливался, но я упорный, я через край чисто механически прихватил, а потом долго подрезал ножом, чтобы нигде не клинило...). Но не буду же я к столу ответную часть тоже на термоклей лепить... Тут я вспомнил про сантехнический скотч, решил сделать из него кольцо клеевой стороной наружу и хоть как-то уже прилепить ответную часть крючка.
Отклеил от мотка скотча небольшой участок, приложил туда крючок... а он отваливается!!! Да вы что, издеваетесь, что же это за крючки такие, что их даже чудо-скотч не берёт?!? И тут-то я пригляделся и заметил, что на скотче осталась прозрачная плёночка... 🤦♂️ В общем, всё нормально: честь сантехнического скотча восстановлена, а я усвоил урок, что если тебе кажется, что некая поверхность отваливается от двустороннего скотча, термоклея и прочих нормальных липучек, словно специально спроектирована, чтобы легко отваливаться от любого клея, то возможно, тебе не кажется. После снятия защитной плёнки "предустановленный" клеевой слой крючка благополучно прилип, куда следовало.
Недавно я решил приобщиться к современным технологиям и обновить свой роутер до чего-нибудь на AArch64 (64-битном ARM) вместо MIPS, и с поддержкой нового wi-fi стандарта "ax". Как оказалось, относительно недорогие устройства в продаже есть, в том числе и с критичной для меня возможностью поставить OpenWRT, но почти у всех 4 ethernet-порта, а не 5, как было на моих прошлых роутерах. Это проблема: один порт WAN, ещё два — мой настольный компьютер и провод к powerline-адаптеру, ещё один — на торрентораздавалку, а хочется иметь запасной порт "на мелкие расходы": временно какой-нибудь raspberry pi подключить для настройки, например.
Требований к устройству у меня было и без того немало, а критерий наличия 5+ ethernet-портов портил картину окончательно. В общем, небольшой лайфхак: вычеркнуть из списка требований количество портов (ну, на самом деле, снизить его до безобидных 3+) поможет неуправляемый ethernet-коммутатор — что-то вроде вот такого устройства. Подобный коммутатор — штука довольно простая, не требующая и, как минимум в моём случае, даже не допускающая настройки: просто воткнул сетевые кабели, подал питание, и через мгновение она уже пуляет входящие ethernet-фреймы в те порты, где последний раз видели нужный MAC-адрес — никаких тебе линуксов, веб-интерфейсов настройки и долгой загрузки при включении. В итоге требование на минимальное количество ethernet-портов роутера снизилось до 3, то есть количества зон файрвола: WAN, LAN и "публично доступные серверы". Правда, потребуется запитать ещё одно устройство от розетки — для меня это, к счастью, оказался пройденный этап, но это уже совсем другая история...
Требований к устройству у меня было и без того немало, а критерий наличия 5+ ethernet-портов портил картину окончательно. В общем, небольшой лайфхак: вычеркнуть из списка требований количество портов (ну, на самом деле, снизить его до безобидных 3+) поможет неуправляемый ethernet-коммутатор — что-то вроде вот такого устройства. Подобный коммутатор — штука довольно простая, не требующая и, как минимум в моём случае, даже не допускающая настройки: просто воткнул сетевые кабели, подал питание, и через мгновение она уже пуляет входящие ethernet-фреймы в те порты, где последний раз видели нужный MAC-адрес — никаких тебе линуксов, веб-интерфейсов настройки и долгой загрузки при включении. В итоге требование на минимальное количество ethernet-портов роутера снизилось до 3, то есть количества зон файрвола: WAN, LAN и "публично доступные серверы". Правда, потребуется запитать ещё одно устройство от розетки — для меня это, к счастью, оказался пройденный этап, но это уже совсем другая история...
👍1
Айтишники: если что-то не соединяется без усилия, не жми, подумай!
Также айтишники: https://youtu.be/q0HpwPy8i6w
Также айтишники: https://youtu.be/q0HpwPy8i6w
YouTube
Как установить кулер AMD с пружинным креплением?
В этом видео показывается, как безопасно, без риска повредить материнку, установить кулер с пружинным креплением (под сокет AM4, AM3, FM). Также указаны нюансы монтажа популярных кулеров Gammaxx 400, 300, 200T и боксовых кулеров.
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬…
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬…
В только что лет уяснил для себя очевидный "опосля" факт: понадобилось купить монитор, и по такому случаю решил проэкспериментировать, и вместо 24" 1080p@60Hz купить 27" 1440p@100Hz (увеличение диагонали — не самоцель, просто достаточно бюджетных 24" с такими параметрами я не нашёл, а так бы только рад был ещё большей плотности пикселей).
Иногда в обсуждениях мониторов с большими диагоналями или частотами обновления я видел сетования на то, что придётся на новую видеокарту раскошеливаться, но проходил мимо с мыслями: "Ну мне-то не для игр, мне чтобы за целый день от глядения в текст глаза не уставали, а с отрисовкой интерфейсов даже встроенный видеочип всегда справлялся без вопросов". В итоге принёс монитор, подключил — и фигушки: нативное разрешение 2560x1440 завелось без проблем, но на 60Hz. Загуглил, и оказалось, что через имеющийся на моей системной плате видеовыход HDMI версии 1.4 можно прокачать только 8.16 Gbit/s несжатых видеоданных, а значит 2560x1440x100x24 ну никак не пролезет. Кстати, по быстрым прикидкам должно бы пролезать аж 92Hz, но Википедия пишет про 85Hz — видимо, накладные расходы на "поля" вокруг видимой области или просто 85Hz — это частота, которая "ну хоть сколько-то стандартная в отличие от".
Почему с нативным разрешением не завелось, скажем, на 75Hz — не знаю, почему-то в EDID монитора такой режим не прописан. Буду разбираться, но это явно выглядит не столь неожиданным осознанием, как то, что я, человек далёкий от AAA-игр, столь запросто умудрился упереться в ограничение интерфейса HDMI.
Иногда в обсуждениях мониторов с большими диагоналями или частотами обновления я видел сетования на то, что придётся на новую видеокарту раскошеливаться, но проходил мимо с мыслями: "Ну мне-то не для игр, мне чтобы за целый день от глядения в текст глаза не уставали, а с отрисовкой интерфейсов даже встроенный видеочип всегда справлялся без вопросов". В итоге принёс монитор, подключил — и фигушки: нативное разрешение 2560x1440 завелось без проблем, но на 60Hz. Загуглил, и оказалось, что через имеющийся на моей системной плате видеовыход HDMI версии 1.4 можно прокачать только 8.16 Gbit/s несжатых видеоданных, а значит 2560x1440x100x24 ну никак не пролезет. Кстати, по быстрым прикидкам должно бы пролезать аж 92Hz, но Википедия пишет про 85Hz — видимо, накладные расходы на "поля" вокруг видимой области или просто 85Hz — это частота, которая "ну хоть сколько-то стандартная в отличие от".
Почему с нативным разрешением не завелось, скажем, на 75Hz — не знаю, почему-то в EDID монитора такой режим не прописан. Буду разбираться, но это явно выглядит не столь неожиданным осознанием, как то, что я, человек далёкий от AAA-игр, столь запросто умудрился упереться в ограничение интерфейса HDMI.
🤔1
Линуксоидное воспоминание из школьных времён, или что бывает, когда не слушают мудрые советы и парсят вывод
Пункт первый: однажды я узнал, что у команды
Изучать, чем отличаются опции
Пункт второй: я захотел написать shell-скрипт, который пройдёт по списку файлов в каталоге и для каждого создаст ещё какой-то файл с "производным" именем. Ну, что-то вроде "был
Пункт третий: решив посмотреть, что же мой скрипт нагенерировал, я с удивлением обнаружил, что теперь цвет намертво въелся в имена свежесозданных файлов — хоть ты с
Сейчас, правда, coreutils поумнели, и попытка напечатать "раскрашенное" имя сразу выдаёт неладное, но всё-таки воспроизвести фокус труда не составляет. А разгадка — когда-нибудь в следующих сериях.
ls.Пункт первый: однажды я узнал, что у команды
ls (если точнее, то у той её реализации, что "в линуксе", то есть из GNU coreutils, но тогда я, наверное, о таких деталях не задумывался) есть параметр --color[=WHEN], принимающий одно из трёх значений: never, auto и always (при этом "always" подразумевается по умолчанию, если передан просто --color). С этим параметром каталоги будут печататься одним цветом, архивы — другим, текстовые файлы — третьим... Недолго думая, прописал себе на постоянной основе в .bash_profilealias ls="ls --color"
Изучать, чем отличаются опции
auto и always — зачем?..Пункт второй: я захотел написать shell-скрипт, который пройдёт по списку файлов в каталоге и для каждого создаст ещё какой-то файл с "производным" именем. Ну, что-то вроде "был
file.c — создадим file.c.backup1". Сейчас по запросу don't parse ls гугл выдаёт довольно много всего — наверное, выдавал и тогда, но я же был не какой-то зануда, соблюдающий best practices, я выяснял, как сохранить вывод ls в переменную и разобрать его на части.Пункт третий: решив посмотреть, что же мой скрипт нагенерировал, я с удивлением обнаружил, что теперь цвет намертво въелся в имена свежесозданных файлов — хоть ты с
--color=always печатай, хоть с never...Сейчас, правда, coreutils поумнели, и попытка напечатать "раскрашенное" имя сразу выдаёт неладное, но всё-таки воспроизвести фокус труда не составляет. А разгадка — когда-нибудь в следующих сериях.