Pull to refresh

Comments 26

Недавно смотрел исходные тексты на сайте довольно-таки известного производителя и увидел следующий код

Мне трудно поверить, что больше нигде в программе не будет ни одного обращения к этому регистру

Пока была показана лишь одна строка подобного кода, а весь юзкейс построен лишь на некотором предположении, то данную статью можно воспринимать и как самоучитель «как написать 10 строк кода, вместо одной»
Если полученный 10 строк кода более надежны, понятны и, внимание, не производят объектный код бОльшей длины, то я голосую за 10 строк кода. После того, как Вы полчаса будете смотреть на свой код через пол-года после написания с мучительными мыслями «А что я имел в виду, когда переворачивал этот бит в этой одной строке?», Вы тоже склонитесь к 10 строкам, хотя кто знает…
После того, как Вы полчаса будете смотреть на свой код через пол-года после написания с мучительными мыслями «А что я имел в виду, когда переворачивал этот бит в этой одной строке?

Не надо драматизировать, это далеко не пример ужасного кода.
Зависист от спецификации на устройство, если там всё расписано в понятиях *(unsigned int *)0xf80ff000 &= 0xffffefff; то и писать лучше так, а не придумывать свои имена. Да и интерфейс из одной комманды «сброс бита по адресу» выглядит очень странным и не полным.
И про магические числа тут притянуто немного за уши. Для человека работающего с железом 0xf80ff000 и CsrReg будут одинаковыми уникальными именами некого регистра. Числовой формат возможно содержит ещё больше информации, например это регистр из группы 0хf8xxxxxxxxx
А вопросы а что я имел ввиду переворачивая бит — возникнут в обеих случаях, так что если и вводить абстракцию то на каком-то более высоком уровне.
Статья высосана из пальца.
Во первых: все знают, что magic numbers это плохо.
Во вторых: А вдруг эта ваша строчка была в исходнике какой нибудь библиотеки в контексте:
void reset_uartrx_interrupt()
{
*(unsigned int *)0xf80ff000 &= 0xffffefff;
}
Тогда ваши 10 строк кода ничего не дадут, кроме увеличения количества кода.
Ну вообще то я показал, что дадут эти десять строк — возможность быстрой модификации (в целях переноса в том числе), автоматическую проверку компилятором (что устраняет возможные ошибки), причем за бесплатно (то есть даром) — удивлен, что Вы этого не увидели. Как я и сказал в тексте этот пост не для «полубогов в программировании», которые не делают ошибок НИКОГДА.
А вообще то забавно получилось 1) все знают, что это плохо 2) но это плохо не всегда?
Приведите полный кусок кода с этой конструкцией, дабы оценить необходимость в дополнительных строчках.
«а вот что будут делать авторы исхлжного фрагмента, мне даже и представить трудно» — добавит куда-нибудь «профилактический ресет» всей системы. Или захардкодит несколько попыток обращения последовательно :)

А если серьезно — практически для каждого микроконтроллера уже ведь есть неплохо написанные заголовки, где все регистры объявлены так, чтоб работало… Хотя, может кому-то религия не позволяет их использовать…
«а вызывать настоящую функцию для сброса бита будет весьма накладно» — вот как раз недавно писал про mbed, в котором почти сброс бита (изменение GPIO пина с IN на OUT) на контроллере с частотой 48 МГц занимал 13 микросекунд…
Ну там все-таки, наверное, настройка всех параметров пина (вход-выход, функция, подтяжки, скорости нарастания, фильтры и т.д.), а не только направления?
Не-а, вызов одной функции типа pin.input(); Просто там С++ фреймворк аппаратно-независимый.
UFO just landed and posted this here
UFO just landed and posted this here
Это хорошо, если в даташите для каждого регистра прямо указан его адрес. В STM32, например, обычно указывают базовый адрес периферийного модуля, а в описаниях регистров указывают только смещения от него.
Эт точно.
Выполнение сброса бита в таком виде
*(unsigned int *)0xf80ff000 &= 0xffffefff;
это скорее всего копипаста из какого-то низкоуровневого мануала.
Это верный признак, что здесь код никто кроме кодера не проверял, а может и не отлаживал.
Хм, а такой вариант мне даже в голову не пришел. А ведь вполне могла быть и копипаста, спасибо за подсказку.
куда?
хаб Программирование микроконтроллеров администрация оттуда удалила
RegBitClr(CsrReg,3); // тут предупреждения компилятора


Какие-то у вас с IAR-ом странные представления о C. Мне не удалось получить warning при неявном приведении int-а в enum, ни в MSVC (с /Wall), ни в gcc (с -W -Wall).
Вот в режиме C++, да, ругаются оба. Но ошибками, а не warning-ами.

Перейти же к настоящим функция мы не можем, поскольку inline не является в C директивой компилятора (ну, по крайней мере, так в моей версии IAR, хотя я вроде это победил при помощи прагмы — костыли, повсюду костыли), а вызывать настоящую функцию для сброса бита будет весьма накладно


Ну если вы уж так сильно желаете странного, можно сделать что-то вроде такого:

enum e {e1};

int fake_fnc(int* a, enum e v); // тела для функции не нужно, т.к. она никогда не вызывается

#define DO_STUFF(a,v) (*(a)=(v),1||fake_fnc(a,v))

void main(void)
{
 int z;

 DO_STUFF(&z, 1);	// тут в случае C++ у нас будет ошибка
 DO_STUFF(&z, e1);
}
А IAR мне бросает варнинг, наверное, дело в настройках, в проектах IAR их много и далеко не все я трогал. Но в данном случае я целиком и полнностью на стороне IAR — такое неявное приведение должно быть помечено, как источник возможной ошибки, иначе возникает вопрос, а нафиг нам вообще тогда enum? Кстати, раньше я думал что IAR юзает gcc (непонятно, почему такая идея была у меня в голове), но после ряда экспериментов убедился, что это не так.

А вот по поводу Вашей проверочной функции — большое спасибо. Это же ассерт времени компиляции, то, что надо. И как здорово с подавлением его исполнения через логическое выражение — я бы никогда не додумался, еще раз спасибо.
Я подозревал, что есть люди, знающие С лучше меня, но сейчас я в этом убедился и это нисколько не сарказм. Сейчас добавлю это решение к основному посту, со ссылкой на Вас, если не возражаете.
Всегда рад помочь :)
Все эти трюки придуманы еще в прошлом веке, поэтому врядли я могу претендовать на авторство :)
А можно было бы просто написать юнит-тест на вызов этой функции, который бы проверял, что что-то выставилось или не выставилось. Занял бы две строки и проверил бы не менее надёжно (плюс ещё бы и подсказал читателю кода как функцию вызывать и чего от неё ожидать).
Ну, вообще то, я про С писал.
И вообще я не уверен, что в embedded система юнит-тестов — хорошая идея.
Вам не думали о том, что этот кусок кода мог быть сгенерирован автоматизированными средствами? Обидно за индусов :)
Sign up to leave a comment.

Articles