По мотивам вчерашнего обсуждения в https://news.1rj.ru/str/theyforcedme/2895
Уточню что моя цель - писать код который легко поддерживать, дописывать и менять его логику, и это требование я выношу в основные.
Довольно частая ситуация при написании программы - захват ресурса и освобождение его при выходе из функции. Для примера ресурса возьмем память. (тут и далее я прошу учитывать что я ограничен в объеме текста, и между захватом - освобождением ресурса вполне могут быть страница-две кода)
Чтобы не отвлекать программиста на другие функции мы можем использовать что-то наподобие "анонимной функции", например цикл из одной итерации. Цикл из одной итерации может быть любого вида, while, for, но do {} while (false); это довольно распространенная и понятная идиома, не требующая дополнительных переменных.
Уточню что моя цель - писать код который легко поддерживать, дописывать и менять его логику, и это требование я выношу в основные.
Довольно частая ситуация при написании программы - захват ресурса и освобождение его при выходе из функции. Для примера ресурса возьмем память. (тут и далее я прошу учитывать что я ограничен в объеме текста, и между захватом - освобождением ресурса вполне могут быть страница-две кода)
void foo(){
// выделяем память
DataType* data = malloc(sizeof(Data));
// работаем с памятью
process(data);
// освобождаем память
free(data);
}
Пока что все выглядит ок. Но требования меняются, и теперь нам надо обрабатывать данные в зависимости от того что вернула предыдущая обработка.void foo(){
DataType* data = malloc(sizeof(Data));
if(process_1(data)){
if(process_2(data)){
if(process_3(data)){
process_4(data);
}
}
}
free(data);
}
Цикломатическая сложность растет, и если мы продолжим писать в этом же стиле - рано или поздно мы уйдем ifами за правый край экрана (ну или запутаемся в блоках). Код становится неприятно читать и сложно поддерживать, давайте его перепишем на множественный возврат из функции.void foo(){
DataType* data = malloc(sizeof(Data));
if(!process_1(data)){
free(data);
return;
}
if(!process_2(data)){
free(data);
return;
}
if(!process_3(data)){
free(data);
return;
}
process_4(data);
free(data);
}
Это выглядит и читается приемлимо, но поддерживать этот код стало еще сложнее, например очень легко можно забыть освободить память перед возвратом. Для борьбы с этим человечество придумало блоки finally и идиому "Получение ресурса есть инициализация" (RAII). К сожалению мы любим простреленные ноги и пишем на языке Си, в котором подобное невозможно реализовать не превратив код обмазанный макросами в некрономикоподобный диалект. Что же мы можем сделать?void foo(){
DataType* data = malloc(sizeof(Data));
if(!process_1(data)) goto finally;
if(!process_2(data)) goto finally;
if(!process_3(data)) goto finally;
process_4(data);
finally:
free(data);
}
К сожалению столь лаконичный код получился с использованием goto. Использование оператора goto в целом критикуется, так как программу с его использованием читать на порядки сложнее, как минимум неявны начала и концы переходов, для подробной критики см "Go To Statement Considered Harmful" Эдсгера Дейкстры. Нам нужен блок который бы исполнялся один раз и из которого мы можем выйти оператором отличным от goto, например возьмем отдельную функцию.void process_data(DataType* data){
assert(data);
if(!process_1(data)) return;
if(!process_2(data)) return;
if(!process_3(data)) return;
process_4(data);
}
void foo(){
DataType* data = malloc(sizeof(Data));
process_data(data);
free(data);
}
Приемлимый и поддерживаемый вариант, но требующий передачи ресурса в аргумент (а следовательно проверки аргумента) и требующий от программиста скакать по функциям, плюс загрязняющий область видимости.Чтобы не отвлекать программиста на другие функции мы можем использовать что-то наподобие "анонимной функции", например цикл из одной итерации. Цикл из одной итерации может быть любого вида, while, for, но do {} while (false); это довольно распространенная и понятная идиома, не требующая дополнительных переменных.
void foo(){
DataType* data = malloc(sizeof(Data));
do {
if(!process_1(data)) break;
if(!process_2(data)) break;
if(!process_3(data)) break;
process_4(data);
} while(false);
free(data);
}
В результате мы получили идиому которая явно указывает начало-конец перехода, выделяет блоком код который можно менять не отвлекаясь на освобождение ресурса, и в целом код который довольно лаконично выглядит.Telegram
Меня заставили создать канал
Вы когда-нибудь видели такие конструкции?
Я программировать практически не умею, увидела такое впервые. Высший пилотаж 🤯
Я программировать практически не умею, увидела такое впервые. Высший пилотаж 🤯
🔥6
Сегодня очень простенькое про эмбед. Часто вижу вопросы "Как мне сделать так чтобы функцию прерывания не пришлось определять в моей библиотеке".
У нас в флиппере довольно большая и сложная прошивка, которая требует в разные моменты времени разных функций-обработчиков прерываний. Это устроено следующим образом.
Есть статический указатель на функцию:
Ознакомиться с полным кодом можно тут:
https://github.com/flipperdevices/flipperzero-firmware/blob/dev/firmware/targets/f7/furi-hal/furi-hal-interrupt.c
https://github.com/flipperdevices/flipperzero-firmware/blob/dev/firmware/targets/f7/furi-hal/furi-hal-interrupt.h
У нас в флиппере довольно большая и сложная прошивка, которая требует в разные моменты времени разных функций-обработчиков прерываний. Это устроено следующим образом.
Есть статический указатель на функцию:
typedef void (*FuriHalInterruptISR)();Есть функция которая устанавливает этот указатель:
volatile FuriHalInterruptISR furi_hal_tim_tim1_isr = NULL;
void furi_hal_interrupt_set_timer_isr(TIM_TypeDef* timer, FuriHalInterruptISR isr) {
if (timer == TIM1) {
furi_hal_tim_tim1_isr = isr;
}
}
И в функции прерывания мы просто вызываем этот указатель если он установлен:/* Timer 1 Update */Это позволяет в любом месте программы установить вашу функцию в прерывание, не трогая сам код прерывания:
void TIM1_UP_TIM16_IRQHandler(void) {
if (furi_hal_tim_tim1_isr) {
furi_hal_tim_tim1_isr();
}
}
furi_hal_interrupt_set_timer_isr(TIM1, furi_hal_irda_tim_rx_isr);И даже если нужно, очистить прерывание от выполнения вашей функции:
furi_hal_interrupt_set_timer_isr(TIM1, NULL);ВАЖНО. Этот подход подразумевает что в момент установки\очистки прерывания соответствующая периферия выключена, так как установка указателя не атомарна. Функция которая была бы защищена от этого должна оборачивать код в критическую секцию, запрещая вызов прерываний:
void furi_hal_interrupt_set_timer_isr(TIM_TypeDef* timer, FuriHalInterruptISR isr) {
__disable_irq();
if (timer == TIM1) {
furi_hal_tim_tim1_isr = isr;
}
__enable_irq();
}
Так же этот подход можно расширить на установку нескольких функций в одно прерывание, но тут уже возникают вопросы к быстродействию и организации списка функций. Если интересно — могу расписать примерные подходы в отдельном посте.Ознакомиться с полным кодом можно тут:
https://github.com/flipperdevices/flipperzero-firmware/blob/dev/firmware/targets/f7/furi-hal/furi-hal-interrupt.c
https://github.com/flipperdevices/flipperzero-firmware/blob/dev/firmware/targets/f7/furi-hal/furi-hal-interrupt.h
Today I Learned
Можно увеличить выходной ток линейного регулятор добавлением резистора в параллель, но нагрузка должна кушать какой-то минимальный ток. Что и делали в древних схемах:
https://www.maximintegrated.com/en/design/technical-documents/app-notes/3/3865.html
Можно увеличить выходной ток линейного регулятор добавлением резистора в параллель, но нагрузка должна кушать какой-то минимальный ток. Что и делали в древних схемах:
https://www.maximintegrated.com/en/design/technical-documents/app-notes/3/3865.html
Помимо очевидных способов выстрелить себе в ногу на языке Си есть еще много совсем неочевидных, самые интересные:
1) думать что Си низкоуровневый язык близкий к железу
2) не отключать в компиляторе следование некоторым частям стандарта
3) надеяться на переносимость кода между версиями компилятора (хотя многие наверное уже стреляли этим способом в ногу)
Шикарная статья про это все: http://cmustdie.com/
1) думать что Си низкоуровневый язык близкий к железу
2) не отключать в компиляторе следование некоторым частям стандарта
3) надеяться на переносимость кода между версиями компилятора (хотя многие наверное уже стреляли этим способом в ногу)
Шикарная статья про это все: http://cmustdie.com/
// Partial null pointer dereference protection
LL_MPU_Disable();
LL_MPU_ConfigRegion(
LL_MPU_REGION_NUMBER0, 0x00, 0x0,
LL_MPU_REGION_SIZE_1MB
| LL_MPU_REGION_PRIV_RO_URO
| LL_MPU_ACCESS_BUFFERABLE
| LL_MPU_ACCESS_CACHEABLE
| LL_MPU_ACCESS_SHAREABLE
| LL_MPU_TEX_LEVEL1
| LL_MPU_INSTRUCTION_ACCESS_ENABLE
);
LL_MPU_Enable(LL_MPU_CTRL_PRIVILEGED_DEFAULT);
А как вы защищаетесь от разыменовывания нулевого указателя? (inb4 ржавеем)Смотрел тут обзор на паяльник SQ-D60, и это какой-то переходниковый чад кутежа. Питание barrel jack -> type-c (обычный type-c не будет работать), жало TS100 -> jack 3.5", причем последний скрыт от пользователя вообще, не понятно зачем так делать. Ну и да, jack 3.5 передает 60 ватт, почему бы и нет.
Forwarded from Zhovner Hub
Пока Флипперы производятся, мы решили дать возможность живым разработчикам познакомиться с нашим кодом и железом, а также потусоваться в приятной компании чисто по-кайфу.
https://habr.com/ru/company/flipperdevices/blog/589585/
https://habr.com/ru/company/flipperdevices/blog/589585/
Хабр
Флиппер Хакатон в Москве
Приходи на Хакатон в Москве — получи Flipper Zero Flipper Zero — проект карманного мультитула для хакеров в формфакторе тамагочи, который мы разрабатываем. Предыдущие посты [ 1 ],[ 2 ],[ 3 ],[ 4 ],[ 5...
This media is not supported in your browser
VIEW IN TELEGRAM
Играюсь с Flipper Zero и Midi. Worst synth ever.
This media is not supported in your browser
VIEW IN TELEGRAM
Неделю назад в Flipper Zero прошел хакатон, к сожалению без меня. Этой ночью я устроил себе собственный хакатон и теперь Flipper умеет загружать приложения с СД карты.
Я не правил ни единой строчки кода в приложении которое загружается, взял из репозитария как есть.
Я не правил ни единой строчки кода в приложении которое загружается, взял из репозитария как есть.
🔥2
Ну и мой любимый способ написания мейкфайлов:
$(error Now you're on your own, please bring the Makefile to match CMakeLists.txt)🔥3