Счетчик реального времени(RTC) в Xmega.

Posted by Reason89 | Posted in , ,


    В микроконтролерах Xmega используется 16 битный счетчик реального времени(RTC) с одним каналом сравнения. Счетчик реального времени обычно используется в экономичных режимах работы МК с целью сохранения счета времени и возобновления активной работы МК через регулярные интервалы времени. Максимальное время отсчета счетчика 18 с лишним часов. Счетчик реального времени имеет предмасштабирование, с максимальным коэффициентом 1024. Коэффициент предмасштабирования можно задать в регистре CTRL.
    RTC - асинхронный модуль. Он синхронизируется от отдельного источника, который никак не связан с сигналом основной системной синхронизации и другими производными от него сигналами.  RTC может тактироваться от: внутреннего RC-генератора ультранизкой мощности на 32 кГц, внутренний подстраиваемый RC-генератор на 32 кГц( более точный, но и более прожорливый), внешний кварцевый генератор на 32.768 кГц (самый точный).
    RTC формирует событие или прерывание по достижению определенного или максимального значения(т.е. по переполнению). Счетчик реального времени может генерировать прерывания, по переполнению и по сравнению.
    Прерывание по переполнению имеют постоянную частоту при постоянном значении периода (PER). Регистр сравнения (COMP) позволяет изменять частоту прерываний без изменения периода или перезапуска регистра счетчика (CNT).
    Система событий не работает в спящих режимах глубже нерабочего режима (Idle).
    Векторы прерываний RTC:
OVF_vect Вектор прерывания по переполнению счетчика реального времени
PER_vect Вектор прерывания по совпадению в счетчике реального времени
    Функция инициализации RTC:

void RTC_init()
{
    CLK.RTCCTRL = CLK_RTCEN_bm+CLK_RTCSRC_RCOSC_gc;    // Включение и настройка на частоту 1кГц от внутреннего генератора 32кГц
    RTC.PER = 264;                                    // Заносим значение вершины счета.(До какого значения считать)
    RTC.CNT = 0;                                    // Обнуляем счетчик. В этом регистре хранится значение счетчика реального времени.
    RTC.INTCTRL = 0x03;                                // Включаем прерывания. Уровень прерываний высокий
}

RTC.CTRL = 0x01;                                    // Включение счетчика реального времени без предделителя, при 0x00 RTC выключен
                                                    // В этот регистр заносится коэффициент деления при включении RTC

Прерывания в Xmega

