логические операторы и вычисление разделения битов в C (программирование PIC)

Я программирую PIC18F94K20 для работы в сочетании с кораблем MCP7941X I2C RTCC и устройством 24AA128 I2C CMOS Serial EEPROM. В настоящее время у меня есть код, который успешно инициализирует значения секунд/дней/и т. д. RTCC и запускает таймер, переключая светодиод при обороте каждую секунду.

Я пытаюсь расширить код, чтобы считывать правильные данные для этих значений, однако у меня возникают проблемы, когда я пытаюсь учесть различные «лишние» биты в значениях. Карта памяти может помочь несколько прояснить мою проблему:

Карта памяти RTCC

Возьмем, к примеру, столбец часов или адрес 02h. Бит 6 устанавливается равным 1, чтобы переключать 12-часовое время, добавляя 01000000 к биту часов. Я могу прочитать все содержимое байта по этому адресу, но я хочу использовать оператор if, чтобы определить, используется ли время в 12 или 24 часа, и соответствующим образом настроить. Меня не беспокоят 10-часовые биты, так как я могу достаточно легко вычислить это с помощью цикла преобразования BCD (я думаю).

Ранее я использовал побитовый оператор ИЛИ в C, чтобы увеличить исходные данные часов до 24. Я инициализировал часы в этом конкретном случае до 0x11 и установил бит управления 12 часами, который равен 0x64. При установке времени:

WriteI2C(0x11|0x64);

который, как вы можете видеть, использует побитовое ИЛИ.

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

current_seconds = ReadI2C();
current_seconds = ST & current_seconds;

но это полностью все портит. Он компилируется, но устройство «зависает» на этой последовательности.

Как мне отделить биты ST / AMPM / VBATEN от фактических данных, которые мне нужны, и какой хороший метод реализации циклов for для различных обстоятельств, которые они представляют (например, чтение 12-часового времени, если бит 6 = 0). и 24 часа, если бит6 = 1, и т. д.).

Я немного новичок в C, и это мой первый опыт в электронике, поэтому я очень ценю любую помощь. Спасибо.


person samanthapants    schedule 20.02.2014    source источник
comment
Я не думаю, что этот вопрос подходит здесь. просто прочитайте о побитовых операторах C (|, &, ‹‹) и битовых манипуляциях.   -  person Karoly Horvath    schedule 20.02.2014
comment
Порекомендуете ли вы где-нибудь, что может быть более подходящим? Извините, если я потерял его.   -  person samanthapants    schedule 20.02.2014
comment
Маскирование битов, которые вы хотите просмотреть, выполняется с помощью оператора & и битов, установленных в 1, которые вы хотите разделить. Так что, если вы смотрите на мои прочитанные секунды, возможно, их просто преобразовать с помощью (((regdata & 0x7) * 10) + (regdata & 0xf); я думаю   -  person kenny    schedule 20.02.2014
comment
Подобные вопросы, вероятно, получат лучший ответ на electronics.stackexchange.com. К сожалению, SO перенаселен программистами для ПК. Тем не менее, вам нужно сузить фактический вопрос и не задавать несколько вопросов в одном.   -  person Lundin    schedule 20.02.2014
comment
@MartinJames Нет. Никогда не используйте битовые поля для битового отображения. На самом деле, не используйте их ни для чего. см. это.   -  person Lundin    schedule 20.02.2014
comment
@Lundin - очевидно, потребуется упакованная директива. Я использовал такой подход много лет с несколькими компиляторами. Без проблем.   -  person Martin James    schedule 20.02.2014
comment
@samanthapants Не имеет отношения к самому вопросу, но ваши значения в WriteI2C(0x11|0x64) совсем не то, что вы говорите, как вы думаете, - 12-часовой бит 6 является десятичным 64, тогда как ваш шестнадцатеричный 0x64 имеет биты 2, 5 и 6. В шестнадцатеричном формате правильное значение будет 0x40, или вы можете просто использовать побитовые сдвиги, чтобы получить то же значение: (1<<6)   -  person Arkku    schedule 20.02.2014
comment
stackoverflow.com/questions/47981/   -  person Karoly Horvath    schedule 21.02.2014
comment
@Arkku ой, я идиот. Спасибо, что указали на это. Спасибо всем за ответы. Похоже, мой первоначальный подход был слишком упрощенным, поэтому я буду реализовывать некоторые из них сейчас. Очень признателен.   -  person samanthapants    schedule 21.02.2014


Ответы (2)


Чтобы удалить (обнулить) бит, вы можете И значение с маской, в которой установлены все остальные биты, то есть дополнение битов, которые вы хотите обнулить, например:

value_without_bit_6 = value & ~(1<<6);

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

if (value & (1<<6)) {
    // bit 6 is set
} else {
    // bit 6 is not set
}

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

value_in_bits#defineand_5 = (value & ((1<<4)|(1<<5))) >> 4;

Для более читаемого кода вы должны использовать константы или макросы #defined для представления различных битовых масок, которые вам нужны, например:

#define BIT_VBAT_EN (1<<3)

if (value & BIT_VBAT_EN) {
   // VBAT is enabled
}

Другой способ сделать это — использовать битовые поля для определения организации битов, например:

typedef union {
    struct {
        unsigned ones:4;
        unsigned tens:3;
        unsigned st:1;
    } seconds;
    uint8_t byte;
} seconds_register_t;

seconds_register_t sr;
sr.byte = READ_ADDRESS(0x00);
unsigned int seconds = sr.seconds.ones + sr.seconds.tens * 10;

Потенциальная проблема с битовыми полями заключается в том, что код, сгенерированный компилятором, может быть непредсказуемо большим или неэффективным, что иногда вызывает проблемы с микроконтроллерами, но, очевидно, его приятнее читать и писать. (Еще одна проблема, которую часто упоминают, заключается в том, что организация битовых полей, например порядок байтов, в значительной степени не определена стандартом C и, следовательно, не гарантируется переносимость между компиляторами и платформами. Однако я считаю, что низкоуровневая разработка для микроконтроллеров, как правило, по своей сути непереносимый, поэтому, если вы найдете правильную раскладку битов, я бы не стал считать использование битовых полей «неправильным», особенно для проектов для любителей.)

Тем не менее, вы можете добиться такого же удобочитаемого синтаксиса с помощью макросов; просто сам макрос менее читаем:

#define GET_SECONDS(r) ( ((r) & 0x0F) + (((r) & 0x70) >> 4) * 10 )
uint8_t sr = READ_ADDRESS(0x00);
unsigned int seconds = GET_SECONDS(sr);
person Arkku    schedule 20.02.2014

Что касается самой битовой маскировки, вы захотите создать модель этой карты памяти в своем микроконтроллере. Самый простой и приятный способ сделать это — #define несколько битовых масок, например:

#define REG1_ST          0x80u
#define REG1_10_SECONDS  0x70u
#define REG1_SECONDS     0x0Fu

#define REG2_10_MINUTES  0x70u
...

А затем при чтении каждого байта маскируйте интересующие вас данные. Например:

bool    st          = (data & REG1_ST) != 0;
uint8_t ten_seconds = (data & REG1_10_SECONDS) >> 4;
uint8_t seconds     = (data & REG1_SECONDS);

Важно минимизировать количество «магических чисел» в исходном коде.

Запись данных:

reg1 = 0;
reg1 |= st ? REG1_ST : 0;
reg1 |= (ten_seconds << 4) & REG1_10_SECONDS;
reg1 |= seconds & REG1_SECONDS;

Обратите внимание, что я не упомянул о связи I2C.

person Lundin    schedule 20.02.2014