Comments 43
1. Код совершенно нечитаем. Вот почему:
Что делают все эти буквы? В даташит лезть? А на какой странице искать?
2. Классная простыня из индексов:
Я в очередной раз сошлюсь на длинный список реальных багов, части которых бы не было, если бы некоторым программистам циклы выдавали не по талонам.
Да, можно сказать «это такая оптимизация! Подлый компилятор наставит там условий, и копирование будет делаться на X тактов дольше».
Ответ простой: А если мне количество тактов безразлично, но я попал в историю одного байта?
Так вот, цикл решает обе эти проблемы:
Он прикрывает нас от путаницы в индексах (и потенциальный читатель не будет проверять каждую строчку на предмет «A индексы точно везде единообразны?»)
Он объясняет компилятору алгоритм. А опции оптимизации заставят компилятор либо раскрутить цикл, либо наоборот, максимально ужать его.
3. Вместо гитхаба код лежит в архиве на облаке-диске
И толку от этого? Лежал бы код в положенном месте, его можно было бы:
-легко просматривать через встроенную смотрелку гитхаба, без необходимости скачивать и распаковывать
-форкнуть, убрать помои и предложить пулл-реквест (который не примут все равно).
-подключать к своим проектам через git-subtree и получать обновления по человечески (то есть автоматом), а не в зипчиках.
short AC1;
short AC2;
short AC3;
unsigned short AC4;
unsigned short AC5;
unsigned short AC6;
short B1;
short B2;
short MB;
short MC;
short MD;
long UT;
long UP;
Что делают все эти буквы? В даташит лезть? А на какой странице искать?
2. Классная простыня из индексов:
BMP180_Struct->AC1 = (buf[0]<<8) | buf[1];
BMP180_Struct->AC2 = (buf[2]<<8) | buf[3];
BMP180_Struct->AC3 = (buf[4]<<8) | buf[5];
BMP180_Struct->AC4 = (buf[6]<<8) | buf[7];
BMP180_Struct->AC5 = (buf[8]<<8) | buf[9];
BMP180_Struct->AC6 = (buf[10]<<8) | buf[11];
BMP180_Struct->B1 = (buf[12]<<8) | buf[13];
BMP180_Struct->B2 = (buf[14]<<8) | buf[15];
BMP180_Struct->MB = (buf[16]<<8) | buf[17];
BMP180_Struct->MC = (buf[18]<<8) | buf[19];
BMP180_Struct->MD = (buf[20]<<8) | buf[21];
Я в очередной раз сошлюсь на длинный список реальных багов, части которых бы не было, если бы некоторым программистам циклы выдавали не по талонам.
Да, можно сказать «это такая оптимизация! Подлый компилятор наставит там условий, и копирование будет делаться на X тактов дольше».
Ответ простой: А если мне количество тактов безразлично, но я попал в историю одного байта?
Так вот, цикл решает обе эти проблемы:
Он прикрывает нас от путаницы в индексах (и потенциальный читатель не будет проверять каждую строчку на предмет «A индексы точно везде единообразны?»)
Он объясняет компилятору алгоритм. А опции оптимизации заставят компилятор либо раскрутить цикл, либо наоборот, максимально ужать его.
3. Вместо гитхаба код лежит в архиве на облаке-диске
И толку от этого? Лежал бы код в положенном месте, его можно было бы:
-легко просматривать через встроенную смотрелку гитхаба, без необходимости скачивать и распаковывать
-форкнуть, убрать помои и предложить пулл-реквест (который не примут все равно).
-подключать к своим проектам через git-subtree и получать обновления по человечески (то есть автоматом), а не в зипчиках.
Моя первая публикация. Гитхабом пользоваться не умею. Библиотека с рабочего проекта. Не знаю, чем вам еще помочь. Дефайны лежат в файле. И так старался как можно компактнее писать и не повторяться за мануалом. Впрочем, есть люди, которым всегда все не нравится. Поэтому оставлю как есть.
> Гитхабом пользоваться не умею.
Это много говорит о квалификации.
> Впрочем, есть люди, которым всегда все не нравится. Поэтому оставлю как есть.
Впрочем, есть люди, которым всегда тяжело проходить pull request/code review и воспринимать критику кода.
Это много говорит о квалификации.
> Впрочем, есть люди, которым всегда все не нравится. Поэтому оставлю как есть.
Впрочем, есть люди, которым всегда тяжело проходить pull request/code review и воспринимать критику кода.
> Гитхабом пользоваться не умею.
Это много говорит о квалификации.
это говорить лишь о том что человеку не нужен, и не был нужен гитхаб, и что он не умеет им пользоваться(на данный момент).
уверен что при необходимости он будет им пользоваться(или аналогичным продуктом).
бегло пробежав по статье я мало что там понял, однако я пользуюсь гитхабом(точнее аналогом bitbucket),
и что? это разве означает что что у меня квалификация выше?
зато ваш отзыв о том кто не пользуется тем инструментом которым пользуетесь вы, многое говорит о вас как о человеке, и о вашем отношении к людям.
ps лично мне вот это помогло разобраться с гитхабом. http://goloburdin.blogspot.ru/2013/11/git-bitbucket-20.html
Спасибо, я пока действительно не вижу смысла пользоваться гитхабом. У меня настроен google drive и GIT для контроля версий. Зачем мне гитхаб?
- Очень удобный и приятный интерфейс
- Некоторые изменения можно джае комитить из браузера
- Легко вносить правки сторонним разработчикам
- История коммитов онлайн
- Возможность комментировать любую строчку в пул реквесте
- readme.md
- Публичность и индексируемость
- Статистика посещений репозитория
- Тикеты
продолжать можно долго.
У меня настроен… GIT для контроля версий. Зачем мне гитхаб?
Если вы уже используете гит — то неужели для вас написать в командной строке git push git@github.com:virviglaz/mics.git HEAD:refs/heads/{имя}
сложнее чем то, что вы обычно делаете для публикации исходников?
… однако я пользуюсь гитхабом(точнее аналогом bitbucket), и что? это разве означает что что у меня квалификация выше?
GitHub это de facto стандарт для open source проектов. Не пытаться разобраться с ним, чтобы сделать один простой push вместо того, чтобы делиться проектом через файлообменник или распределённую файловую систему, таки говорит о квалификации разработчика много.
зато ваш отзыв о том кто не пользуется тем инструментом которым пользуетесь вы, многое говорит о вас как о человеке, и о вашем отношении к людям.
Да, и я не против. Привык быть строгим ко всем и к себе.
P.S. А ещё я не против того, чтобы меня считали мудаком, ага.
Открыл для себя неплохой способ избавиться от простыней и повысить переносимость с помощью X macro или на шаблонах.
Хотя препроцессор вроде не Тьюринг-полный, на нем все равно можно сделать очень и очень много. Теоретически на макросах можно изобразить внебрачного сына рефала, хаскеля и T-SQL, чтобы по заданному табличному описанию периферии генерировался код, но вот стоит ли? Сейчас смотрю на язык Order (вырос из проекта Chaos, который в свою очередь вырос из попытки довести до абсурда Boost.Preprocessor): могучий инструмент, да, но имеет ли смысл его использовать и не проще ли написать кодогенератор на питоне или перле?
Хотя препроцессор вроде не Тьюринг-полный, на нем все равно можно сделать очень и очень много. Теоретически на макросах можно изобразить внебрачного сына рефала, хаскеля и T-SQL, чтобы по заданному табличному описанию периферии генерировался код, но вот стоит ли? Сейчас смотрю на язык Order (вырос из проекта Chaos, который в свою очередь вырос из попытки довести до абсурда Boost.Preprocessor): могучий инструмент, да, но имеет ли смысл его использовать и не проще ли написать кодогенератор на питоне или перле?
По мне так если этот пост рассматривать как пост про стиль кода, то сам стиль кода не очень.
Взять хотя бы строку
тут в одной строке и вызов функции и проверка результата и возврат значения.
Читать довольно тяжело.
Имена переменных то с большой буквы, то с маленькой.
Что мешает написать хотя бы так:
Взять хотя бы строку
if ((Result = BMP180_Struct->ReadReg(BMP180_Struct->I2C_Adrs, AC1_Reg, buf, sizeof(buf))) != 0) return Result;
тут в одной строке и вызов функции и проверка результата и возврат значения.
Читать довольно тяжело.
Имена переменных то с большой буквы, то с маленькой.
Что мешает написать хотя бы так:
Result = BMP180_Struct->ReadReg(BMP180_Struct->I2C_Adrs, AC1_Reg, buf, sizeof(buf));
if (Result != 0)
return Result;
И разницы между библиотеками, выложенными в интернете не увидел, кроме заворачивание всего действия в структуру.
Можно еще пояснить вот эту строку, не совсем ее понял: «В моем случае все это работает в отдельном потоке и пока функции ждут, с интерфейсом работают другие датчики. Так что я ничего не теряю.»
То есть если у меня три датчика подключены к SPI_1, каждый отпрашивается в своем независимом потоке? А за счет чего это реализовано?
Был бы любопытно увидеть конкретную реализацию сего действа.
Можно еще пояснить вот эту строку, не совсем ее понял: «В моем случае все это работает в отдельном потоке и пока функции ждут, с интерфейсом работают другие датчики. Так что я ничего не теряю.»
То есть если у меня три датчика подключены к SPI_1, каждый отпрашивается в своем независимом потоке? А за счет чего это реализовано?
Был бы любопытно увидеть конкретную реализацию сего действа.
Лично я не вижу смысла использовать ARM'ы без операционной системы, за исключением очень узкого круга задач, связанных с потоковой обработкой чего-либо. Да и то это больше удел FPGA. В качестве операционной системы я уже давно и успешно использую FreeRTOS. В данном случае код выглядит примерно так:
Т.е. в том случае, если датчик работает, создается задача, которая этот датчик читает. Т.к. таких задач может быть много, как и устройств на шине, наша задача реализовать защиту доступа. В простейшем случае на помощь приходит mutex. Тогда нам уже не важно, в какой момент задача обращается к интерфейсу. Если интерфейс занят, задача будет ждать его освобождения:
if (!Error.BMP180)
xTaskCreate(BMP180_DataCollector, NULL, 70, NULL, tskIDLE_PRIORITY + 1, NULL);
Т.е. в том случае, если датчик работает, создается задача, которая этот датчик читает. Т.к. таких задач может быть много, как и устройств на шине, наша задача реализовать защиту доступа. В простейшем случае на помощь приходит mutex. Тогда нам уже не важно, в какой момент задача обращается к интерфейсу. Если интерфейс занят, задача будет ждать его освобождения:
void BMP180_DataCollector (void * pvArg)
{
BMP180_Init(&BMP180_Struct);
while(1)
{
/* Task delay */
vTaskDelay(LogConfig.BMP180_ReadInt * 1000);
/* Get data */
BMP180_Get_Result(&BMP180_Struct);
SensList.intBMP180.Pressure = BMP180_Struct.Pressure;
SensList.intBMP180.Temperature = BMP180_Struct.Temperature;
SensList.intBMP180.Altitude = Altitude(BMP180_Struct.Pressure);
SensList.intBMP180.mmHg = Pa_To_Hg(BMP180_Struct.Pressure);
/* Update flags */
SensList.intBMP180.isDataNotLogged = 1;
SensList.intBMP180.isDataUpdated = 1;
}
}
«Зная давление, мы теоретически можем рассчитать высоту над уровнем моря. Честно говоря, никакого практического применения я пока не придумал, но возможность имеется.»
Почему-то я всегда наивно думал что такие датчики давления как раз и используются для определения высоты.
Как пример, в коптерах, текущая высота рассчитывается как разница давлений между давлением на точке старта и давлением в текущий момент времени. Ну или как в вашем примере разница высот над уровнем моря.
Почему-то я всегда наивно думал что такие датчики давления как раз и используются для определения высоты.
Как пример, в коптерах, текущая высота рассчитывается как разница давлений между давлением на точке старта и давлением в текущий момент времени. Ну или как в вашем примере разница высот над уровнем моря.
«Никто не станет проверять давление 10 раз в секунду.»
Я бы поспорил с этим утверждением. Бывает и чаще нужно измерять, чтобы узнать вертикальную скорость.
Я бы поспорил с этим утверждением. Бывает и чаще нужно измерять, чтобы узнать вертикальную скорость.
Погрешность ~17см. Чтобы измерять чаще 10 раз в секунду, и при этом, иметь разницу в высоте при измерениях адекватно большую, чем погрешность (а иначе смысла такое измерение не имеет), нужно лететь на истребителе. Причем вертикально вниз лететь.
А можно попробовать применить фильтр Калмана. Могу даже поделиться кодом (для float):
Параметр К мы выбираем в интервале 0.1 — 0.5. Для 0.1 к результату мы придем через 10 итераций.
Эта штука реально помогает замыливать перемещения и снижает шум. Лучше использовать Калмана, чем оверсамплинг.
typedef struct
{
float Result;
float Value;
float Previous;
float K;
}KalmanFloatStructTypeDef;
void KalmanFloatCalc (KalmanFloatStructTypeDef * KalmanFloatStruct)
{
KalmanFloatStruct->Result = KalmanFloatStruct->K * KalmanFloatStruct->Value;
KalmanFloatStruct->Value = 1.0 - KalmanFloatStruct->K;
KalmanFloatStruct->Previous *= KalmanFloatStruct->Value;
KalmanFloatStruct->Result += KalmanFloatStruct->Previous;
KalmanFloatStruct->Previous = KalmanFloatStruct->Result;
}
Параметр К мы выбираем в интервале 0.1 — 0.5. Для 0.1 к результату мы придем через 10 итераций.
Эта штука реально помогает замыливать перемещения и снижает шум. Лучше использовать Калмана, чем оверсамплинг.
Калман который фильтрует только один датчик??? Тогда в нем никакого смысла нет, используйте НЧ-фильтр. А Калман нужен для сведения разных датчиков которые выдают физически зависимые показания.
Вы не правы. Фильтр Калмана можно изучить по этой статье. Я с нее код и писал.
Не верьте всему, что пишут в интернете. И уж точно фильтр Калмана нельзя назвать простым.
Насколько я знаю, у Калмана немного другая задача, он не фильтрует показания какого-то одного датчика, он берет показания с нескольких датчиков и наиболее точно пытается предсказать текущее положение вещей. То есть если бы вы высоту измеряли бы своим барометром, плюс с помощью ультразвукового датчика плюс к ним, допустим, измерение по камере, получили бы 3 разные значения текущей высоты, отдали бы все эти 3 значения Калману, на выходе бы получили высоту, наиболее близкой к реальной. И то, только в том случае, если сам фильтр правильно настроен.
То, о чем говорите вы, это по сути получается обычный НЧ-фильтр, как уже написали выше.
То, о чем говорите вы, это по сути получается обычный НЧ-фильтр, как уже написали выше.
Для его нужно Previous, если оно на выходе всегда равно Result?
Почему функция модифицирует свои входные значения (Value)? Обычно это признак плохой архитектуры.
Следующий код считает по сути то же, что и ваш, но выглядит понятнее. И компилируется чаще всего во что-то более короткое.
Для чего эта возня с присваиваниями? Она действительно оправдана?
Почему функция модифицирует свои входные значения (Value)? Обычно это признак плохой архитектуры.
Следующий код считает по сути то же, что и ваш, но выглядит понятнее. И компилируется чаще всего во что-то более короткое.
void KalmanFloatCalc2 (KalmanFloatStructTypeDef * s)
{
s->Result = (1.0f - s->K) * s->Result + s->K * s->Value;
}
Для чего эта возня с присваиваниями? Она действительно оправдана?
Может быть, все может быть. Я не математик, увы. Может быть вы и правы. Проверьте ваш код в железе. Если заработает, давайте вместе им пользоваться.
Комменатрий удален
Здесь я с вами не соглашусь, фильтрацию данных пока еще никто не отменял. Точно также как и интегрирование.
Откройте исходный код любого автопилота коптера и посмотрите как там сделан основной цикл и его частоту. Также можете посмотреть как устроен ПИД-регулятор для удержания высоты и на какие данные он опирается. Там присутствуют обычно как показания акселлерометра, так и показания барометра, которые смешиваются между собой в определенной пропорции (самый простой случай), а уж дальше эти данные идут на регулятор.
Откройте исходный код любого автопилота коптера и посмотрите как там сделан основной цикл и его частоту. Также можете посмотреть как устроен ПИД-регулятор для удержания высоты и на какие данные он опирается. Там присутствуют обычно как показания акселлерометра, так и показания барометра, которые смешиваются между собой в определенной пропорции (самый простой случай), а уж дальше эти данные идут на регулятор.
Ни в коей мере не претендую на истину в последней инстанции, все же хотел бы сказать, что пост, в первом абзаце которого встречается сочетание слов «стиль программирования» должен быть вычитан автором весьма и весьма тщательно. Вам тут набросали замечаний, мне же сразу резанул взгляд первый фрагмент кода.
Если Вам потребовалось в тексте за фрагментом пояснять смысл полей, то это признак плохого (недостаточно откомментированого) кода.
Если Вы применяете повсюду тип char, то это признак плохого кода, поскольку поля очевидно на символьные, хорошей альтернативой было бы u8.
Если у Вас в структуре появляются поля с именем ххх1… ххх6, то это признак плохого стиля, поскольку скорее всего они будут использоваться как элементы массива (так оно дальше по коду и происходит).
Если Вы пишете «Поле Settings позволяет нам указать», а в структуре нет такого поля, то это плохой стиль программирования (недостоверные комментарии).
Если Вы пишете о значении адреса I2C_Adrs по умолчанию, то хотелось бы понять, как это обеспечивается — в дальнейшем выясняется, что никак не обеспечивается, и это признак плохого стиля.
Если Вы собираетесь использовать обобщенные функции ввода/вывода, но в их сигнатуру прописываете адрес на I2C шине, то это плохой стиль программирования.
Лично мне такого начала достаточно, чтобы предположить определенные недоработки в Вашем стиле программирования и предложить Вам сначала поработать над его совершенствованием, и лишь потом приступать к написанию столь обязывающих постов.
Как пост на тему о взаимодействии с конкретным с устройством еще сойдет, хотя и не без огрехов, но как рекомендации по стилю… здесь точно не по адресу.
Без обид, просто я этой темой плотно занимаюсь.
Если Вам потребовалось в тексте за фрагментом пояснять смысл полей, то это признак плохого (недостаточно откомментированого) кода.
Если Вы применяете повсюду тип char, то это признак плохого кода, поскольку поля очевидно на символьные, хорошей альтернативой было бы u8.
Если у Вас в структуре появляются поля с именем ххх1… ххх6, то это признак плохого стиля, поскольку скорее всего они будут использоваться как элементы массива (так оно дальше по коду и происходит).
Если Вы пишете «Поле Settings позволяет нам указать», а в структуре нет такого поля, то это плохой стиль программирования (недостоверные комментарии).
Если Вы пишете о значении адреса I2C_Adrs по умолчанию, то хотелось бы понять, как это обеспечивается — в дальнейшем выясняется, что никак не обеспечивается, и это признак плохого стиля.
Если Вы собираетесь использовать обобщенные функции ввода/вывода, но в их сигнатуру прописываете адрес на I2C шине, то это плохой стиль программирования.
Лично мне такого начала достаточно, чтобы предположить определенные недоработки в Вашем стиле программирования и предложить Вам сначала поработать над его совершенствованием, и лишь потом приступать к написанию столь обязывающих постов.
Как пост на тему о взаимодействии с конкретным с устройством еще сойдет, хотя и не без огрехов, но как рекомендации по стилю… здесь точно не по адресу.
Без обид, просто я этой темой плотно занимаюсь.
big indian / little indian — это вы хорошо написали — большой индусский/малый индусский — видимо код.
Порядок байтов на английском звучит как big endian/little endian.
Порядок байтов на английском звучит как big endian/little endian.
> Порядок байтов на английском звучит как big endian/little endian.
big/little endian — это тупоконечники/остроконечники из «Путешествия Гулливера» Свифта.
Применяется в IT для обозначения соответствующих отношений между сторонниками
most/less significant bit first.
big/little endian — это тупоконечники/остроконечники из «Путешествия Гулливера» Свифта.
Применяется в IT для обозначения соответствующих отношений между сторонниками
most/less significant bit first.
Мне об этом уже раз 50 сообщили, спасибо. Пусть остается, как свидетельство моей неграмотности. Кстати, перед публикацией гуглил. Видимо, не я один плохо помню английский.
Что-то я ни разу не видел холиваров "тупоконечников" и "остроконечников". "big/little endian" давно уже стали просто терминами, обозначающими порядок байт.
Для начала, неплохо было бы читателя «инитиализировать» в плане общей постановки задачи и выбора методов решения. Что, зачем и в строгой логической последовательности из 1 в 2, а затем в 3. А теперь на примерах:
У меня с ходу два предложения для Вас (если весь этот эпос о стиле программирования не просто фарс):
1. DMA или если его на АРМ-подобном МК нет, то
2. Цикл через луковичку.
Выпилить из std_periph_lib не особо нужные действия не означает сделать код оптимальнее. Рационализаторство, вот что Вы сделали.
Позвольте возразить, математика тут представляется на уровне 4 класса. Одни алгебраические операции. А вот код, через который они реализованы, надо еще прожевать… Попробуйте вставить предшествующую формулу и всем все сразу же станет понятно.
Где тут многопоточность? И где тут оверсемплинг?
ЗЫ Присоединяюсь ко всем вышеуказанным замечаниям.
… порядок следования старшего и младшего байт у этого датчика обратный
У меня с ходу два предложения для Вас (если весь этот эпос о стиле программирования не просто фарс):
1. DMA или если его на АРМ-подобном МК нет, то
2. Цикл через луковичку.
Красивый код:
— Оптимален (с точки зрения использования памяти и количества циклов, требуемых на его выполнение)
Выпилить из std_periph_lib не особо нужные действия не означает сделать код оптимальнее. Рационализаторство, вот что Вы сделали.
Математика, конечно, тут тяжелая
Позвольте возразить, математика тут представляется на уровне 4 класса. Одни алгебраические операции. А вот код, через который они реализованы, надо еще прожевать… Попробуйте вставить предшествующую формулу и всем все сразу же станет понятно.
Где тут многопоточность? И где тут оверсемплинг?
ЗЫ Присоединяюсь ко всем вышеуказанным замечаниям.
В копилку:
Не понятно зачем писать на С? Для того же BME 280 есть на github библиотека.
Чем не не нравится С, нормально отделить интерфейс от реализации нельзя.
Например, нет смысла давать пользователю функции чтения calibration data и oversampling.
Вообще с точки зрения пользователя должна быть 1 функция, которая возвратит значение температуры и давления в вашем случае, все остальное юзеру не нужно.
В парадигме ООП, калибровочную информацию надо читать в конструкторе класса, там же писать в датчик oversampling.
Что качается интерфейса, меня в бошевской библиотеке раздражал вызов функции по указателю, у вас тоже.
И еще надежда на 3 датчика одновременно, мне кажется слишком оптимистично, например в моем BME 280 жестко прошит 0x76, и поменять его никак нельзя (в самом датчике можно, но этот контакт не разведен у меня на плате када впаян сенсор.
Ну и наконец, я вытыкаю его в RPI, в котором либо SPI либо I2C, итого 1 датчик получается
Ну и по коду, пишите коментарии в Doxygen, это дефакто стандарт. Таже бошевская библиотека везде имеет комментарии в doxygen формате.
P.S> В связи с этим я велосипедирую BME 280 на C++.
Не понятно зачем писать на С? Для того же BME 280 есть на github библиотека.
Чем не не нравится С, нормально отделить интерфейс от реализации нельзя.
Например, нет смысла давать пользователю функции чтения calibration data и oversampling.
Вообще с точки зрения пользователя должна быть 1 функция, которая возвратит значение температуры и давления в вашем случае, все остальное юзеру не нужно.
В парадигме ООП, калибровочную информацию надо читать в конструкторе класса, там же писать в датчик oversampling.
Что качается интерфейса, меня в бошевской библиотеке раздражал вызов функции по указателю, у вас тоже.
И еще надежда на 3 датчика одновременно, мне кажется слишком оптимистично, например в моем BME 280 жестко прошит 0x76, и поменять его никак нельзя (в самом датчике можно, но этот контакт не разведен у меня на плате када впаян сенсор.
Ну и наконец, я вытыкаю его в RPI, в котором либо SPI либо I2C, итого 1 датчик получается
Ну и по коду, пишите коментарии в Doxygen, это дефакто стандарт. Таже бошевская библиотека везде имеет комментарии в doxygen формате.
P.S> В связи с этим я велосипедирую BME 280 на C++.
Sign up to leave a comment.
Датчик абсолютного давления BMP180