Перейти к содержанию
    

Любое битовое поле в регистре в общем случае записывается в два прохода:

сначала выставляются 1 в нужных позициях

затем 0

Можно наоборот, порядок не важен.

Кто вас научил такому ужасному ужасу, "библиотеки"? Что будет делать контроллер, получив 1 в нужных позициях и случайные предыдущие значения вместо нулей в остальных (или наоборот, не важно)? Я уже не говорю о лишней записи в регистр. Грамотный программист использует либо конструкцию Reg = (Reg & Mask1) | Mask2, либо ее же, разбитую на два выражения и временную переменную.

И еще - грамотный программист сложное выражение в #define обрамляет в do {} while(0)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Объяснюсь по поводу своего вопроса.

1. На просторах Интернета много раз встречал высказывания, что "магическое" число - это не есть хорошо.

2. ИМХО, работа через регистры более компактна и нагляднее чем применение библиотек.

3. Из п2. вытекает(для меня), что написание своих макросов и дефайнов не сокращает время долбания клавиатуры и не повышает читаемость кода.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Задам еще вопрос по Keil. У меня версия 5.23 (32Кб). Иногда при запуске Keil, теряется какой-то заголовочный файл. Лечится перезагрузкой Keil, но чаще приходится перезагружать Win7. Не смертельно, но иногда задалбывает. Кто- нибудь сталкивался с подобным?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что будет делать контроллер, получив 1 в нужных позициях и случайные предыдущие значения вместо нулей в остальных (или наоборот, не важно)? Я уже не говорю о лишней записи в регистр. Грамотный программист использует либо конструкцию Reg = (Reg & Mask1) | Mask2, либо ее же, разбитую на два выражения и временную переменную.

Согласен с замечанием.

Так лучше?

// Макрос записи в регистр reg битовой последовательности val в позицию pos (по младшему разряду)
// msk - маска битового поля
#define TuneBitField(reg,val,pos,msk) \
                do { \
                    tmp=reg; \
                    (tmp) |= (((val) << (pos))&(msk)); \
                    (tmp) &= (((val) << (pos))|~(msk)); \
                    reg=tmp; \
                } while (0);

И еще - грамотный программист сложное выражение в #define обрамляет в do {} while(0)

А здесь - не могу представить себе пример кода, когда обрамляющие фигурные скобки (блок {} ) работают хуже (да просто не точно так же), как конструкция do {} while (0);

Приведите пример, если можете.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это очень просто

https://www.google.ru/search?q=зачем+исполь...ile+в+%40define

и читаем первый ответ.

Если есть Яндекс Алиса - можно голосом задать вопрос

 

https://ru.stackoverflow.com/questions/6801...D1%8F-do-while0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1. На просторах Интернета много раз встречал высказывания, что "магическое" число - это не есть хорошо.

Так и есть. Лучше давать таким константам осмысленные имена. Так и читается легче и вносить изменения в код проще.

2. ИМХО, работа через регистры более компактна и нагляднее чем применение библиотек.

Компактна - да, возможно, в некоторых случаях. Но далеко не всегда и не везде. Что компактней: в некотором регистре установить какой-то бит в 1 (сбросить бит в 0) или вызвать функцию с 2-3 параметрами? В обоих случаях это будет одна строка кода.

Наглядна - нет. Если бы библиотеки делали код менее наглядным их бы не писали в принципе. А библиотеки как раз и пишутся для повышения наглядности. Они повышают уровень абстракции. Работая с регистрами вам надо знать не только "что делать", но и "как это делать" - какие битовые поля в каких регистрах в какие значения установить. Работая с библиотекой надо знать лишь "что делать". Вопрос "как это сделать" библиотечная функция взяла на себя. Она сама внутри своего тела оперирует регистрами, битами, их позициями в регистрах, сдвигами и масками битовых полей. Снаружи ничего этого не видно и знать не нужно.

3. Из п 2. вытекает(для меня), что написание своих макросов и дефайнов не сокращает время долбания клавиатуры и не повышает читаемость кода.

Не сокращает и не повышает.

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

 

Это очень просто

Точно, вспомнил - из-за точки с запятой.

Просто компилятор Кейла допускает конструкцию типа { }; и не ругаются на неё, связывая идущий после этого else с последним if. Так что забыл про этот неприятный момент, хотя и читал про это ранее (забыл уже где).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Не буду оспаривать очевидное.

