2017-01-13 7 views
0

Я работаю над Motorola HCS08 μCU в CodeWarrior V10.6, я пытаюсь создать битовое поле extern, которое имеет биты из существующих регистров. То, как битовые создаются в заголовке μCU, какКак создать бит из существующих переменных в C

typedef unsigned char byte; 
typedef union { 
    byte Byte; 
    struct { 
    byte PTAD0  :1; 
    byte PTAD1  :1;          
    byte PTAD2  :1;          
    byte PTAD3  :1;          
    byte PTAD4  :1;          
    byte PTAD5  :1;          
    byte PTAD6  :1;          
    byte PTAD7  :1;          
    } Bits; 
} PTADSTR; 
extern volatile PTADSTR _PTAD @0x00000000; 
#define PTAD       _PTAD.Byte 
#define PTAD_PTAD0      _PTAD.Bits.PTAD0 
#define PTAD_PTAD1      _PTAD.Bits.PTAD1 
#define PTAD_PTAD2      _PTAD.Bits.PTAD2 
#define PTAD_PTAD3      _PTAD.Bits.PTAD3 
#define PTAD_PTAD4      _PTAD.Bits.PTAD4 
#define PTAD_PTAD5      _PTAD.Bits.PTAD5 
#define PTAD_PTAD6      _PTAD.Bits.PTAD6 
#define PTAD_PTAD7      _PTAD.Bits.PTAD7 

который позволит значение регистра быть изменено либо PTAD = 0x01 или PTAD_PTAD0 = 1, например. Это определение в основном одно и то же для PTAD, PTBD, PTCD, ... PTGD, единственное изменение - это адрес.

Мой attemp создать пользовательское битовое из предыдущих существующих переменных

typedef union { 
    byte Byte; 
    struct { 
    byte *DB0; 
    byte *DB1; 
    byte *DB2; 
    byte *DB3; 
    byte *DB4; 
    byte *DB5; 
    byte *DB6; 
    byte *DB7; 
    } Bits; 
} LCDDSTR; 

Я хотел бы создать и инициализировать битовый в LCDDSTR lcd = {{&PTGD_PTGD6, &PTBD_PTBD5, ...}}, потому что по какой-то причине, инициализация как LCDSTR lcd = {*.Bits.DB0 = &PTGD_PTGD6, *.Bits.DB1 = &PTBD_PTBD5, ...} (рассматривая его как структуру , пожалуйста, исправьте меня снова). Совет в How to initialize a struct in accordance with C programming language standards не работает с этим компилятором (он работает в онлайн-компиляторе).

Однако, как вы можете видеть, я как бы группирую биты, и (если бы это сработало) я мог бы изменить значения фактического регистра, выполнив *lcd.Bits.DB0 = 1 или что-то в этом роде, но если я это сделаю lcd.Byte = 0x00, я бы изменил последний (я думаю) байт адреса памяти, содержащегося в lcd.Bits.DB0, вы знаете, потому что структура фактически не содержит данные, а указатели вместо этого.

Как я могу продолжить создание структуры, которая может содержать и изменять биты из нескольких регистров? (Я думаю, проблема заключается в том, что в памяти биты не одно рядом друг с другом, что, я думаю, облегчило бы). Возможно ли это? Надеюсь, это так.

+0

Есть ли адреса памяти, которые отображаются в отдельных регистрах? Например, адрес памяти 0x00000000 отображается в регистр 0, а адрес памяти 0x00000001 сопоставляется с регистром 1 (при условии, что 1 байтовый или 8-битный регистры)? Я ожидал бы использовать ту же конструкцию «PTADSTR» для определения, а затем создать создать указатель на начало области памяти, где расположены регистры, и ссылаться на них как на массив структуры «PTADSTR». –

+0

У вас не может быть указатель на бит-поле. Посмотрите http://stackoverflow.com/questions/13547352/c-cannot-take-address-of-bit-field. –

+0

@RichardChambers Я могу сказать, что далее в определении структура PTADDSTR определена как 0x00000001, а затем PTBDSTR до 0x00000002, затем PTBDDSTR до 0x00000003 и т. Д. И да 8-битные регистры, я думаю, это отвечает на ваши первые вопросы, но я Подумайте, создайте массив с указателями на биты каждой структуры, вы говорите? – Hans

ответ

1

Как я могу продолжить создание структуры, которая может содержать и изменять биты из нескольких регистров? (Я думаю, проблема в том, что в памяти биты не одно рядом друг с другом.

Я не думаю, что вы можете сделать это с помощью структуры. Это потому, что битполы по определению должны занимать одинаковые или смежные адреса.

Однако макросы могут быть полезны здесь

#define DB0 PTGD_PTGD6 
#define DB1 PTBD_PTBD5 
.... 

И чтобы очистить биты все 0 или установить все 1 вы можете использовать многострочный макрос

#define SET_DB(x) do { \ 
    PTGD_PTGD6 = x; \ 
    PTBD_PTBD5 = x; \ 
    ......    \ 
} while(0) 
+0

