CDEblog
77 subscribers
10 photos
5 videos
2 files
81 links
Circuit Design Engineer BLOG

Программирование, схемотехника может быть что-то ещё 🤷🏻‍♂️

Для личного контакта: @devprodest
Блог в интернете: https://cdeblog.ru

Немного о кулинарии: @cdefood
Download Telegram
Интересная статья с описанием нескольких подводных камней, которые могут возникнуть при разработке электронного устройства.

https://interrupt.memfault.com/blog/schematic-review-checklist
А давайте в честь дня программиста пройдем ИТ-Диктант?

https://dictation.72to.ru/home/events/b2cecef4-98c6-48f7-9b19-7409e73e1eff

Только честно и не подглядывая в поисковые системы
👩‍💻👩‍💻 Пробовали работать с памятью выделенной в куче как с многомерным массивом?

Не говорите, что писали нечто вроде:

uint8_t * a = malloc(100);
*(a + 2*10 + 4) = 42; // a[2][4]=42

На самом деле можно сделать нормальный указатель и пользоваться выделенной памятью как массивом:

uint8_t (* a)[10][10] = malloc(100);
(*a)[2][4] = 42

Особенно классно, что можно объявлять массивы переменной длины

uint8_t (* a)[x][y][z] = malloc(sizeof(*a));

#c #cpp
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
Название, описание и применяемость основных моделей данных.

Наверняка видели ключ -mabi=... в строке вызова компилятора, это оно самое.
👍1
К слову о пасхалочках, всем же нравится пасхалки в программах, играх, на печатных платах и кристаллах микросхем?

Нашел в галерее старую фотку шурупа, который прикрутил на даче лет 5 назад.

Это же Pac-Man 😊
😁3👍2
Мне сейчас старый знакомый написал такое сообщение, с просьбой проголосовать. Не поверил, решил проанализировать. Ссылка совсем другая.

‼️Это фишинговое сообщение. Обманом через QR код подключают своё устройства к вашему аккаунту.

‼️Будьте аккуратны. Не переходите по левым ссылкам, даже очень похожим на настоящие.
Please open Telegram to view this post
VIEW IN TELEGRAM
4
Media is too big
VIEW IN TELEGRAM
👩‍💻 В своей работе довольно часто испытываю необходимость быстрой вставки перечислимых значений, будь то индексы массива, значения флагов или список действий. Всегда была проблема со вставкой какого-то значения в середину и изменение всех последующих.

В общем, что бы не страдать решил сделать себе маленькое дополнение для редактора.

Функционал пока скудный, но мои потребности закрывает.

Пользоваться просто: выбираем места где нужно вставить, запускаем команду и по всем курсорам вставляем значение с нужным начальным ззначением и шагом. Можно в хексе, можно в десятичном формате.

https://marketplace.visualstudio.com/items?itemName=ZaikinDenis.vscode-enumerator

#vscode #extension
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
👩‍💻 Заметка для тех кто столкнулся с тем что исчезла ссылка для скачивания расширений для vscode с сайта.

Ниже ссылка, куда нужно вставить необходимые значения.

https://marketplace.visualstudio.com/_apis/public/gallery/publishers/<autor>/vsextensions/<extension>/<version>/vspackage

Пользуйтесь

#vscode #extension
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Закончил сегодня работу над плагином для vscode

Плагин позволяет осуществить базовую аналитику ELF файлов:
🔵Получение информации об Именах символов
🔵Получение информации о размерах символов
🔵Фильтрация и сортировка символов
🔵Оценка размера символов и секций


https://marketplace.visualstudio.com/items?itemName=ZaikinDenis.devprodest-elf-analyzer

По сути это надстройка над утилитами вроде llvm-nm и парсер их вывода

#vscode #extension
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
CDEblog
👩‍💻 Закончил сегодня работу над плагином для vscode Плагин позволяет осуществить базовую аналитику ELF файлов: 🔵Получение информации об Именах символов 🔵Получение информации о размерах символов 🔵Фильтрация и сортировка символов 🔵Оценка размера символов и…
Обновил версию до 0.0.2

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

Отключать ненужные куски на диаграмме нельзя, зато при нажатии сразу фильтруется до нужного символа.

Добавил нормальные тултипы для диаграм
👩‍💻👩‍💻 Variadic macro там где его не просили. Однако без него никак.

Представим, что мы пишем некую обобщенную инициализацию с использованием макроса generic.

void mod1_init(inst1_t*, cfg_t*);
void mod2_init(inst2_t*, cfg_t*);

#define mod_init(x,c) _Generic((x),\
inst1_t*: mod1_init, \
inst2_t*: mod2_init)(x, c)

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

cfg_t cfg = {
.f=1,
.d=3,
};
mod_init(&inst, &cfg);