Небольшой нюанс - я к МК пришел через проектирование электроники на логических элементах малой степени интеграции, поэтому у меня в крови - знать и понимать что и как я делаю. Чувствую себя не комфортно, если не понимаю, как это действие выполняется.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так лучше?
Да. Осталось избавиться от pos, перейдя от (val) << (pos) к (val) * ((msk) & -(msk)). Поскольку и val и msk - константы времени компиляци, замена сдвига на умножение не повлияет на размер и скорость результирующего кода, а в исходнике будет меньше вероятность использовать pos и msk от разных битовых полей.

Точно, вспомнил - из-за точки с запятой.

Просто компилятор Кейла допускает конструкцию типа { }; и не ругаются на неё, связывая идущий после этого else с последним if.

Надеюсь, что тут вы ошибаетесь. Иначе это был бы компилятор "языка, похожего на Си".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кто вас научил такому ужасному ужасу, "библиотеки"? Что будет делать контроллер, получив 1 в нужных позициях и случайные предыдущие значения вместо нулей в остальных (или наоборот, не важно)? Я уже не говорю о лишней записи в регистр. Грамотный программист использует либо конструкцию Reg = (Reg & Mask1) | Mask2, либо ее же, разбитую на два выражения и временную переменную.

И еще - грамотный программист сложное выражение в #define обрамляет в do {} while(0)

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

 

Для mihlit:

1. Запись нового значения в регистр

REG = value; // запишет в регистр REG значение value

2. Установка некоторых бит в регистре, не затрагивая значений остальных бит

REG |= value; // установит в REG биты в 1, установленные в двоичном представлении value

3. Сброс некоторых бит в регистре, не затрагивая значений остальных бит

REG &= ~value; // сбросит в REG биты в 0, установленные в двоичном представлении value

4. Одновременные действия пунктов 2 и 3 (было уже озвучено выше)

REG = (REG & Mask1) | Mask2; // сбросит нужные биты по маске Mask1 (там, где у нее 0 в битах) и установит биты по маске Mask2

 

 

Объяснюсь по поводу своего вопроса.

1. На просторах Интернета много раз встречал высказывания, что "магическое" число - это не есть хорошо.

2. ИМХО, работа через регистры более компактна и нагляднее чем применение библиотек.

3. Из п2. вытекает(для меня), что написание своих макросов и дефайнов не сокращает время долбания клавиатуры и не повышает читаемость кода.

1. Да, так и есть. Я, например, осмысленные числа, которые еще тем более могут корректироваться в проекте в процессе отладки, заменяю на именованные #define. Пример

#define HW_EEPROM_I2C_ADDRESS             0xA0

#define HW_EXCHANGE_I2C_ADDRESS_OWN       0x12
#define HW_EXCHANGE_I2C_ADDRESS_ABONENT   0x34

В некоторых местах, например, я прибегаю к явным числам, например

if(xSemaphoreTake(MeasureSemaphoreHandle, 50) == pdTRUE) // жду семафор в течение 50мс

В Вашем случае AFRH15 - это 4 старших бита 32-битного регистра AFRH (GPIOx->AFR[1]).

Теперь можно написать что-то вроде

#define AFRH_POS_0 0
...
#define AFRH_POS_15 28
#define AFRH_REG_WRITE(GPIOX, POSITION, VALUE) GPIOX->AFR[1] |= VALUE << POSITION
...
...
...
(в коде)
AFRH_REG_WRITE(GPIOA, AFRH_POS_15, 5);

Здесь устанавливаются нужные биты, заданные числом (5 в данном случае), в позициях битового поля AFRH15 регистра AFRH.

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

 

2. Не всегда. Меня, например, в некоторых приложениях полностью устраивает библиотека настройки, например, тех же GPIO. Я их настроил, а вот уже дальше - в основном работа через регистры. В общем-то, разницы нет. Можно и через регистры написать свою компактную библиотеку, это не возбраняется.

 

3. Заблуждение. По мере написания проекта Вы начнете понимать, что Вы полностью этот проект контролируете и разбираетесь в нем. Поверьте, в этом случае, если Вы будете использовать магические числа, будет только хуже. Основное правило - зачем исправлять одну и ту же настройку (например, размер приемного буфера какого-либо интерфейса) в разных местах проекта, если его достаточно поправить в одном определении #define?

 

 

Задам еще вопрос по Keil. У меня версия 5.23 (32Кб). Иногда при запуске Keil, теряется какой-то заголовочный файл. Лечится перезагрузкой Keil, но чаще приходится перезагружать Win7. Не смертельно, но иногда задалбывает. Кто- нибудь сталкивался с подобным?

Нужно больше информации. Скрин экрана, например, либо что конкретно пишет среда. У меня такой проблемы нет.

 

 