Большое вам спасибо за ваш ответ. Мне нравится этот подход, я ожидал что-то вроде «DB = 0xAB», как указано в моем вопросе, но, я думаю, я мог бы написать макрос 'SET_DB (x)', чтобы я мог получить доступ к каждому биту в 'x' и назначить его к каждому биту BD, например, позвольте мне попробовать, чтобы увидеть, работает ли он так, как ожидалось. Я также должен видеть, достаточно ли этого (я имею в виду, что по какой-то причине мне нужны биты, содержащиеся в переменной). – Hans

+0

Сделано много тестов и работал как шарм, большое вам спасибо. – Hans

+0

Добро пожаловать. –

1

Как я могу продолжить создание структуры, которая может содержать и изменять биты из нескольких регистров?

Вы не можете.

Структура должна представлять собой единый непрерывный блок памяти - в противном случае такие операции, как принятие структуры sizeof или выполнение операций с указателем на один, не имеют смысла.

Если вы хотите переставить биты значения, вам нужно будет найти способ сделать это явно. Если порядок ваших бит относительно прост, это может быть возможно с несколькими побитовыми операциями; если это странно, вам может потребоваться использовать таблицу поиска.

Помимо этого: битподы в C довольно ограничены. Язык не дает много гарантий относительно того, как структура, содержащая битовые поля, будет исчерпана в памяти; их обычно лучше избегать для переносного кода. (Который здесь не применяется, поскольку вы пишете код для конкретной комбинации компилятора/микроконтроллера, но это стоит иметь в виду в целом.)

+0

Человек, большое вам спасибо за ваш ответ. Очень полезно. – Hans

1

Ваш союз, к сожалению, не имеет никакого смысла, поскольку он образует объединение от одного byte и 8 byte*. Поскольку указатель 16 бит на HCS08, это заканчивается как 8 * 2 = 16 байтов данных, которые нельзя использовать каким-либо значимым образом.

  • Пожалуйста, обратите внимание, что структура C называется битовых полей очень плохо определены стандартом, и поэтому следует избегать в любой программе. See this.
  • Обратите внимание, что карты регистров Codewarrior не находятся удаленно близко к следующему стандарту C (или MISRA-C).
  • Обратите внимание, что структуры, как правило, проблематичны для сопоставления аппаратных регистров, поскольку структуры могут содержать отступы. У вас нет этой проблемы специально для HCS08, поскольку она не требует выравнивания данных. Но большинство микроконтроллеров этого требуют.

Поэтому, если у вас есть этот вариант, лучше развернуть свою собственную карту регистрации в стандартном C. Регистр данных порта А может просто быть определен следующим образом:

#define PTAD (*(volatile uint8_t*)0x0000U) 
#define PTAD7 (1U << 7) 
#define PTAD6 (1U << 6) 
#define PTAD5 (1U << 5) 
#define PTAD4 (1U << 4) 
#define PTAD3 (1U << 3) 
#define PTAD2 (1U << 2) 
#define PTAD1 (1U << 1) 
#define PTAD0 (1U << 0) 

Как мы можем сказать, определение битовых масок в основном лишний в любом случае, поскольку PTAD |= 1 << 7; одинаково доступно для чтения PTAD |= PTAD7;. Это потому, что это был чистый порт ввода-вывода. С другой стороны, определение текстовых бит-масок для регистров состояния и управления значительно повышает читаемость кода.


Если вы хотите изменить биты из нескольких регистров, вы могли бы сделать что-то вроде следующего:

Предположим, мы имеем RGB (красный-зеленый-синий) LED, общим катодом, с 3-х цветов подключен к 3 различным контактам на 3 разных портах. Вместо избиения дизайнер PCB, вы можете сделать это:

#define RGB_RED_PTD  PTAD 
#define RGB_RED_PTDD PTADD 
... 
#define RGB_BLUE_PTD PTBD 
#define RGB_BLUE_PTDD PTBDD 
... 
#define RGB_GREEN_PTD PTDD 
#define RGB_GREEN PTDD PTDDD 

#define RGB_RED_PIN 1 
#define RGB_BLUE_PIN 5 
#define RGB_GREEN_PIN 3 

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

void rgb_init (void) 
{ 
    RGB_RED_PTDD |= (1 << RGB_RED_PIN); 
    RGB_BLUE_PTDD |= (1 << RGB_BLUE_PIN); 
    RGB_GREEN_PTDD |= (1 << RGB_GREEN_PIN); 
} 