Ровно до тех пор, пока мы не захотим передать указатель на составной литерал.

➡️А я очень люблю составные литералы, иногда без них совсем никак, а иногда уйма строк кода сворачиваются в пару-тройку (это не тот случай 😁)

mod_init(&inst, &(cfg_t){
.f=1,
.d=3,
});

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

Что бы исправить ситуацию введём в макрос вариадик аргумент, но без всяких "__VA_ARGS__"

#define mod_init(x,c...) _Generic((x),\
inst1_t*: mod1_init,\
inst2_t*: mod2_init)(x, c)

Заметили три точки после параметра "c"?
Есть только одно НО: это не стандарт, а лишь расширение GNU 🤷‍♂️

https://cdeblog.ru/variadic-macro-with-generic

#c #cpp
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🤔1🤩1👌1
👩‍💻👩‍💻 Не нужно кастовать указатели в void* и void* к другим

Хотелось бы озвучить свое мнение о данном вопросе.

Речь пойдет о передачи каких-либо параметров в функцию и возврате указателей различных типов.

Например такие:
// Пример (1)
uint32_t * p = (uint32_t *) malloc(1245);

// Пример (2)
uint32_t (* arr)[32] = malloc(sizeof(* arr));

void * init_fq(void* init_arr);

typedef struct data_t {
void * f1;
data_item_t * f2;
volatile void * f3;
} data_t;

data_t var_s = {
.f1 = (void *)init_fq((void *)(*arr)),
.f2 = (data_item_t *)malloc(sizeof(data_item_t)),
.f3 = &volatile_array,
};

// Пример (3) с потерей квалификаторов
uint32_t * p1 = (void *)var_s.f3; // теряем volatile

С одной стороны кажется, что ни чего плохого в данном коде нет, возможно кроме некоторых специфичных конструкций, ок которых писал ранее.

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

Случай 2 уже не так безобиден. Пока у нас поле f1 структуры имеет тип void* всё хорошо и работает так же как предыдущий случай. Но может настать момент когда тип поля структуры изменится, а возвращаемое значение изменится на что-то иное, например перестанет быть указателем, или станет указателем совсем другого типа, в таком случае не исключается вариант конфликта типов, который не будет подсвечен из-за принудительного каста.

Другим нехорошим обстоятельством (Пример 3) может быть потеря квалификаторов volatile и const, а вот это уже может быть большой проблемой. Начиная от неопределенного поведения и заканчивая падениями в рантайме, не понятно что хуже.

Перепишем код и избавимся от лишних вещей:
uint32_t * p = malloc(1245);

data_t var_s = {
.f1 = init_fq(*arr),
.f2 = malloc(sizeof(data_item_t)),
.f3 = &volatile_array,
};

volatile uint32_t * p1 = var_s.f3; // теряем volatile

Стоит сказать, что кастование одних типов к другим весьма нехорошая практика, которая потенциально приносит проблемы и грязь в код.

Есть еще одна мысль на счет универсальных функций вроде аллокаторов или функций вида send() возвращаемые значения и типы входных аргументов оформлять как void*. Таким образом и код будет чище и сопровождать его проще.
// Пример с приемом и возвратом uint8_t*
size_t sent_data_u8(uint8_t * data, size_t size);
size_t sent_data_pv(void * data, size_t size);

data_t data_s = { ... };
uint8_t data_arr[32] = { ... };
uint32_t data_32 = 42;

sent_data_u8((uint8_t *)&data_s, sizeof(data_s));
sent_data_u8((uint8_t *)data_arr, sizeof(data_arr));
sent_data_u8((uint8_t *)&data_32, sizeof(data_32));

sent_data_pv(&data_s, sizeof(data_s));
sent_data_pv(data_arr, sizeof(data_arr));
sent_data_pv(&data_32, sizeof(data_32));

Мне кажется выводы сделать просто.

https://cdeblog.ru/void-pointers-casts

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

#c #cpp
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Перечисление (enum)

Иногда вместе со списком элементов необходимо иметь их количество и значение последнего элемента.

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

Не редно видел конструкции вида:

enum dma_instance_e
{
DMA_INST_0,
DMA_INST_1,
DMA_INST_2,
DMA_INST_LAST = DMA_INST_2,
DMA_INST_QTY = DMA_INST_LAST + 1,
};

На этапе разработки постоянно требуется меняеть количество этих элементов.

Но в коде выше есть проблема: решили добавить дма - необходимо изменить ещё и ласт. Одна правка, а изменяет две строчки, что не только не красиво, но и потенциальная ошибка из-за невнимательности.

➡️Да-да, решили добавить дма. Нам можно, мы микросхемы проектируем 😊