Posted by Reason89 | Posted in , ,


     В семейсте Xmega добавлена разветвленная система прерываний. Дл каждого прерывания может быть задано 4 уровня приоритета прерываний(High (высокий), Medium (средний),Low (низкий) и Off (выключенный). Плюс есть несколько не маскируемых прерываний, на вроде отказа внешней частоты.
     При поступлении запроса на обслуживание прерывания с более высоким уровнем приоритета процесс обработки прерывания с более низким уровнем приостанавливается, и процессор начинает обрабатывать более значимый запрос. Запросы на прерывание, которым присвоен статус High, обслуживаются немедленно после поступления, даже если центральный процессор сильно загружен. Чтобы не пропал ни один из запросов, и все они были обслужены, пусть даже и с задержкой есть специальная процедура Round Robin , этакий своеобразный циклический алгоритм диспетчеризации.
    Общая команда для включения всех прерываний осталась неизменной:
sei();
    Далее необходимо включить все три уровня прерываний:
PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
    Соответственно если нужно включить только низкий уровень прерываний, то код упрощается:
PMIC.CTRL |= PMIC_LOLVLEN_bm
    О более конкретной настройке PMIC.CTRL можно прочесть в мануале.
    Для всех переферийных прерываний уровень приоритета выставляется отдельно, в соответствующем регистре. В качестве примера назначи прерыванию по таймеру TCC0, самый высокий уровень прерываний:
TCC0.INTCTRLA = 0x03;
    а для внешнего прерывания на порт ввода-вывода поставим самый низкий уровень прерываний:
PORTA.INTCTRL = 0x01;
    Для каждого порта может быть объявленно только 2 прерывания, int0 и int1:
PORTA.DIR = 0x00;
PORTA.INT0MASK = 0x01;
PORTA.INT1MASK = 0x02;
    Далее для корректной работы прерываний, необходимо подтянуть вывод порта:
PORTA.PIN1CTRL= PORT_OPC_WIREDANDPULL_gc;
PORTA.PIN2CTRL= PORT_OPC_WIREDANDPULL_gc;
    Так же можно настроить по какому из фронтов будет срабатывать прерывание, в качестве примера приведем срабатывание по заднему фронту:
PORTA.PIN1CTRL = PORT_ISC_FALLING_gc;
PORTA.PIN2CTRL = PORT_ISC_FALLING_gc;
     После того, как правильно настроили работу прерываний, необходимо написать обработчки этого прерывания:
ISR(PORTA_INT0_vect)
{
   //Code
}
    В Xmega огромное количество всевозможных прерываний, как по приему и передаче данных по всевозможным протоколам, так и по переполнению и совпаднию таймеров. Гибкая настройка позволяет реализовать практически любые свои потребности.

ЦАП в XMEGA(DAC)

Posted by Reason89 | Posted in , ,

    В микроконтроллерах есть высокоскоростные 12-разрядные аналоговые модули. В дополнении к АЦП и аналоговым компараторам, есть еще и высокопроизводительные цифроаналогвые преобразователи.
    Разрядность ЦАПа XMEGA 12 бит, скорость преобразования — до 1 Мбит/с. ЦАП получает частоту с периферийной тактовой частоты, запуск на преобразование могут осуществлять различные периферийные модули, подключенные к системе событий(Event System). Есть функция формирования нулевого выходного напряжения. Обычно все ЦАПы не отличаются линейностью, когда выходное напряжение в районе нуля. Для этого в  XMEGA есть два регистра, в которые можно записывать 7-битные калибровочные данные, для более точного выставления напряжения.
     Преобразования ЦАП инициируется либо записью значения во входные регистры ЦАП, либо поступлением события от Event System. Данные для преобразования во входные регистры можно записывать как из программы, так и через DMA. Выход с ЦАП может быть выполнен двумя способами. В одном случае один линейный выход на выводе микроконтроллера. В другом два независимых выхода на двух выводах микроконтроллера. В результате два канала ЦАП могут работать независимо друг от друга и выдавать два аналоговых сигнала, различающихся как по амплитуде, так и по частоте.
    В XMEGA отдельные регистры для записи входных данных на выдачу в оба канала. Выход ЦАП можно подключать внутри кристалла к другим узлам, таким как АЦП или к компаратору.
    Привожу самый простой пример работы ЦАПа, программа для микроконтроллера atxmega32a4. Пример просто выводит на вывод микроконтроллера половину питания AVCC.
volatile int Voltage = 0x7F;

void dac_init()
{
    DACB.CTRLC = ( DACB.CTRLC & ~DAC_REFSEL_gm) | DAC_REFSEL_AVCC_gc; // Используем AVCC
    DACB.CTRLB = ( DACB.CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_SINGLE_gc; // Используем один канал ЦАП
    DACB.CTRLA = DAC_CH0EN_bm | DAC_ENABLE_bm;
}

void DACB_out(int Voltage)
{
   //DACB.CH0DATA = Voltage;  // Можно выводить значение из этой переменной
   DACB.CH0DATAH = 0x0F;    // А можно в ручную заполнять значения регистров
   DACB.CH0DATAL = 0xFF;
   while (!DACB.STATUS & DAC_CH0DRE_bm);
}

void dac_init();
void DACB_out(int Voltage);

int main(void)
{
    PORTB.DIR = 0xFF;//0x08;
    PORTB.PIN3CTRL = PORT_ISC_INPUT_DISABLE_gc;
    dac_init();
    
   while(1)
   {
      DACB_out(Voltage);
   }
}

    В регистре CTRLC настраивается источник опорного напряжения:
DACB.CTRLC = ( DACB.CTRLC & ~DAC_REFSEL_gm) | DAC_REFSEL_AVCC_gc;

    В регистре CTRLA необходимо разрешить канал или каналы, который мы будем использовать, и включить сам ЦАП
DACB.CTRLA = DAC_CH0EN_bm | DAC_ENABLE_bm;
     В регистре CTRLB выбирается режим, в котором будет работать ЦАП, т.е. однолинейный или двухлинейный.
DACB.CTRLB = ( DACB.CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_SINGLE_gc;
     Соответственно регистры в которые заносится значение. Не стоит забывать, что ЦАП 12 разрядный.
DACB.CH0DATAH = 0x0F; 
DACB.CH0DATAL = 0xFF;
     И в завершении не стоит забывать отключать от порта цифровой вывод.
PORTB.PIN3CTRL = PORT_ISC_INPUT_DISABLE_gc;

Пропадает звук в Windows при перезагрузке с Ubuntu

Posted by Reason89 | Posted in



     Держу на своем компьютере по старинке две операционки. Одна Ubuntu другая Windows, не так давно перешел с семерки на десятку.
    При перезагрузке с Ubuntu в Windows пропадает звук в наушниках. Долго рылся в настройках и параметрах, но так ничего и не нашел. Realtek корректно определяет, что наушники подключены, но звука никакого нет. При отключении наушников, встроенные колонки свободно воспроизводят звук. Откат драйверов так же не принес результатов.
    На форумах натыкался на записи, что при перезагрузке, нужно вытаскивать наушники из гнезда, и тогда при загрузке Windows звук появляется. Но у меня такая фишка не прокатила)
    Проблема кстати давольно часто появляется на ноутбуках Asus, Aser, Toshiba и.т.д.
    В итоге решение было найдено. Вроде как драйвер snd_hda_intel в Ubuntu не совсем корректно останавливают звуковую карту, и при загрузке Windows звуковая карта включается некорректно.
    Для того чтобы звук появился необходимо немного иначе перезагружать компьютер, нежели он делает это по умолчанию.
    Для этого, с правами root, редактируем файл /etc/init.d/reboot
    Находим там функцию "do_stop ()", и заменяем строчку "reboot -d -f -i", на последовательность команд:
 sync 
 echo u > /proc/sysrq-trigger 
 sleep 1s 
 echo b > /proc/sysrq-trigger 


    И собственно все, можно наслаждаться любимыми композициями.
    Небольшая подобная проблема есть и при перезагрузке с Ubuntu в Windows. Проблема в том, что при выключении Windows в версиях выше Windows 7, windows не выключается, а переходит в нечто подобное спящему режиму. А жесткие диски в таком режиме не отключаются. В результате чего, доступ к жестким дискам под Ubuntu не возможен. Нужно в настройках электропитания поставить именно выключение компьютера. А не переход в режим гибернации или еще чего.

Прием и передача данных через USART XMEGA

Posted by Reason89 | Posted in , ,


    В передаче данных по USART нет ничего сложного. Сначала вы ждете пока регистр данных опустошится, а предыдущие данные будут отправлены. А потом просто подставляете в регистр передачи данных, новую посылку.
    Флаг DREIF оповещает о наличии данных в регистре передачи данных. Этот бит находится в регистре состояния USART. Более подробно об этом вы можете узнать на странице 278 в руководстве к МК XMEGA.
    Код для отправки одного символа, довольно прост:
void sendChar(char c)
{
    while( !(USARTC0_STATUS & USART_DREIF_bm) ); //Wait until DATA buffer is empty
    USARTC0_DATA = c;
Если нужно отправить строку из символов:
void sendString(char *text)
{
    while(*text)
    {
        sendChar(*text++);
    }
}
В качестве примера:
int main(void)
{
    setUpSerial();
    while(1)
    {
        _delay_ms(3000);
        sendString("Hello World!\n\r");
    }
}
Для чтения данных по USART осуществляется практически так же. Используется тот же регистр данных, только необходимо проверять другой бит в регистре STATUS:
char usart_receiveByte()

{
    while( !(USARTC0_STATUS & USART_RXCIF_bm) ); //Interesting DRIF didn't work.
    return USARTC0_DATA;    
}
    Обычно используют прерывания, для получения данных.
    При передаче данных полезно делать проверку на ошибки:
//Биты Регистра STATUS
//#define USART_FERR_bm            Флаг ошибки в посылке
//#define USART_PERR_bm            Ошибка паритета
//#define USART_BUFOVF_bm        Флаг переполнения буфера
//#define USART_DREIF_bm        Флаг прерывания по опустошению буфера
//USART_RXCIF_bm                Флаг по завершению передачи
char getChar0(void)

{
    char buffer0;    
    while (1)
        {          
        while( !(USARTC0_STATUS & USART_RXCIF_bm) );
        buffer0=USARTC0_DATA;
        if ((USARTC0_STATUS & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm))==0)
        return buffer0;
        };    
}
    Общий код с функцией инициализации и функциями приема передачи, у меня выглядит так:
 // Функция включения USART_C0, cкорость 115200 bod
void StartUsartC0()
{   
    USARTC0_BAUDCTRLB = 0;
    USARTC0_BAUDCTRLA = 0x04;
    USARTC0_CTRLA = 0; //Отключение прерываний
    USARTC0_CTRLC = USART_CHSIZE_8BIT_gc; //8 data bits, no parity and 1 stop bit 
    USARTC0_CTRLB = USART_TXEN_bm | USART_RXEN_bm; //Включение приема передатчика
    PORTC_OUTSET = PIN3_bm; 
    PORTC_DIRSET = PIN3_bm; 
    PORTC_OUTCLR = PIN2_bm;
    PORTC_DIRCLR = PIN2_bm;
}
// Функция получения данных USART_C0.
char getChar0(void)
{
    char buffer0;   
    while (1)
        {         
        while( !(USARTC0_STATUS & USART_RXCIF_bm) );
        buffer0=USARTC0_DATA;
        if ((USARTC0_STATUS & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm))==0)
        return buffer0;
        };   
}
// Фунуция передачи данных USART_C0
void sendChar0(char c0)
{
    while( !(USARTC0_STATUS & USART_DREIF_bm) );
    USARTC0_DATA = c0;
}

Инициализация USART в XMEGA

Posted by Reason89 | Posted in , ,


    Большинство проектов на МК подразумевают связь между ПК и устройством. Либо передача данных между двумя устройствами.
    Вдаваться в то, что такое USART и по какому принципу я не буду, в интернете информации и без того хватает.
    В свою очередь, я приведу примеры работы и настройки модуля USART для МК XMEGA.
    Инициализация USART
    Для начала работы необходимо инициализировать модуль. В качестве примера буду использовать 32 МГц.В Datasheet на странице 284 есть таблица для работы скорости.

    Предположим нам необходима скорость передачи 9600 bod, на мой взгляд самая распространенная. Я стараюсь не использовать умножение скорости передачи(CLK2X = 0), кроме того стараюсь так же не использовать BSCALE. За счет этого расчет скорости упрощается. BSCALE может быть задан от -7 до +7. Его обычно используют, для подстройки скорости, как можно ближе к заданной.
    Слишком тонкая настройка здесь не нужна, во всяком случае как следует из документации. погрешность бывает до 13%.


    Мы в праве использовать для настройки только целые числа, поэтому округляем до 207 и проверяем что из этого вышло.
    Получили довольно близкое значение, с погрешностью меньше 1%. В итоге BSCALE = 0, BSEL = 207. Группа этих битов находится в регистрах BAUDCTRLA и BAUDCTRLB.

USARTC0_BAUDCTRLB = 0;   // BSCALE = 0
USARTC0_BAUDCTRLA = 0xCF;   // BSEL = 207 

    После того как настроили скорость, нужно задать все остальные настройки, такие как размер посылки, стоп бит и бит четности. Это все задается в регистре CTRLC. Я буду использовать в примере 8 бит данных, асинхронный режим с одни стоп битом и без бита четности
USARTC0_CTRLC = USART_CHSIZE_8BIT_gc; 
    Заключительным этапом в настройке является заполнение регистра CTRLB.
    Теперь нас интересуют  два бита RXEN и TXEN, которые включают приемник и передатчик.
// Разрешить принимать и передавать
 USARTC0_CTRLB = USART_TXEN_bm | USART_RXEN_bm;
По умолчанию USART настроен на асинхронную передачу данных, то есть работает в режиме UART.
void setUpSerial()
{
    // Baud rate selection
    // BSEL = (32000000 / (2^0 * 16*9600) -1 = 207.333 -> BSCALE = 0
    // FBAUD = ( (32000000)/(2^0*16(207+1)) = 9615.384 -> it's alright
    USARTC0_BAUDCTRLB = 0; //Just to be sure that BSCALE is 0
    USARTC0_BAUDCTRLA = 0xCF; // 207
    //Disable interrupts, just for safety
    USARTC0_CTRLA = 0;
    //8 data bits, no parity and 1 stop bit 
    USARTC0_CTRLC = USART_CHSIZE_8BIT_gc;
     //Enable receive and transmit
    USARTC0_CTRLB = USART_TXEN_bm | USART_RXEN_bm; // And enable high speed mode
} 
     Отличительной особенностью XMEGA является то, что порты можно переназначать под себя.  По умолчанию TX и RX занимают PC3 и PC2. В более новых версиях Xmega их можно заменить на PC7 и PC6. Более наглядно это представлено в таблице:
     В каждой группе портов есть регистр REMAP, где можно переключаться между альтернативными выводами.

Если в вкратце, то вы просто должны установить бит USART0 в 1, тогда будут использоваться альтернативные выводы.

// Ради примера, я просто переназначил выводы USART с PC3 и PC2 в PC7 и PC6
 PORTC_REMAP | = 0x16;  // Стр 152 в спецификации, перераспределяет USART0
 PORTC_OUTSET = PIN7_bm;  
 PORTC_DIRSET = PIN7_bm;  
 PORTC_OUTCLR = PIN6_bm;
 PORTC_DIRCLR = PIN6_bm;  

Файл Excel для автоматического расчета скорости

XMEGA - таймер/счетчик.

Posted by Reason89 | Posted in , ,


     XMEGA содержит несколько 16-битных таймеров-счетчиков с четырьмя каналами захвата/сравнения в первом и двумя каналами захвата/сравнения во втором, в зависимости отмодификации. Каждый из каналов помимо основного регистра счета, имеет буферный регистр, откуда удобно считывать данные на случай если в процессе считывания значения в основном регистре все время обновляются.
    Таймер-счетчик может быть в двух исполнениях: таймер-счетчик 0, который содержит четыре CCx-канала, и таймер-счетчик 1 с двумя CCx-каналами. Таким образом, упоминаемые далее регистры и биты регистров CCx-каналов 3 и 4 имеются только у таймера-счетчика 0. Блок AWeX доступен только у таймера-счетчика 0, а блок Hi-Res имеется у всех таймеров-счетчиков.
    Таймеры-счетчики в XMEGA содержат полный набор функций, позволяют реализовать генерацию: прямоугольных импульсов, широтно-импульсную модуляцию с одно- и двунаправленным счетом и частотных импульсов. Входной 32-битный захват имеет возможности шумоподавления, захвата частоты и длительности импульсов. Таймеры имеют возможности генерации событий/прерываний по переполнению, по совпадению или захвату и по ошибке. Также имеет возможность использовать поддержку DMA.
    Для осуществления более сложных и специализированных генераторов импульсов предусмотрена возможность совместной работы таймеров-счетчиков с блоками расширения разрешающей способности (Hi-Res) с возможностью увеличения разрешающей способности на 2 бита (в 4 раза) и расширения возможностей генерации импульсов (AWeX) включающих в себя: 4 блока генерации мертвого времени (DT) с отдельными настройками для включения и отключения сигнала, событие контролирующие защиту от повреждения, одноканальная работа с несколькими выходами и образцовый генератор.
    Таймер-счетчик состоит из основного счетчика и нескольких каналов сравнения или захвата (CCx-каналов). Основной счетчик может использоваться для счета импульсов синхронизации или событий. Предусмотрена возможность задания направления и периода счета. CCx-каналы могут использоваться для реализации функций управления по условию совпадения счетчика с заданным значением, для генерации импульсов (частота или ШИМ) или для измерения параметров импульсного сигнала.
    Функции захвата и сравнения нельзя выполнять одновременно, т.е. таймер-счетчик не может одновременно выполнять и генерацию, и захват импульсов. Когда CCx-канал используется для выполнения операций сравнения, его называют каналом сравнения. Если же CCx-канал используется для захвата, то его называют каналом захвата.
     Удобная система прерываний, которые гененрируются  отдельно по каждому каналу в с следующих случаях:  переполнение таймера, ошибка таймер, захват или сравнения.


Не большой пример по работе с таймером счетчиком по переполнению

 
 //Переменные
volatile int timer_blink = 500; // Начальное значение переменной
//Инициализация
void initialization(void)
{ 
 cli(); // Запрещаем прерывания
 //Настройка таймера (TimerC0 в 1Khz)
 TCC0.CTRLA = TC_CLKSEL_DIV1024_gc; // Presacler 1024
 TCC0.CTRLB = 0x00; // Выбираем режим: Normal
 TCC0.PER = 32; // Верхнее значение счетчика 1000 Hz 
 TCC0.CNT = 0x00; // Сбрасываем счетчик
 TCC0.INTCTRLA = 0b00000011; // Даем высокий уровень для прерываний
 //Определение всех 
 PMIC.CTRL |= PMIC_HILVLEN_bm |PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm; 
 sei(); // Активируем прерывания
}
//Прерывания по переполнению  TimerC0 в 1Khz 
ISR(TCC0_OVF_vect) 
{
// По прерыванию уменьшаем значение переменной на 1
 timer_blink--; if (timer_blink <0 br="" timer_blink="0;}">}
int main (void) 
{ 
 initialization();
 while(1) 
 { 
 if (timer_blink <= 0) 
 { 
 // Сюда вставляем инструкцию, которую необходимо выполнить по достижению переменной 0
 timer_blink=500; // Затем вновь возвращаем переменной начальное значение.
 }
 } 
}

    В следующей программе настраиваются три таймер счетчика на захват. Запускаются и останавливаются таймера по внешним прерываниям. При переполнении таймера на определенные выводы для индикации подается короткий сигнал. т.е. вы можете сравнить значения поступающие по разным каналам на микроконтроллер, за один промежуток времени.

 
void StartTimers(void) // Запускаем режим захвата по трем каналам таймера
{
    TCC0.CTRLA = TC_CLKSEL_EVCH0_gc; // Выбираем источником тактов нулевой канал порта
    TCD0.CTRLA = TC_CLKSEL_EVCH1_gc;                
    TCE0.CTRLA = TC_CLKSEL_EVCH2_gc;                    
    TCC0.CTRLB = 0x00;               // Выбрали режим: Normal
    TCD0.CTRLB = 0x00;                                
    TCE0.CTRLB = 0x00;                                
    TCC0.INTCTRLA = 0x01;            // Разрешаем прерывания низкого уровня по переполнению
    TCD0.INTCTRLA = 0x01;            // 0x01 - низкий уровень; 0x02 - средний; 0x03- высокий уровень...
    TCE0.INTCTRLA = 0x01;                            
}
void StopTimers(void)  // Останавливаем работу таймеров
{
    TCC0.CTRLA = TC_CLKSEL_OFF_gc;
    TCD0.CTRLA = TC_CLKSEL_OFF_gc;
    TCE0.CTRLA = TC_CLKSEL_OFF_gc;
}
ISR(PORTD_INT0_vect) // Обработка команды на начало Счета
{
    TCC0.CCA = 0;    // Обнуляем регистры захвата                    
    TCD0.CCA = 0;                                
    TCE0.CCA = 0;        
    StartTimers();   // Запускаем таймеры
}
ISR(PORTE_INT0_vect)  // Обработка команды по Сбросу
{
    StopTimers();     // Останавливаем таймеры
}
ISR(TCC0_OVF_vect)   // Обработка прерывания по переполнению таймера
{
    PORTC.OUTSET =0x04;
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    PORTC.OUTCLR =0x04;    
}
ISR(TCD0_OVF_vect)  // Обработка прерывания по переполнению таймера
{
    PORTC.OUTSET =0x04;
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    PORTC.OUTCLR =0x04;    
}
ISR(TCE0_OVF_vect) // Обработка прерывания по переполнению таймера
{
    PORTC.OUTSET =0x04;
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    asm("NOP");
    PORTC.OUTCLR =0x04;
}
int main(void)
{
    // Настройка работы от внешнего кварца в 4МГц
    cli();                // Отключение прерываний
    OSC.XOSCCTRL = 0x4B;  // 2-9 МГц  без режима сбережения и со стабильной частотой во время запуска (01001011)
    OSC.CTRL = 0x08;      // Разрешение работы внешнего генератора
    while((OSC.STATUS & 0x08) == 0);  // Ожидание сигнала о включении внешнего генератора
    
    // Настройка портов
    PORTC.DIR = 0x1C;     // PC0 - T/C-X; PC2, PC3, PC4 - Для индикации переполнения таймера 
    PORTD.DIR = 0x00;     // PD0 - T/C-Y; PD1 - Прерывание на начало Счета 
    PORTE.DIR = 0x00;     // PE0 - T/C-Y; PE1 - Прерывание на Сброс счетчиков  
    //Разрешаем внешнии прерывания
    // Установка маски прерываний на PIN1
    PORTD.INT0MASK = 0x02;
    PORTE.INT0MASK = 0x02;
    // Установка чувствительности по заднему фронту
    PORTD.PIN1CTRL = PORT_OPC_WIREDORPULL_gc | PORT_ISC_FALLING_gc;
    PORTE.PIN1CTRL = PORT_OPC_WIREDORPULL_gc | PORT_ISC_FALLING_gc;
    PORTD.PIN3CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_FALLING_gc;
    PORTD.PIN5CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_FALLING_gc;
    EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN3_gc;
    EVSYS.CH1MUX = EVSYS_CHMUX_PORTD_PIN4_gc;
    EVSYS.CH2MUX = EVSYS_CHMUX_PORTD_PIN5_gc;
    TCC0.CTRLD = TC_EVACT_CAPT_gc | TC_EVSEL_CH0_gc;
    TCD0.CTRLD = TC_EVACT_CAPT_gc | TC_EVSEL_CH1_gc;
    TCE0.CTRLD = TC_EVACT_CAPT_gc | TC_EVSEL_CH2_gc;
     // Настройка высокого уровня прерываний
    PORTD.INTCTRL = PORT_INT0LVL_HI_gc;
    PORTE.INTCTRL = PORT_INT0LVL_HI_gc;
// Разрешаем все уровни прерываний    
PMIC.CTRL|=PMIC_HILVLEN_bm|PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm;     
    sei(); // Включение прерываний
    while(1)
        {
        // Код программы
        }

    Так же Xmega стандартно позволяет  считать на таймере счетчике количество тактов,  с делителем от 2 до 1024. В данном случае необходимости использовать систему событий не возникает:

void StartTimer()

{
    TCC0.PER = 16384; 
    TCC0.INTCTRLA = 0x03;
    TCC0.CTRLA = TC_CLKSEL_DIV1024_gc;
}


ISR(TCC0_OVF_vect)                                                    
{
    // Подпрограмма обработки данных по переполнению
}
В регистр PER заносится значение до которого будет считать таймер счетчик.
В регистре CTRLA разрешаем работу таймера счетчик с частотой поделенной на 1024.
По достижению 16384, таймер счетчик сгенерирует прерывание высокого уровня и уйдет на его обработку.
 Документация AVR по работе с таймерами.
Материал к документации

Тактирование Xmega и настройка внешнего генератора

Posted by Reason89 | Posted in , ,


    По сравнению с семьей Atmega, в микроконтроллерах Xmega задание частоты тактирования задается не Fuse битами, а программно. На первый взгляд это выглядит немного сложнее, но имеет в целом большое преимущество, так как вы в процессе работы можете изменять частоту тактирования, нет нужды делать отдельную аппаратную  установку и больше не заблокируете контроллер, когда установите Fuse биты не правильно. К тому же появилось такое нововведение, как в случае отсутствия внешнего сигнала тактирования. микроконтроллер по прерыванию перейдет на внутренний генератор с частотой в 2 МГц.
    Появилась возможность использовать внутренние генератор на 32 кГц, 2 МГц или 32MHz, подключать сторожевой таймер и для получения широкого спектра частот ииспользовать блок фазовой подстройки частоты PLL с множителем x1-x31. Кроме того, микроконтроллер может также работать с  внешним кварцем от 0,4МГц  до 16 МГц.
    После выбора тактовой частоты, она может делиться коэффициентом 2, 4, 8, ..., и в итоге быть уменьшена в 2048 раз. Внутренний генератор может быть откалиброван для более высокой точности в процессе работы.
    Но лучше опустить все то, что можно и так найти в документации и показать настройку на примере.
    Начнем с самого простого, настройка работы от внешнего кварца в 4МГц
    
    // 2-9 МГц  без режима сбережения и со стабильной частотой во время запуска   
    OSC.XOSCCTRL = 0x4B;
    // Разрешение работы внешнего генератора                 
    OSC.CTRL = 0x08;
    // Ожидание сигнала о включении внешнего генератора
    while((OSC.STATUS & 0x08) == 0);
    CCP = CCP_IOREG_gc;
    CLK.CTRL = CLK_SCLKSEL_XOSC_gc;  

    Программа настройки основной частоты синхронизации на 3-х кратную частоту внешнего генератора. При частоте генератора 12-16 МГц имеем частоту в 36-48 МГц.
   
   // выбор внешнего генератора с временем запуска 16 тыс. CLK и частотой 12-16 МГц
   OSC.XOSCCTRL = 0xCB;
   // разрешение работы внешнего генератора                
   OSC.CTRL = 0x08;
   // ожидание появления в регистре статуса бита включения синхронизации от внешнего генератора                   
   while((OSC.STATUS & 0x08) == 0 ) ;
   // настройка блока PLL на синхронизацию от внешнего источника и 3-х кратоное умножение
   OSC.PLLCTRL = 0xC3;
   // разрешение работы блока PLL                   
   OSC.CTRL = OSC.CTRL | 0x10;
   // ожидание появления в регистре статуса бита включения блока PLL        
   while((OSC.STATUS & 0x10) == 0 ) ;
   // включение защиты от изменения регистров ввода-вывода на время изменения синхронизации
   CCP = 0xD8;
   // настройка системной синхронизации от блока PLL                        
   CLK.CTRL = 0x04;
   // отключение системной синхронизации от внутреннего RC-генератора частотой 2 МГц
   OSC.CTRL = OSC.CTRL & 0xFE;       

    Как вариант можете прочесть перевод документации AVR1003
    Сам источник и архив к нему
    Он предлагает использовать библиотеку clksys_driver.h для настройки XMEGA
    Ну и соответственно не большой пример:
#include "clksys_driver.h"
... 
void init_clock( void )
{
 //Настройка источников таковых импульсов
 CLKSYS_XOSC_Config(OSC_FRQRANGE_12TO16_gc,false,OSC_XOSCSEL_XTAL_16KCLK_gc);
 // Включаем источник синхронизации
 CLKSYS_Enable( OSC_XOSCEN_bm );
 // Настраиваем PLL для синхронизации
 CLKSYS_PLL_Config( OSC_PLLSRC_XOSC_gc, 2 ); 
 // Включаем ЗДД
 CLKSYS_Enable( OSC_PLLEN_bm );
 // Настраиваем делитель частоты
 CLKSYS_Prescalers_Config( CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc );
 // Ждем включения 
 do {} while ( CLKSYS_IsReady( OSC_PLLRDY_bm ) == 0 );
 // Выбираем новый источник для синхронизации
 CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_PLL_gc );
 // Выключаем внутренний генератор
 CLKSYS_Disable( OSC_XOSCEN_bm );
} 

XMEGA - Первые шаги.

Posted by Reason89 | Posted in , ,

    Начинаю цикл статей, как небольшой мануал по AVR микроконтроллерам семейства XMEGA фирмы Atmel в языке программирования "C".
    Структура этого семейства контроллеров структурирована иначе, нежели ATmega. Тем не менее, общий принцип работы с ними понять на много легче, если есть хоть какие то познания в AVR, к тому же все порты и функции структурированы примерно однотипно. Плюс ко всему, имена регистров для базовых функций, таких как таймеры, АЦП, прерывания и.т.д. для каждого порта аналогичны и отличаются только конкретным обозначением для каждого порта. Atmel повсеместно устраняет хаос в названиях регистров, что наглядно видно в новых Xmega. Имена регистров на каждом микроконтроллере XMEGA одни и те же.
    Xmega имеет иной принцип программирования, нежели предыдущие семейства. Например в Atmega и в семействе Attiny используется интерфейс ISP, так называемый "интерфейс программирования и отладки", в Xmega используется двухпроводной PDI. Этот интерфейс требует всего две линии (PDI_CLK и PDI_Data). Поскольку интерфейс все еще является относительно новым, он поддерживается далеко не каждым программатором. Таким образом, перед изучением вы должны убедиться, что ваш программатор поддерживает PDI.
     Я использую программатор от Atmel "Atmel AVR ISP MKII".



    При программировании Xmega, Я рекомендую использовать последнюю версию AVR Studio. Начиная с 5 версии, используется оболочка на основе Microsoft Visual Studio, которая предлагает очень высокий уровень комфорта для каждого программиста. В качестве микроконтроллера в примерах я использую XMega128A3. Но, т.к. основные функции и регистры Xmega совместимы друг с другом, программы должны работать на всех других контроллерах всего семейства без особых изменений.
    В какой то мере можно даже сказать, что серия Xmega практически не имеет общего с предыдущими микроконтроллерами ATmega AVR.  В связи с этим не возможно использовать программы, написанные для ATmega, в микроконтроллерах Xmega без существенных изменений. Это также относится и к самым простым программ на вроде мигания светодиодом.   
    Не смотря на то, что на первый взгляд это очень сложно, изучение этого семейства после более близкого знакомства принесет вам много пользы и удовольствия.
    Много интересной информации для начала можно черпать из перевода официальной документации на это семейство. Выкладывать сюда все эти таблицы с именами регистров и архитектурой не вижу смысла.
    И обратите внимание на цикл статей в журнале Компоненты и технологии за 2008 год, начиная с 3 номера.