1 Модули микроконтроллеров XMEGA
Микроконтроллеры AVR XMEGA состоят из нескольких блоков: ядро процессора AVR, ОЗУ, флэш-память, EEPROM и некоторые периферийные модули. Эти строительные блоки называются «типами модулей». XMEGA может иметь один или несколько экземпляров одного типа. Все экземпляры модуля одного типа имеют одинаковые возможности и функции.
Некоторые типы модулей могут составлять подмножество других типов модулей. Они наследуют подмножество функций (и регистров) супер типа, все унаследованные функции полностью совместимы. Это относится, например, к таймерам и портам ввода/вывода. Например, подмножество типа модуля таймера может означать, что модуль этого типа по сравнению с полноценным модулем таймера имеет меньше каналов сравнения и захвата. По аналогии, порты ввода-вывода могут иметь менее восьми линий.
Тип модуля может быть «USART», в то время как экземпляром модуля является, например, «USARTC0», где «C0» суффикс, указывающий экземпляр «USART с номером 0 на порте С». Для простоты, в данном документа экземпляр модуля будет называться модулем, если не возникнет необходимости их различать.
Каждый модуль имеет набор регистров, которые содержат биты управления или состояния. Все модули одного типа содержат одинаковый набор (или подмножество) регистров, и все эти регистры содержат одинаковый набор (или подмножество) битов управления и состояния.
Рисунок 2-1. Типы модулей, экземпляры, регистры и биты.
Каждый модуль имеет фиксированный базовый адрес в карте памяти ввода/вывода, и все регистры, имеющиеся в модуле, имеют фиксированные смещения адреса относительно базового адреса модуля. Таким образом, каждый регистр имеет не только абсолютный адрес в пространстве памяти ввода/вывода, но и также относительный адрес, определяемый его смещением. Смещение адреса регистра одинаково для всех экземпляров модуля определенного типа, что упрощает задачу написания драйверов, которые могут быть использованы для всех модулей определенного типа.
1.1 Соглашение об именовании регистров
Регистры, грубо говоря, можно разделить на управляющие, статусные и регистры данных, и именование регистров отражает это. Для модуля управляющий регистр общего назначения называется CTRL. Если модуль содержит несколько управляющих регистров общего назначения, в их именах будет присутствовать суффикс. То есть управляющие регистры будут называться CTRLA, CTRLB, CTRLC, и так далее. Это также относится и к регистрам статуса.
Для регистров, которые имеют конкретные функции, эта функциональность отражена в названии. Например, регистр, который управляет уровнем прерываний модуля, называется INTCTRL.
Так как в AVR шина данных 8-битная, регистры более восьми бит реализованы с использованием нескольких 8-битных регистров. Для 16-битных регистров, имена старшего и младшего байтов состоят из имени регистра и букв «H» и «L» соответственно. Например, регистр счёта 16-разрядного таймера-счётчика называется CNT. А имена байтов CNTL и CNTH.
Для регистров размером более 16 бит байты пронумерованы от младшего к старшему. Например, 32-разрядный регистр калибровки АЦП называется CAL. Четыре байта этого регистра называются CAL0, CAL1, CAL2 и CAL3 (от младшего к старшему значимому байту).
Большинство компиляторов языка C предлагают автоматическую обработку доступа к многобайтным регистрам. В этом случае для получения доступа к 16-битному регистру счёта таймера-счётчика можно использовать имя CNT, без суффиксов «Н» или «L». Это также касается 32-разрядных регистров.
1.2 Соглашение об именовании битов
Биты регистров могут выполнять отдельные функции или быть частью группы бит, которые выполняют связанные функции: отдельным битом может быть бит, включающий модуль. Например, для USART это бит ENABLE. Группа бит может состоять из двух и более битов, которые совместно отвечают за специфическую функцию модуля, к которому они относятся. Группа бит обеспечивает до 2n вариантов, где n — число бит в группе битов. Примером группы битов являются два бита, которые контролируют уровень прерывания Завершения Приёма модуля USART (RXINTLVL[1:0]). Эти два бита позволяют выбрать один из следующих вариантов:
Таблица 2-1. Биты RXINTLVL и соответствующие им уровни прерывания
RXINTLVL1 | RXINTLVL0 | Выбранный уровень прерывания |
---|---|---|
0
|
0
|
Прерывания отключены |
0
|
1
|
Низкий уровень прерывания |
1
|
0
|
Средний уровень прерывания |
1
|
1
|
Высокий уровень прерывания |
Биты, которые являются частью группы, всегда имеют числовой суффикс. Биты, которые не являются частью битовой группы, никогда не будут иметь числовых суффиксов. Управляющий регистр таймера-счётчика «D» имеет две битовые группы, EVACT и EVSEL. Биты в этих группах имеют числовой суффикс, а бит EVDLY, который не является частью группы битов, такого суффикса не имеет.
Таблица 2-2. Группы битов и имена битов управляющего регистра таймера-счётчика «D» — CTRLD
Имя группы | EVACT | – | EVSEL | |||||
---|---|---|---|---|---|---|---|---|
Имя бита |
EVACT2
|
EVACT1
|
EVACT0
|
EVDLY
|
EVSEL3
|
EVSEL2
|
EVSEL1
|
EVSEL0
|
Номер бита |
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
2 Написание кода на языке C для XMEGA
В последующих разделах основное внимание уделяется написанию кода на языке программирования C для микроконтроллеров XMEGA. Приведённые примеры показывают, как сделать код хорошо читаемым и портируемым между различными устройствами XMEGA.
Модули микроконтроллеров XMEGA находятся в специальных и непрерывных блоках памяти, и могут рассматриваться как инкапсулированные блоки. Это означает (касательно доступа к модулям при программировании на языке C) следующее: модули инкапсулируются использованием структур языка C, в которых содержаться все регистры модуля. Рисунок 3-1 наглядно это иллюстрирует.
Рисунок 3-1. Модули, размещённые отдельными блоками внутри карты памяти ввода/вывода.
Стоит обратить внимание на то, что некоторые регистры не имеют прямой связи с какими либо модулями. Они не являются инкапсулированными в структуры, так как структуры используются для привязки регистров с модулям.
Для проектов с большим количеством кода структуры, описывающие модули, обеспечивают не только хорошую читаемость, но и возможность повторного использования драйверов модулей, тем самым делая код более компактным. Далее это будет описано более подробно.
Этот документ описывает соглашение об именовании и методы доступа к регистрам, которые отличаются от того, к чему привыкли ветераны программирования для микроконтроллеров AVR. Но следует знать, что «классический» способ доступа к регистрам по-прежнему поддерживается заголовочными файлами. Это также применимо и на уровне битов.
2.1 Заголовочные файлы XMEGA
Для каждого устройства XMEGA существует свой заголовочный файл. Если целевое устройство указано в настройках проекта (предполагается, что используется среда IDE IAR EWAVR) и подключен файл устройства так же, как показано в листинге 3-1, компилятор IAR будет автоматически подключать правильный заголовочный файл.
Листинг 3-1. Включение заголовочного файла в IAR
#include <ioavr.h>
Преимущество такого подхода заключается в том, что при изменении целевого устройства нет необходимости менять исходный код проекта. Придется поменять только его свойства.
3.2 Регистры модулей
Карта ввода/вывода организованна таким образом, что все регистры одного периферийного модуля расположены одним непрерывным блоком памяти. Регистры, относящиеся к разным модулям, не пересекаются. Это делает возможным описать все периферийные модули в виде структур языка С, где адрес структуры определяется базовым адресом модуля. Все регистры, принадлежащие модулю, являются элементами структуры модуля.
Примером является модуль программируемого многоуровневого контроллера прерываний (PMIC). Определение структуры для этого модуля показано в листинге 3-2, а пример ее использования в листинг 3-3. Обратите внимание, что пример в листинге 3-3 предполагает наличие экземпляра типа PMIC_t с именем PMIC. Этот вопрос рассматривается далее в этом документе.
Листинг 3-2. Определение структуры модуля
typedef struct PMIC_struct {
unsigned char STATUS; // Status Register
unsigned char INTPRI; // Interrupt Priority
unsigned char CTRL; // Control Register
} PMIC_t;
Листинг 3-3. Использование структуры, описывающей модуль
unsigned char temp;
temp = PMIC.STATUS; // Read status register into temp
PMIC.CTRL |= PMIC_PMRRPE_bm; // Set PMRRPE bit in control
// register
2.2.1 Регистры, состоящие из нескольких слов
Некоторые регистры используются в сочетании с другими регистрами для представления 16 или 32 битных значений. В качестве примера можно посмотреть определение структуры для модуля АЦП, приведённое в листинге 3-4.
Листинг 3-4. Определение структуры АЦП
typedef struct ADC_struct {
unsigned char CH0MUXCTRL; // Channel 0 MUX Control
unsigned char CH1MUXCTRL; // Channel 1 MUX Control
unsigned char CH2MUXCTRL; // Channel 2 MUX Control
unsigned char CH3MUXCTRL; // Channel 3 MUX Control
unsigned char CTRLA; // Control Register A
unsigned char CTRLB; // Control Register B
unsigned char REFCTRL; // Reference Control
unsigned char EVCTRL; // Event Control
WORDREGISTER(CH0RES); // Channel 0 Result
WORDREGISTER(CH1RES); // Channel 1 Result
WORDREGISTER(CH2RES); // Channel 2 Result
WORDREGISTER(CH3RES); // Channel 3 Result
unsigned char reserved_0x10;
unsigned char reserved_0x11;
unsigned char CH0INTCTRL; // Channel 0 Interrupt Control
unsigned char CH1INTCTRL; // Channel 1 Interrupt Control
unsigned char CH2INTCTRL; // Channel 2 Interrupt Control
unsigned char CH3INTCTRL; // Channel 3 Interrupt Control
unsigned char INTFLAGS; // Interrupt Flags
WORDREGISTER(CMP); // Compare Value
unsigned char PRESCALER; // Clock Prescaler
unsigned char reserved_0x1A;
unsigned char reserved_0x1B;
unsigned char CALCTRL; // Calibration Control
DWORDREGISTER(CAL); // Calibration Value
} ADC_t;
В листинге 3-4, регистры результата канала АЦП CH0RES, CH1RES, CH2RES, CH3RES и регистр сравнения CMP являются 16-разрядными значениями. Они объявлены с помощью макроса WORDREGISTER, показанного в листинге 3-5. Калибровочный регистр CAL является 32-разрядным значением, которое объявлено с помощью макроса DWORDREGISTER, показанного в листинге 3-6.
Листинг 3-5. Макрос WORDREGISTER
#define WORDREGISTER(regname) \
union { \
unsigned short regname; \
struct { \
unsigned char regname ## L; \
unsigned char regname ## H; \
}; \
}
Листинг 3-6. Макрос DWORDREGISTER
#define DWORDREGISTER(regname) \
union { \
unsigned long regname; \
struct { \
unsigned char regname ## 0; \
unsigned char regname ## 1; \
unsigned char regname ## 2; \
unsigned char regname ## 3; \
}; \
}
Как видно из листинга 3-5, макрос WORDREGISTER использует суффиксы «Н» и «L» для старшего и младшего байта соответственно. В макросе DWORDREGISTER используются числовые суффиксы, указывающие порядок следования байтов. Как 16-битные, так и 32-битные значения регистров можно получить в 16-bit/32-bit режиме, используя имя регистра без суффикса. Это демонстрируется в листинге 3-7.
Листинг 3-7. Доступ к регистрам различного размера
unsigned char tempByte;
unsigned int tempWord;
unsigned long tempDword;
tempByte = ADCA0.CTRLA; // Read control register A
tempWord = ADCA0.CH0RES; // Read Channel 0 16-bit result
tempDword = ADCA0.CAL; // Read 32-bit Calibration value
В листинге 3-7 показано, как прочитать регистр CTRLA размером один байт, как прочитать регистр CH0RES[H:L] размером два байта с помощью 16-битных операций, и как прочитать регистр CAL[3:0] размером четыре байта с помощью 32-битных операций. Компиляторы языка C обрабатывают многобайтные регистры автоматически. Следует заметить, что в некоторых случаях для предотвращения искажения данных чтение и запись многобайтных регистров просто необходимо выполнять одной атомарной операцией. В этом случае во время доступа к многобайтным регистрам прерывания должны быть отключены. Это необходимо чтобы убедиться, что обработчики прерываний не помешают доступу к многобайтным регистрам. Статья по применению AVR1306 включает в себя примеры кода, демонстрирующие атомарный доступ к регистрам модуля таймера-счётчика микроконтроллера XMEGA.
2.3 Адреса модулей
Определения всех периферийных модулей находятся в заголовочных файлах микроконтроллеров XMEGA. Адреса модулей объявлены с соблюдением стандарта ANSI C, что делает их совместимыми с большинством доступных компиляторов языка C. Листинг 3-8 демонстрирует определение ADC 0 на порте A.
Листинг 3-8. Определение периферийного модуля
#define ADCA (*(volatile ADC_t *) 0x0200)
Листинг 3-8 показывает, как определение экземпляра модуля использует разыменованный указатель на абсолютный адрес в памяти, совпадающий с базовым адресом экземпляра модуля. Указатели модулей предопределены в заголовочных файлах XMEGA, поэтому нет необходимости добавлять эти определения в свой исходный код.
2.4 Битовые маски и маски групп битов
Манипулировать битами регистров можно с помощью предопределенных масок, или, в качестве альтернативного варианта, с помощью битовых позиций. Для большинства задач использовать битовые позиции не рекомендуется. Предопределенные маски, связанные с отдельными битами, так же называются битовыми маскам, а связанные с группой битов называются маской группы битов, или, для краткости, просто маска группы.
Битовые маски используется как для установки, так и для сброса отдельных битов. Маски групп битов, как правило, используются для сброса нескольких бит, составляющих группу битов. Установка нескольких бит, являющихся частью группы битов, рассматривается в разделе 3.5.
2.4.1 Битовые маски
Рассмотрим регистр управления таймером-счётчиком D (CTRLD). Группы битов, имена битов, позиций битов и битовые маски этого регистра приведены в таблице 3-1.
Таблица 3-1. Группы битов, имена битов, позиций битов и битовые маски регистра управления таймером-счётчиком D (CTRLD)
Имя группы | EVACT | – | EVSEL | |||||
---|---|---|---|---|---|---|---|---|
Имя бита | EVACT2 | EVACT1 | EVACT0 | EVDLY | EVSEL3 | EVSEL2 | EVSEL1 | EVSEL0 |
Битовая позиция |
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Битовая маска |
0x80
|
0x40
|
0x20
|
0x10
|
0x08
|
0x04
|
0x02
|
0x01
|
Так как имена битов должны быть уникальными для компилятора, все биты имеют префикс типа модуля, к которому они принадлежат. Часто имя типа модуля сокращенно. Имена всех битов, связанных с модулями таймеров-счётчиков, имеют префикс «TC_».
Чтобы отличить битовые маски от битовых позиций, используется суффикс. Для битовых масок применяется суффикс «_bm». Таким образом, имя битовой маски для бита EVDLY будет TC_EVDLY_bm. В листинге 3-9 показано типичное использование битовой маски. В коде устанавливается бит EVDLY регистра CTRLD, при этом остальные биты регистра остаются без изменений.
Листинг 3-9. Использование битовой маски
TCD0.CTRLD |= TC_EVDLY_bm;
2.4.2 Маски группы битов
Многие функции управляются группами битов. Биты EVACT[2:0] и EVSEL[3:0] регистра CTRLD таймера-счётчика являются примерами групп битов. Значение битов в группе выбирает конкретную конфигурацию.
Часто при изменении значения бит в группе битов перед установкой нового значения необходимо сначала сбросить все биты группы. Иначе говоря: не достаточно просто установить биты, которые должны быть установлены, требуется также сбросить биты, которые должны быть сброшены. Для облегчения этого процесса и были определены маски групп битов. Маска группы битов использует то же самое имя, что и биты в группе битов, но с суффиксом «_gm».
В листинге 3-10 показано, как соотносятся между собой маска группы битов и битовые маски. В действительности, значения масок групп предварительно предопределены в заголовочных файлах, так что компилятору не нужно их вычислять каждый раз заново.
Листинг 3-10. Соотношение между маской группы битов и битовыми масками
#define TC_EVACT_gm (TC_EVACT2_bm | TC_EVACT1_bm | TC_EVACT0_bm)
Маски групп битов в первую очередь предназначены для сброса старой конфигурации группы битов перед записью нового значения. В листинге 3-11 показано, как это можно сделать. В коде сбрасывается группа битов EVACT регистра CTRLD таймера-счётчика D0. Эта конструкция сама по себе не очень полезна. Маски групп битов используются, как правило, в сочетании с конфигурационными масками групп битов, которые будут рассматриваться в разделе 3.5.
Листинг 3-11. Использование маски группы битов
TCD0.CTRLD &= ~(TC_EVACT_gm); // Сброс группы бит с помощью маски группы.
2.5 Конфигурационные маски групп битов
Часто для выяснения необходимых битовых шаблонов для той или иной конфигурации требуется сверятся с документацией на микроконтроллер. Это так же относится и для чтения значений или для отладки кода. Для повышения читаемости кода и для сведения вероятности неправильной установки бит в битовых группах были созданы наборы конфигурационных масок групп битов. Каждая «конфигурационная маска группы битов» выбирает конфигурацию для конкретной «маски группы битов».
Имя конфигурационной маски группы битов состоит из имени типа модуля, имени группы битов, описания конфигурации и суффикса «_gc», который указывает на то, что это конфигурационная маска группы битов. Пример представлен на рисунке 3-2.
Рисунок 3-2. Состав имени конфигурационной группы (на примере битов управления уровнем прерывания Завершения Приёма модуля USART)
Изучив конфигурационную группу, представленную на рисунке 3-2, можно увидеть, что она используется для выбора конфигурации битов RXCINTLVL модуля USART. Данная конфигурационная группа выбирает высокий (HI) уровень прерывания.
Битовая группа для уровня прерывания Завершения Приёма состоит из двух битов, RXINTLVL[1:0]. В таблице 3-2 показаны возможные конфигурации для этой битовой группы.
Конфигурации имеют имена «OFF», «LO», «MED» и «HI». Эти имена делают конфигурации простыми при написании и поддержке кода, так как требуется затратить мало усилий чтобы понять, какую конфигурацию выбирает конкретная конфигурационная маска.
Таблица 3-2. Биты RXINTLVL и соответствующие им уровни прерываний модуля прерываний
RXINTLVL1 | RXINTLVL0 | Уровень прерывания | Маска конфигурации группы |
0
|
0
|
Прерывание отключено | USART_RXCINTLVL_OFF_gc |
0
|
1
|
Низкий уровень прерывания | USART_RXCINTLVL_LO_gc |
1
|
0
|
Средний уровень прерывания | USART_RXCINTLVL_MED_gc |
1
|
1
|
Высокий уровень прерывания | USART_RXCINTLVL_HIGH_gc |
Чтобы изменить конфигурацию битовой группы на новою, конфигурационная битовая группа используется, как правило, в сочетании с маской группы бит таким образом, чтобы в первую очередь стирались старые настройки. В листинге 3-12 показано, как можно маску группы и конфигурационную маску использовать для перенастройки уровня прерывания Завершения Приёма модуля USART C0 на средний уровень.
Листинг 3-12. Изменение конфигурации битовой группы
USARTC0.INTCTRL = (USARTC0.INTCTRL & ~USART_RXCINTLVL_gm ) |
USART_RXCINTLVL_MED_gc;
У Вас может возникнуть соблазн разделить код из листинга 3-12 на две отдельные строки кода, в первой сделать сброс группы бит, а во второй установить новое значение. Этого делать не рекомендуется. Так как регистр INTCTRL определен с ключевым словом volatile, две отдельных строки кода породят две отдельные операции записи регистра INTCTRL, то есть две операции вместо одной. Кроме этого, такой код будет неэффективным потому, что может перевести периферийный модуль в неожиданное состояние.
Но использование масок групп для сброса битов требуется не всегда. В листинге 3-13 показано, как настроить одновременно уровни всех прерываний модуля USARTC0. Уровни прерываний Завершения Приёма, Завершения Передачи и Опустошения Регистра Данных модуля USART устанавливаются в значения «Medium», «OFF» и «LOW» соответственно.
Листинг 3-13. Настройка всех конфигураций регистра одновременно
USARTC0.INTCTRL = USART_RXCINTLVL_MED_gc |
USART_TXCINTLVL_OFF_gc |
USART_DREINTLVL_LO_gc;
2.5.1 Перечисления конфигурационных масок групп битов
В отличие от битовых масок и масок групп битов, конфигурационные маски групп битов определяются с помощью перечислений языка C. Для каждой группы битов определяется одно перечисление. В листинге 3-14 показано перечисление для группы битов RXCINTLVL модуля USART.
Листинг 3-14. Перечисление RXCINTLVL
typedef enum {
USART_RXCINTLVL_OFF_gc = (0x00 << 4);
USART_RXCINTLVL_LO_gc = (0x01 << 4);
USART_RXCINTLVL_MED_gc = (0x02 << 4);
USART_RXCINTLVL_HI_gc = (0x03 << 4);
} USART_RXCINTLVL_t;
Как видно из листинга 3-14, название перечисления состоит из имени типа модуля (USART), имени группы битов (RXCINTLVL) и суффикса (_t), который указывает, что это тип данных.
Каждая константа перечисления ведет себя так же, как обычная константа. Однако, применение перечислений имеет то преимущество, что они создают новый тип данных. USART_RXCINTLVL_t можно использовать в качестве имени типа так же, как int или char. Константа перечисления может использоваться как обычное целое число, но присваивание переменной, имеющей тип перечисления, целого числа вызовет предупреждение компилятора. Это может быть полезно для программистов. Представьте себе функцию, которая устанавливает уровень прерывания Завершения Приёма модуля USART. Если функция принимает уровень прерывания как целый тип (например, unsigned char), в функцию может быть передано любое допустимое или недопустимое значение. Если же функция будет принимать параметр типа USART_RXCINTLVL_t, то допустимыми входными значениями функции будут только четыре предопределенных константы перечисления USART_RXCINTLVL_t. Передача в функцию значений, отличных от предопределенных констант, приведёт к предупреждению компилятора.
Обратите внимание на то, что константы в листинге 3-14 смещены относительно их фактического положения. Это означает, что константы перечисления являются фактическими значениями, которые должны быть записаны в регистр. Никаких дополнительных сдвигов делать не требуется.
3.6 Вызовы функций и драйверы модулей
При написании драйверов для типов модулей, имеющих несколько экземпляров, тот факт, что все экземпляры имеют одинаковую карту памяти регистров, делает возможным использование драйвера для всех экземпляров типа модуля. Если драйвер получает в качестве аргумента указатель, указывающий на соответствующий экземпляр модуля, драйвер может быть использован для всех модулей данного типа. С точки зрения переносимости кода это дает большое преимущество.
Рассмотрим устройство с восемью таймерами-счётчиками. Функций для инициализации и доступа к модулям таймеров-счётчиков могут быть общими для всех экземпляров. Хотя есть небольшие накладные расходы при передачи указателя модуля в функцию, общий размер кода все же будет снижаться, поскольку код используется повторно для всех экземпляров каждого типа модуля. Что ещё важней, используя такой подход можно снизить время разработки, расходы на поддержку и портирование кода.
В листинге 3-15 показана функция, которая использует указатель на модуль для выбора источника тактового сигнала для любого модуля таймера-счётчика.
Листинг 3-15. Пример функции, использующей указатель на экземпляр модуля
void TC_ConfigClockSource(
volatile TC_t * tc,
TC_CLKSEL_t clockSelection)
{
tc->CTRLA = tc->CTRLA & ~TC_CLKSEL_gm | clockSelection;
}
Функция принимает два аргумента: указатель на модуль типа TC_t, и тип конфигурационной группы. Код функции использует указатель модуля таймера-счётчика для доступа к регистру CTRLA с целью установить новый источник тактового сигнала для модуля таймера-счётчика, экземпляр которого определен параметром tc.
В листинге 3-16 приведён код, демонстрирующий использование функции из листинга 3-15 для настройки разных таймеров-счётчиков на различные источники тактового сигнала.
Листинг 3-16. Вызов функции, принимающей указатель экземпляра модуля в качестве параметра
TC_ConfigClockSource (&TCC0, TC_CLKSEL_OFF_gc);
TC_ConfigClockSource (&TCC1, TC_CLKSEL_DIV1_gc);
TC_ConfigClockSource (&TCD0, TC_CLKSEL_DIV2_gc);
TC_ConfigClockSource (&TCD1, TC_CLKSEL_DIV1024_gc);
3 Альтернативные способы написания кода
Для удобства и чтобы избежать необходимости немедленного изменения стиля программирования людьми, давно работающими с микроконтроллерами AVR, оставлена возможность использовать стиль программирования, не завязанный на структуры. Кроме того, можно использовать стиль именования битов, который используется для микроконтроллеров megaAVR®. В этом разделе кратко описаны альтернативные способы доступа к регистрам и использование имён битов.
3.1 Именование регистров
Можно получить доступ к любому регистру без использования структуры, описывающей модуль. Для прямой ссылки на регистр соедините имя экземпляра модуля, символ подчеркивания и имя регистра. Такой же способ именования регистров используется и при программировании на ассемблере.
Пример: Для доступа к регистру CTRLA таймера-счётчика C0 следует использовать имя TCC0_CTRLA.
3.2 Битовые позиции
Для установки или сброса бита можно использовать битовые маски. Битовые позиции в регистре определяются с тем же именем, что и битовые маски, только с дополнительным суффиксом “_bp”. В листинге 4-1 показано, как использовать битовую позицию для настройки регистра.
Листинг 4-1. Альтернативный код доступа к регистру
// Установка бита bit0 порта PORTB_OUT.
PORTB_OUT = (1 << PORTB_OUT0_bp);
Определения для битовых позиций включены в заголовочные файлы исключительно в целях совместимости. Кроме того, они нужны при программировании на ассемблере для инструкций, использующих битовые позиции.
4 Итог
В таблицу 5-1 сведены различные суффиксы, которые используются при работе с битами.
Таблица 5-1. Список суффиксов
Суффикс | Meaning | Example |
---|---|---|
_gm | Маска группы битов | TC_CLKSEL_gm |
_gc | Конфигурационная маска группы битов | TC_CLKSEL_DIV1_gc |
_bm | Битовая маска | TC_CCAEN_bm |
_bc | Битовая позиция | TC_CCAEN_bp |
Использование предлагаемых методов написания кода на языке C для микроконтроллеров XMEGA отнюдь не обязательно, но получаемые при таком методе преимущества не следует сбрасывать со счетов. Ведь Вы получаете надежный, портируемый, повторно используемый и хорошо читаемый код. Чем больше проект и чем больше задействованных функций устройства, тем большие извлекаемые из этого метода преимущества. Для небольших проектов справедливо утверждение, что нет никакой выгоды, но посмотрев с другой стороны можно так же смело утверждать, что применение такого метода написания кода не является недостатком.
Одно можно сказать точно: хороший стиль программирования всегда является преимуществом.
Source: http://radio-house.ucoz.ru