Согласен с замечанием.

Так лучше?

// Макрос записи в регистр reg битовой последовательности val в позицию pos (по младшему разряду)
// msk - маска битового поля
#define TuneBitField(reg,val,pos,msk) \
                do { \
                    tmp=reg; \
                    (tmp) |= (((val) << (pos))&(msk)); \
                    (tmp) &= (((val) << (pos))|~(msk)); \
                    reg=tmp; \
                } while (0);

Точка с запятой после while(0) не нужна.

 

 

Не буду оспаривать очевидное.

Небольшой нюанс - я к МК пришел через проектирование электроники на логических элементах малой степени интеграции, поэтому у меня в крови - знать и понимать что и как я делаю. Чувствую себя не комфортно, если не понимаю, как это действие выполняется.

Это хороший подход к делу!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Надеюсь, что тут вы ошибаетесь. Иначе это был бы компилятор "языка, похожего на Си".

Да, ошибся. Проверил. Действительно, выдаёт ошибку.

Это он не ругается на ; после блока в конструкциях вида:

operator1;
{  operator2; 
   operator3;
};
operator4;

while (SomeExpr1) {
  if (SomeExpr2) {
    operator1; 
    operator2;
  };
}

хотя ; после } там не нужна.

Возможно, он воспринимает ; после } как пустой оператор, поэтому и не имеет ничего против.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нужно больше информации. Скрин экрана, например, либо что конкретно пишет среда. У меня такой проблемы нет.

Дык в том то и дело, что стоит крест на, допустим, строчке

#include "stm32f4xx.h"

наводишь стрелку на крест - внизу экрана выводится - "фатальная ошибка: нет пути"

но Keil то работает, т.е можно запустить компиляцию - выдает 0 ошибок, отладка тоже запускается и работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дык в том то и дело, что стоит крест на, допустим, строчке

#include "stm32f4xx.h"

наводишь стрелку на крест - внизу экрана выводится - "фатальная ошибка: нет пути"

но Keil то работает, т.е можно запустить компиляцию - выдает 0 ошибок, отладка тоже запускается и работает.

Include paths прописаны в закладке C/C++ проекта?

У Keil есть такая болезнь, ставить крестики на нормальных строчках. Но не на stm32f4xx.h (у себя ни разу не видел).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Include paths прописаны в закладке C/C++ проекта?

У Keil есть такая болезнь, ставить крестики на нормальных строчках.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С макросом оказалось всё немного сложнее.

При введении в него промежуточной переменной tmp появилась необходимость указывать её тип. Он должен совпадать с типом reg. А т.к. он в общем случае разный и заранее неизвестный, то придётся его вводить одним из параметров макроса.

Как-то так

// Макрос записи в регистр reg битовой последовательности val
// в позицию pos (по младшему разряду), msk - маска битового поля
#define TuneBitField(typename,reg,val,pos,msk) \
  do { \
      typename tmp=reg; \
      tmp |= (((val) << (pos))&(msk)); \
      tmp &= (((val) << (pos))|~(msk)); \
      reg=tmp; \
  } while (0)

В С++ этот макрос можно (а скорее нужно) переоформить в виде шаблонной подставляемой (inline) функции, где параметром шаблона как раз и будет имя типа регистра reg, а аргументом функции будет ссылка на регистр, чтобы его содержимое можно было изменить.

Второй вариант выхода из ситуации (для Си без плюсов) - переписать макрос без использования промежуточной переменной, приведя его к виду: do {reg=(reg&msk1)|msk2;} while (0)

// Макрос записи в регистр reg битовой последовательности val 
// в позицию pos (по младшему разряду), msk - маска битового поля
#define TuneBitField(reg,val,pos,msk) \
    do { \
      reg = ((reg)&(((val) << (pos)))|~(msk))|(((val) << (pos))&(msk)); \
    } while (0)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

// в позицию pos (по младшему разряду), msk - маска битового поля

#define TuneBitField(typename,reg,val,pos,msk) \

do { \

typename tmp=reg; \

tmp |= (((val) << (pos))&(msk)); \

tmp &= (((val) << (pos))|~(msk)); \

reg=tmp; \

} while (0)[/code]

Это-ж как так надо запутать простейшее выражение?? :

Пользуйтесь: :rolleyes:

reg = ((reg) & ~(msk)) | ((val) << (pos) & (msk));

Либо так:

reg = ((reg) | (msk)) ^ (~(val) << (pos) & (msk));

и никаких промежуточных переменных....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...