Jump to content

    

отладка в Keil

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

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

затем 0

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

 

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites
Что будет делать контроллер, получив 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);

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

Share this post


Link to post
Share on other sites

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

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

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

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

 

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

Share this post


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

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

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

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

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

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

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

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

 

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

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

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

 

Share this post


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

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

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

 

Share this post


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

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

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

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

Share this post


Link to post
Share on other sites
Кто вас научил такому ужасному ужасу, "библиотеки"? Что будет делать контроллер, получив 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) не нужна.

 

 

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

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

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

Share this post


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

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

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

operator1;
{  operator2; 
   operator3;
};
operator4;

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

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

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

Share this post


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

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

#include "stm32f4xx.h"

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

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

Share this post


Link to post
Share on other sites
Дык в том то и дело, что стоит крест на, допустим, строчке

#include "stm32f4xx.h"

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

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

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

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

Share this post


Link to post
Share on other sites
Include paths прописаны в закладке C/C++ проекта?

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

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

Share this post


Link to post
Share on other sites

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

При введении в него промежуточной переменной 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)

Share this post


Link to post
Share on other sites
// в позицию 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));

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

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this