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 номера.