Намного проще поменять порядок элементов и возложить эту задачу на компилятор:

enum dma_instance_e
{
DMA_INST_0, // 0
DMA_INST_1, // 1
DMA_INST_2, // 2
// <== добавлять тут
DMA_INST_QTY, // 3
DMA_INST_LAST=DMA_INST_QTY-1, // 2
};

#си #перечисление #enum
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Switch statement с диапазонами

Очередное расширение GNU.

Допустим у нас ситуация... с такими условиями:

void func(uint32_t var)
{
if (var >= VALUE_A_START && var <= VALUE_A_END)
{ /* CODE */}
else if (var >= VALUE_B_START && var <= VALUE_B_END)
{ /* CODE */}
else if (var >= VALUE_C_START && var <= VALUE_C_END)
{ /* CODE */}
else if (var >= VALUE_D_START && var <= VALUE_D_END)
{ /* CODE */}
else
{ /* CODE */}
}

В зависимости от попадания в нужный диапазон выполняется то или иное действие.

Попробуем переписать при помощи switch:

void func(uint32_t var)
{
switch (var)
{
case VALUE_A_START:
case VALUE_A_1:
case VALUE_A_2:
// ... здесь куча других case
case VALUE_A_30:
case VALUE_A_31:
case VALUE_A_END:
/* CODE */
break;

// Остальные секции: D, C, D
...

default: break;
}
}

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

Попробуем использовать расширения GNU и переписать код в более удобном для восприятия виде:

void func(uint32_t var)
{
switch (var)
{
case VALUE_A_START ... VALUE_A_END:
/* CODE */
break;
case VALUE_B_START ... VALUE_B_END:
            /* CODE */
            break;
case VALUE_C_START ... VALUE_C_END:
            /* CODE */
            break;
case VALUE_D_START ... VALUE_D_END:
            /* CODE */
            break;

default: break;
}
}

В итоге код стал проще выглядеть и восприниматься. Мы сохранили читаемость такую же как у "if", но приобрели скорость выполнения конструкции "switch"

Пусть это и расширение GNU, но clang собирает без ругани даже если указать -std=c11 , а не -std=gnu11

#c #switchcase #switch
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
👩‍💻 Шина событий на костылях. Реализация в FreeRTOS

Паттерн "шина событий" (Event Bus) - это архитектурный шаблон, который позволяет компонентам системы взаимодействовать друг с другом, обмениваясь событиями, без необходимости явного знания о существовании друг друга.
Казалось бы к чему это.

Начнем рассказ с архитектуры ПО, которое работает на нашем видеопроцессоре.

Существует тракт обработки видео и множество модулей, которые выполняют действия в какой-то момент обработки потока кадров:
🔴перед кадром
🔴после кадра
🔴во время обработки кадра
🔴после сжатия
🔴и других событиях.

Для того, что бы не нагружать центральный модуль (тракт) вызовом кучей различных функций и не менять постоянно его струтуру при изменении и добавлении функционала было решено реализовать шину событий.

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

Первой попыткой реализации было использование event flags и event groups. Однако у этого подхода есть ограничения в невозможности одновременной подписки нескольких задач на одно событие. Флаг сбрасывает первая разблокированная задача.

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

Реализация заключается в использовании двухмерного массива хэндлов задач, ожидающих событий.
/// \brief Массив очередей событий
/// \note events[ количество событий ][ глубина очереди ]
static event_task_handler_t events
[ EVENT_BUS_QTY ]
[ EVENT_LIST_DEPHT ] = {0};

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

Задача которая генерирует событие посылает уведомление каждой задаче из списка.

Таким образом каждая ожидающая задача гарантированно получает уведомление и разблокируется для выполнения, заодно и решается проблема связанная со сбросом флагов в event flags.

Небольшой пример:

Источник событий
static void task_1(void * arg)
{
/* INIT TASK CODE */
for (;;){
/* CODE */
event_bus_push(EVENT_COMPETE_1);
}
}

Потребитель событий 1
static void task_2(void * arg)
{
/* INIT TASK CODE */
// Подписать текущую задачу
event_bus_subscribe(EVENT_COMPETE_1);
// Подписать задачу 3
event_bus_unsubscribe_ex(EVENT_COMPETE_1, task_3_handler);
for (;;){
if (event_bus_wait(EVENT_COMPETE_1, 100) == true){
/* CODE */
}
}
}

Потребитель событий 2
static void task_3(void * arg)
{
for (;;){
if (event_bus_wait(EVENT_COMPETE_1, 100) != true)
{
continue;
}
/* CODE */
}
}


Полный код реализации можно найти в репозитории:
https://github.com/devprodest/event-bus-freertos/tree/main

#eventbus #freertos #шинасобытий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21