void rgb_yellow (void) 
{ 
    RGB_RED_PTD |= (1 << RGB_RED_PIN); 
    RGB_BLUE_PTD &= ~(1 << RGB_BLUE_PIN); 
    RGB_GREEN_PTD |= (1 << RGB_GREEN_PIN); 
} 

И так далее. Примеры были для HCS08, но то же самое можно, конечно, использовать повсеместно на любом MCU с прямым портом ввода-вывода.

0

Это звучит как подход, такой как следующее, в соответствии с тем, как вы хотели бы пойти с решением.

Я не тестировал это, так как у меня нет оборудования, однако это должно обеспечить альтернативу для просмотра.

Предполагается, что вы хотите включить определенные контакты или отключить отдельные контакты, но не будет случая, когда вы захотите включить некоторые контакты и отключить другие контакты для определенного устройства за одну операцию. Если это так, я бы подумал о том, чтобы тип RegPinNo был беззнаковым коротким, чтобы включать код op для каждой комбинации номеров регистра/вывода.

Это также предполагает, что время операций не является критическим ограничением и что аппаратное обеспечение имеет достаточную мощность, так что небольшие петли не являются большой нагрузкой на пропускную способность и затормаживают процессорное время, необходимое для других целей. Таким образом, этот код может потребовать изменений для улучшения оптимизации, если это необходимо.

Я предполагаю, что вы хотите какой-то легко читаемый способ выражения команды, которая включает и выключает ряд бит, разбросанных по нескольким областям памяти.

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

typedef byte RegPinNo; // upper nibble indicates register number 0 - 7, lower nibble indicates pin number 0 - 7 

const byte REGPINNOEOS = 0xff; // the end of string for a RegPinNo array. 

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

RegPinNo myLed[] = { 0x01, 0x12, REGPINNOEOS }; // LED is addressed through Register 0, Pin 0 and Register 1, Pin 1 (zero based) 

Так что на данный момент у нас есть способ, чтобы описать, что конкретное устройство, светодиод в данном случае рассматривается через ряд регистры/контактные пункты номера.

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

typedef unsigned char byte; 
typedef union { 
    byte Byte; 
    struct { 
     byte PTAD0 : 1; 
     byte PTAD1 : 1; 
     byte PTAD2 : 1; 
     byte PTAD3 : 1; 
     byte PTAD4 : 1; 
     byte PTAD5 : 1; 
     byte PTAD6 : 1; 
     byte PTAD7 : 1; 
    } Bits; 
} PTADSTR; 

// Define a pointer to the beginning of the register area. This area is composed of 
// 8 different registers each of which is one byte in size. 
// We will address these registers as Register 0, Register 1, ... Register 7 which just happens 
// to be how C does its zero based indexing. 
// The bits representing pins on the PCB we will address as Pin 0, Pin 1, ... Pin 7. 
extern volatile PTADSTR (* const _PTAD) = 0x00000000; 

void SetRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte |= pins[bPinNo]; 
    } 
} 

void ClearRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte &= ~pins[bPinNo]; 
    } 
} 

void ToggleRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte ^= pins[bPinNo]; 
    } 
} 

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

void LightLed (int nMilliSeconds) 
{ 
    RegPinNo myLed[] = { 0x01, 0x12, REGPINNOEOS }; // LED is addressed through Register 0, Pin 0 and Register 1, Pin 1 (zero based) 

    SetRegPins(myLed); // turn on the LED 
    Sleep(nMilliSeconds); // delay for a time with the LED lit 
    ClearRegPins(myLed); // turn the LED back off 
} 

Edit - Уточнение

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

При таком подходе у вас будет функция SetRegPins(), которая будет выглядеть следующим образом. Аналогичные изменения потребуются для других функций.

void SetRegPins(RegPinNo *x) 
{ 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 8) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0xFF;   // get the pin mask 
     _PTAD[bRegNo].Byte |= bPinNo; 
    } 
} 

И определения типов будет выглядеть так:

typedef unsigned short RegPinNo; // upper byte indicates register number 0 - 7, lower byte provides pin mask 

const byte REGPINNOEOS = 0xffff; // the end of string for a RegPinNo array. 

и эти элементы будут использоваться как:

void LightLed (int nMilliSeconds) 
{ 
    RegPinNo myLed[] = { 0x0002, 0x0103, REGPINNOEOS }; // LED is addressed through Register 0, Pin 1 and Register 1, Pin 0 and Pin 1 (zero based) 

    SetRegPins(myLed); // turn on the LED 
    Sleep(nMilliSeconds); // delay for a time with the LED lit 
    ClearRegPins(myLed); // turn the LED back off 
}