Что такое ШИМ и как он работает особо подробно расписывать не буду, информацию без труда найдёте на просторах интернета. Коснусь лишь общих понятий. ШИМ - это Широтно-Импульсная Модуляция, (по-английски PWM - Pulse Width Modulation) уже из самого названия ясно, что здесь что-то связанное с импульсами и их шириной. Если изменять ширину (длительность) импульсов постоянной частоты, то можно управлять, например, яркостью источника света, скоростью вращения вала электродвигателя или температурой какого-либо нагревательного элемента. Обычно, именно с помощью ШИМ микроконтроллер управляет подобной нагрузкой. Микроконтроллеры имеют аппаратную реализацию ШИМ, но, к сожалению, количество аппаратных ШИМ-каналов ограничено, например, в AТmega88 их аж шесть штук, в ATtiny2313 - четыре, в ATmega8 - три, а в ATtiny13 только два. В AVR ШИМ-каналы используют таймеры и их регистры сравнения OCRxx. Изменяя их содержимое и задавая параметры таймеров, в зависимости от задач, можно управлять состоянием, связанного с регистром, выхода - подавать на него 1 либо 0. То же самое можно организовать программно, управляя любым выводом контроллера, а главное, реализовать большее количество ШИМ-каналов, чем имеется на борту аппаратных. Практически, количество каналов ограничено лишь количеством ножек-выводов микроконтроллера (по крайней мере, если говорить о семействах Mega или Tiny). Как оказалось, алгоритм довольно прост, но у меня ушло некоторое время на его понимание и полное осознание.

Данный алгоритм подробно изложен в оригинальном Appnote AVR136: Low-Jitter Multi-Channel Software PWM. Принцип работы программной реализации заключается в имитации работы таймера в режиме ШИМ. Требуемая длительность импульсов задаётся переменными, соответственно, по одной на каждый канал (в моём коде lev_ch1, lev_ch2, lev_ch3), а так же задаются «близнецы» этих переменных, которые хранят значение для конкретного периода работы таймера (в моём коде buf_lev_ch1, buf_lev_ch2, buf_lev_ch3). Восьмибитный таймер запускается на основной частоте МК и генерирует прерывание по переполнению, то есть, каждые 256 тактов. Это накладывает ограничение на длительность процедуры обработки прерывания - необходимо уложиться в 256 тактов, чтобы не пропустить следующее прерывание. В результате, один полный период ШИМ равняется 256*256=65536-и тактам. Восьмибитная переменная-счетчик (в моём примере counter) увеличивается на единицу каждое прерывание и действует, как указатель позиции внутри цикла ШИМ. Всё это обеспечивает разрешение (минимальный шаг) ШИМ в 1/256, а частоту импульсов в ƒ/(256*256), где ƒ-частота задающего генератора микроконтроллера. Следует заметить, что тактовая частота микроконтроллера должна быть довольно высокой. В моём примере ATtiny13 работает на максимально возможной частоте, без применения внешнего генератора - 9,6МГц. Это даёт период ШИМ в 9600000/65536≈146,5Гц чего вполне достаточно в большинстве случаев.
Код на C, пример реализации идеи для МК ATtiny13 (три канала ШИМ на выводах PB0, PB1, PB2):

#define F_CPU 9600000 //fuse LOW=0x7a #include #include uint8_t counter=0; uint8_t lev_ch1, lev_ch2, lev_ch3; uint8_t buf_lev_ch1, buf_lev_ch2, buf_lev_ch3; void delay_ms(uint8_t ms) //функция задержки { while (ms) { _delay_ms(1); ms--; } } int main(void) { DDRB=0b00000111; // установка PortB пины 0,1,2 выходы TIMSK0 = 0b00000010; // включить прерывание по переполнению таймера TCCR0B = 0b00000001; // настройка таймера, делитель выкл sei(); // разрешить прерывания lev_ch1=0; //начальные значения lev_ch2=64; //длительности ШИМ lev_ch3=128; //трёх каналов while (1) //бесконечная шарманка { for (uint8_t i=0;i<255;i++) { lev_ch1++; //увеличиваем значения lev_ch2++; //длительности ШИМ lev_ch3++; //каждого канала delay_ms(50); //пауза 50мс } } } ISR (TIM0_OVF_vect) //обработка прерывания по переполнению таймера { if (++counter==0) //счетчик перехода таймера через ноль { buf_lev_ch1=lev_ch1; //значения длительности ШИМ buf_lev_ch2=lev_ch2; buf_lev_ch3=lev_ch3; PORTB |=(1<Думаю, всё достаточно наглядно и пояснения излишни. Для значений длительности и их буферов, при большем числе каналов, возможно, будет лучше использовать массивы, но в данном примере, я этого делать не стал, ради большей наглядности.
Проверено на avr-gcc-4.7.1 и avr-libc-1.8.0. Компиляция и получение файла прошивки:
avr-gcc -mmcu=attiny13 -Wall -Wstrict-prototypes -Os -mcall-prologues -std=c99 -o softPWM.obj softPWM.c
avr-objcopy -O ihex softPWM.obj softPWM.hex
Для правильной работы нужно выставить младшие fuse-биты в 0x7a (частота 9,6МГц). в avrdude это, например, делается так:
avrdude -p t13 -c usbasp -U lfuse:w:0x7a:m

Мой вариант реализации на ассемблере. Программа делает абсолютно то же самое, что и предыдущий код на C.
;чтобы не тянуть include-файл.list .equ DDRB= 0x17 .equ PORTB= 0x18 .equ RAMEND= 0x009f .equ SPL= 0x3d .equ TCCR0B= 0x33 .equ TIMSK0= 0x39 .equ SREG= 0x3f ;это лишь демонстрация, потому регистров и не жалеем.def temp=R16 .def lev_ch1=R17 .def lev_ch2=R18 .def lev_ch3=R19 .def buf_lev_ch1=R13 .def buf_lev_ch2=R14 .def buf_lev_ch3=R15 .def counter=R20 .def delay0=R21 .def delay1=R22 .def delay2=R23 .cseg .org 0 ;таблица прерываний из даташита: rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp PIN_CHG_IRQ ; PCINT0 Handler rjmp TIM0_OVF ; Timer0 Overflow Handler rjmp EE_RDY ; EEPROM Ready Handler rjmp ANA_COMP ; Analog Comparator Handler rjmp TIM0_COMPA ; Timer0 CompareA Handler rjmp TIM0_COMPB ; Timer0 CompareB Handler rjmp WATCHDOG ; Watchdog Interrupt Handler rjmp ADC_IRQ ; ADC Conversion Handler ;RESET: EXT_INT0: PIN_CHG_IRQ: ;TIM0_OVF: EE_RDY: ANA_COMP: TIM0_COMPA: TIM0_COMPB: WATCHDOG: ADC_IRQ: reti RESET: ldi temp,0b00000111 ; назначаем PortB пины PB0, PB1 out DDRB,temp ; и PB2 выходами ldi temp,0 ; выставляем все выводы out PORTB,temp ; PortB в 0 ldi temp,low(RAMEND) ; инициализация out SPL,temp ; стека ldi temp,0b00000001 ; вкл. таймер out TCCR0B,temp ; без делителя ldi temp,0b00000010 ; вкл. прерывание out TIMSK0,temp ; таймера по переполнению sei ; разрешить прерывания start_pwm: ; бесконечная шарманка inc lev_ch1 ; увеличиваем значения inc lev_ch2 ; длительности ШИМ inc lev_ch3 ; по всем каналам rcall delay ; небольшая пауза для плавности rjmp start_pwm delay: ; процедура задержки ldi delay2,$01 ; выставляем число ldi delay1,$77 ; до скольки считать ldi delay0,$00 ; $017700 - даст задержку в 50мс loop: subi delay0,1 ; считаем sbci delay1,0 ; считаем sbci delay2,0 ; считаем brcc loop ret TIM0_OVF: ; обработка прерывания таймера push temp ; на всякий пожарный сохраняем in temp,SREG ; temp и SREG в стеке push temp inc counter ; счетчик перехода таймера через 0 cpi counter,0 ; если не 0, то проверяем brne ch1_off ; не надо ли чего погасить mov buf_lev_ch1,lev_ch1 ; если счетчик 0 mov buf_lev_ch2,lev_ch2 ; то задаем новые mov buf_lev_ch3,lev_ch3 ; значения длительности ШИМ каналов ldi temp,0b00000111 ; включить все out PORTB,temp ; три выхода ch1_off: ; а не погасить ли нам cp counter,buf_lev_ch1 ; первый канал? brne ch2_off ; нет, рано - проверяем второй cbi PORTB,0 ; да погасить ch2_off: ; а не погасить ли нам cp counter,buf_lev_ch2 ; второй канал? brne ch3_off ; нет, рано - проверяем третий cbi PORTB,1 ; да погасить ch3_off: ; а не погасить ли нам cp counter,buf_lev_ch3 ; третий канал? brne irq_end ; нет, рано - двигаемся к выходу из прерывания cbi PORTB,2 ; да, погасить irq_end: ; достаем из стека pop temp ; SREG и temp out SREG,temp pop temp reti ;выходим из прерывания
Компилируется с помощью avra или tavrasm. Не забыть про fuse-биты (см. выше).

Урок 13

Часть 1

ШИМ. Мигаем светодиодом плавно

Сегодня мы изучим возможность использования широтно-импульсной модуляции в микроконтроллере AVR , или, как говорят в народе, ШИМ .

В технической документации мы будем видеть чаще аббревиатуру PWM или pulse-width modulation , что преводится имено также.

Что же такое вообще широтно-импульсная модуляция.

ШИМ — это управление свечением светодиодов, вращением двигателей, и прочими устройствами необычным способом, при котором данное управление осуществляется не приложенным напряжением к контактам, а квадратными импульсами. При этом напряжение будет только двух видов — высокое (1 ) и низкое (0 ). При данном способе результирующее напряжение вычисляется как среднее по времени между временем высокого состояния в одном импульсе и временем низкого состояния. Мы вычисляем отношение времени (или широты) высокого состояния к общему периоду импульса. Называем мы это скважностью импульса. То есть чем больше в периоде напряжение находилось в высоком состоянии, тем больше скважность, а, следовательно, тем больше и результирующее среднее напряжение. То есть, чтобы найти результирующее напряжение, нам необходимо и достаточно вычисленную скважность умножить на напряжение и разделить на 100, так как скважность как правило измеряется в процентах. Например, если у нас в квадратном импульсе широта логического нуля равна широте логической единицы, то скважность у нас будет 50 процентов, и, если напряжение будет 5 вольт, то среднее результирующее напряжение мы получим равное 2,5 вольт и т.д. Лучшую картину объяснения данной ситуации мы можем увидеть, посмотрев видеоурок, ссылка на который дана в конце данной статьи.

Это конечно очень упрощённое понятие ШИМ. Есть более серьёзные разъяснение данной технологии, но нам для наших экспериментов этого будет вполне достаточно.

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

Но всё-таки самое интересное, как же всё-таки всё это организовано в нашем микроконтроллере?

В микроконтроллере AVR широтно-импульсную модуляцию можно организовать как программно , так и аппаратно .

Программная организация ШИМ — это когда мы включим на определённое время на ножке контроллера логическую единицу, а затем на определённое время — логический ноль и так по циклу. Плюсы данного способа — это то, что мы можем организовать ШИМ на абсолютно любой ножке любого порта контроллера, а минусом — то, что всё это будет связано с немалыми затратами на процессорное время, и возможно даже будет сопряжено с какими-то ошибками, вытекающими из этого.

Поэтому всегда в любой технологии мы стараемся придерживаться всё-таки именно аппаратного способа реализации.

Аппаратная организация ШИМ в МК AVR происходит на уровне таймера 2 .

Как мы помним из предыдущего занятия, таймеров в конкретном нашем микроконтроллере Atmega8 три. И таймеры 0 и 2 являются восьмибитными. Но на уровне 2-го таймера как раз и организована широтно-импульсная модуляция.

Давайте посмотрим вот такую вот картину

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

Биты WGM как раз и включают таймер в резим PWM (ШИМ). С остальными битами разберёмся позже, если это потребуется.

А сейчас, чтобы это всё прочувствовать, нам необходим проект, который мы и создадим сейчас.

Проект создаем таким же образом, как и на прошлых занятиях. Назовём его Test10, создадим и добавим файлы PWM.c и PWM.h, последний автоматически оформится при создании соответствующими директивами прероцессора. А в главном файле Test01.c мы напишем стандартный код

#include «main.h»

//—————————————-

void port_ini ( void )

PORTB =0x00;

DDRB =0x08;

//—————————————-

int main ( void )

Port_ini ();

While (1)

Как мы видим в коде, в порте B мы включили на выход 3 ножку. В распиновке контроллера мы видим, что у данной ножки есть ещё альтернативное обозначение OC2 , которое и означает возможность этой ножки работать непосредственно с ШИМ

Файл main.h мы можем даже подключить с прошлого занятия по LCD и немного исправим код, подключив туда уже новый наш модуль PWM

#ifndef MAIN_H_

#define MAIN_H_

#define F_CPU 8000000UL

#include

#include

#include

#include

#include

#include «PWM.h»

#endif /* MAIN_H_ */

Ну и теперь начнём писать код в новую библиотеку для работы непосредственно с ШИМ — в файл PWM.c.

Там мы также подключим заголовочный файл и создадим каркас функции для работы с ШИМ

#include «PWM.h»

void init_PWM_timer ( void )

{

}

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

#ifndef PWM_H_

#define PWM_H_

#include

void init_PWM_timer ( void );

#endif /* PWM_H_ */

И вызовем сразу эту функцию в main()

port_ini ();

init_PWM_timer ();

Начнём теперь писать код непосредственно в функцию. Сначала запишем все нули в регистр ASSR , который существует у таймера, но мы его никак не используем

void init_PWM_timer ( void )

ASSR =0x00;

Дальше уже займёмся управляющим регистром нашего таймера. Назначение битов WGM и то, какие именно из них мы включим, мы разобрали. Теперь биты COM20 и COM21, отвечающие за режим самого ШИМ. Мы выберем вот такой режим и соответственно ему и включим данные биты

А включим мы режим, при котором при начале счёта напряжение на ножке OC2 будет находиться в высоком логическом состоянии, а как только мы досчитаем до определённой цифры, то ножка перейдёт в низкое состояние и будет в нём находиться до окончания счёта. И так по кругу.

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

Поэтому мы включим все три бита.

В итоге значение регистра станет вот таким

ASSR =0x00;

TCCR2 =0b01101110; //Fast PWM, Clear OC2 on Compare Match, clkT2S/256 (From prescaler) (реальная частота получится 8мгц/256 = 31250 гц)

Теперь попробуем собрать проект. Проект нормально собрался. В следующей части мы продолжим работать с ШИМ.

Смотреть ВИДЕОУРОК (нажмите на картинку)

Post Views: 9 484

ШИМ (PWM) — широтно-импульсная модуляция. Не нужно пугаться данного термина. Это всего навсего способ регулирования напряжения. Допустим подсветка монитора горит слишком ярко, вы меняете яркость. А что же происходит в этот момент на самом деле?

Представим себе, что подсветка монитора это несколько светодиодов. Питается все это дело от постоянного напряжения. Но вот нам понадобилось уменьшить яркость монитора. Логично ответить, что это можно сделать переменным резистором. На маленьких токах — возможно. Но на больших, резистор будет сильно греться. Сильно возрастут габариты, потери, энергопотребление.

Поэтому люди придумали схему на транзисторах, которая делает из постоянного напряжения пульсирующее. Оказывается, пульсирующее напряжение, в зависимости от заполнения периода будет эквивалентно постоянному напряжению. Т.е. если в течение периода напряжение 50% времени было включено, 50% выключено, то эквивалент постоянного напряжения будет равен 50% от номинального.

В цифрах это просто — было 5В постоянного напряжения прогнали через ШИМ — получили 2,5В. Если заполнение импульса равно 75%, то эквивалентное постоянное напряжение будет 3,75В. Думаю идея понятна.

Теперь приступим к практической реализации. Будем при помощи микроконтроллера изменять заполнение от 0 до 100%, потом от 100% до нуля. Конечный результат должен выглядеть так:

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

Запускаем наш любимый CodeVision. Создаем проект при помощи мастера. В разделе таймеров (Timers), выбираем Timer 2 и выставляем настройки как на рисунке.

Если попробовать сгенерировать проект, то прога может ругнуться. Соглашаемся, ведь у нас нога 3 порта В должна быть настроена как выход.

Приводим код к следующему виду:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include void main(void ) { PORTB= 0x00 ; DDRB= 0x08 ; // Timer/Counter 2 initialization ASSR= 0x00 ; TCCR2= 0x6C ; TCNT2= 0x00 ; OCR2= 0x00 ; TIMSK= 0x00 ; while (1 ) { } ; }

#include void main(void) { PORTB=0x00; DDRB=0x08; // Timer/Counter 2 initialization ASSR=0x00; TCCR2=0x6C; TCNT2=0x00; OCR2=0x00; TIMSK=0x00; while (1) { }; }

Уделим внимание строке OCR2=0x00; Эта переменная как раз и отвечает за величину заполнения импульса. Изменяется данная величина от 0 до 255(0хFF), т.е. 255 соответствует 100% -му заполнению (постоянный ток). Следовательно, если нужно 30% заполнение (255/100)*30=77. Далее 77 переводим в шестнадцатеричную систему OCR2=0x4D;

TCCR2=0x6C; Изменяя данную величину мы можем регулировать частоту ШИМ. Величина частоты работы ШИМ кратна частоте, на которой работает микроконтроллер. В проекте использована частота микроконтроллера 8 МГц, частоту ШИМ использовали 125кГц, следовательно делитель равен 8/125=64
0x6C в двоичной системе счисления 1101100, открываем даташит на Atmega8 и видим описание регистра TCCR2, так вот 1101100 последние цифры 100 и отвечают за выбор частоты работы ШИМ

Приступим непосредственно к программе:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include #include void main(void ) { PORTB= 0x00 ; DDRB= 0x08 ; ASSR= 0x00 ; TCCR2= 0x6C ; TCNT2= 0x00 ; OCR2= 0x00 ; TIMSK= 0x00 ; while (1 ) { while (OCR2< 0xff ) { OCR2= OCR2+ 0x01 ; delay_ms(5 ) ; } while (OCR2> 0x00 ) { OCR2= OCR2- 0x01 ; delay_ms(5 ) ; } } ; }

#include #include void main(void) { PORTB=0x00; DDRB=0x08; ASSR=0x00; TCCR2=0x6C; TCNT2=0x00; OCR2=0x00; TIMSK=0x00; while (1) { while(OCR2<0xff) { OCR2=OCR2+0x01; delay_ms(5); } while(OCR2>0x00) { OCR2=OCR2-0x01; delay_ms(5); } }; }

Код прост до безобразия: сначала в цикле увеличиваем заполнение от 0 до 255(ff), потом уменьшаем от 255 до 0.
И напоследок видосик, как это все должно работать. Успехов в изучении)

Например);

  • резистор номиналом 190…240 Ом (вот отличный набор резисторов самых распространённых номиналов);
  • персональный компьютер со средой разработки Arduino IDE.
  • Инструкция по использованию ШИМ в Arduino

    1 Общие сведения о широтно-импульсной модуляции

    Цифровые выводы Arduino могут выдавать только два значения: логический 0 (LOW, низкий уровень) и логическую 1 (HIGH, высокий). На то они и цифровые. Но есть у Ардуино «особые» выводы, которые обозначаются PWM . Их иногда обозначают волнистой чертой "~" или обводят кружочками или ещё как-то выделяют среди прочих. PWM расшифровывается как Pulse-width modulation или широтно-импульсная модуляция , ШИМ .

    Широтно-импульсно модулированный сигнал - это импульсный сигнал постоянной частоты, но переменной скважности (соотношение длительности импульса и периода его следования). Из-за того, что большинство физических процессов в природе имеют инерцию, то резкие перепады напряжения от 1 к 0 будут сглаживаться, принимая некоторое среднее значение. С помощью задания скважности можно менять среднее напряжение на выходе ШИМ.

    Если скважность равняется 100%, то всё время на цифровом выходе Arduino будет напряжение логическая "1" или 5 вольт. Если задать скважность 50%, то половину времени на выходе будет логическая "1", а половину - логический "0", и среднее напряжение будет равняться 2,5 вольтам. Ну и так далее.


    В программе скважность задаётся не в процентах, а числом от 0 до 255. Например, команда analogWrite(10, 64) скажет микроконтроллеру подать на цифровой PWM выход №10 сигнал со скважностью 25%.

    Выводы Arduino с функцией широтно-импульсной модуляции работают на частоте около 500 Гц. Значит, период следования импульсов - около 2 миллисекунд, что и отмеряют зелёные вертикальные штрихи на рисунке.

    Получается, что мы можем сымитировать аналоговый сигнал на цифровом выходе! Интересно, правда?!

    Как же мы можем использовать ШИМ? Применений масса! Например, управлять яркостью светодиода, скоростью вращения двигателя, током транзистора, звуком из пьезоизлучателя и т.д.…

    2 Схема для демонстрации широтно-импульсной модуляции в Arduino

    Давайте рассмотрим самый базовый пример - управление яркостью светодиода с помощью ШИМ. Соберём классическую схему.


    3 Пример скетча с ШИМ

    Откроем из примеров скетч "Fade": Файл Образцы 01.Basics Fade .


    Немного изменим его и загрузим в память Arduino.

    Int ledPin = 3; // объявляем пин, управляющий светодиодом int brightness = 0; // переменная для задания яркости int fadeAmount = 5; // шаг изменения яркости void setup() { pinMode(ledPin, OUTPUT); } void loop() { analogWrite(ledPin, brightness); // устанавливаем яркость brightness на выводе ledPin brightness += fadeAmount; // изменяем значение яркости /* при достижении границ 0 или 255 меняем направление изменения яркости */ if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount; // изменяем знак шага } delay(30); // задержка для большей видимости эффекта }

    4 Управление яркостью светодиода с помощью PWM и Arduino

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


    Посмотрите приложенные видео, где наглядно показано изменение яркости светодиода, на подключённом осциллографе видно, как при этом меняется сигнал с Arduino.

    После того, как мы поморгали светодиодами, разобрались с тактированием и источниками прерываний, пришло время освоить таймер контроллера MSP430G2553.
    Таймер – это счетный механизм, привязанный к импульсам сигнала тактового генератора. Таймер Timer_A , является 16-ти битным таймером. Это означает, что он считает от нуля до двоичного 0b1111111111111111, или шестнадцатеричного 0xFFFF, или десятеричного 65535. Таймер является периферийным устройством и так же имеет собственные прерывания.
    Таймер Timer_A имеет три режима работы.

    Режим непрерывного счёта, в нём таймер просто считает от 0 до 0xFFFF, потом начинает с начала, и так до бесконечности.

    Режим прямого счёта, в нём таймер работает так же, считает до верхней границы, и начинает опять с 0. Только в этом режиме вы можете выбирать, верхнюю границу, до которой будет считать таймер.

    Режим реверсивного счёта, похож на режим прямого счёта тем, что в нём вы можете выбрать максимальное значение таймера. Отличие его в том, что достигнув максимума, таймер начинает считать вниз, потом, достигнув 0, опять вверх и т.д.
    На графиках есть обозначение TACCR0 – это регистр захвата сравнения. Их может быть два (TACCR0 и TACCR1), может быть три (TACCR0, TACCR1 и TACCR2) в зависимости от модели. В этот регистр можно записывать так называемые ключевые точки, по достижению которых таймер будет вызывать прерывание.

    Ниже подробно описаны регистры Таймера А
    TACTL – Timer_A Control Register, Регистр управления Таймера А. Используется для связи таймера с тактовыми сигналами и выбора режимов работы.
    TASSELx , биты 8 и 9, указывают таймеру, какой из тактовых сигналов использовать.
    IDx , биты 6 и 7, указывают, какой делитель частоты тактового сигнала использовать, 2, 4 или 8. Делится частота, полученная уже после применения делителя в самом генераторе тактового сигнала.
    MCx , биты 4 и 5, указывают на режим работы таймера. Если они равны 0 (стоит по умолчанию) таймер полностью остановлен.
    TACLR , бит 2. Если в него вписать 1, это приведет к сбросу таймера. Микроконтроллер автоматически вписывает в этот бит 0, после перезапуска таймера.
    TAIE и TAIFG , биты 0 и 1, соответственно. Контролируют прерывание таймера, об этом чуть ниже.
    TAR – Timer_A Register, Регистр счётчика Таймера А, в нём содержится текущее значение таймера.
    TACCRx – Timer_A Capture/Compare Registers, Регистры захвата/сравнения Таймера А. Их может быть два (TACCR0 и TACCR1), может быть три (TACCR0, TACCR1 и TACCR2) в зависимости от модели микроконтроллера. В режиме сравнения, мы вписываем в них значения, по достижении которых, таймер должен подать нам сигнал. TACCR0 зачастую используется для указания верхней границы счёта. В режиме захвата, процессор записывает в них текущее значение TAR, по сигналу на входе.
    TACCTLx – Timer_A Capture/Compare Control Registers, Регистр управления блоком захвата/сравнения Таймера А. От его значения, зависят режимы работы регистров захвата/сравнения.
    CMx , биты 14 и 15, определяют тип сигнала, по которому происходит захват. (По нарастающему, спадающему, обоим фронтам. – Прим. пер.)
    CCISx , биты 12 и 13, выбирают, откуда берется сигнал захвата.
    SCS и SCCI, биты 11 и 10 соответственно, синхронизация сигнала захвата с тактовым сигналом таймера. Обычно, таймер работает независимо, асинхронно, от внешних сигналов. (Я сам до конца не разобрался в этой функции, напишу подробней в следующих уроках)
    CAP , бит 8, выбор режима работы, 1 – режим захвата, 0 – режим сравнения.
    OUTMODx , биты 5-7, выбор режима работы модуля вывода, т.е. тип реакции на событие захвата или сравнения.
    CCIE и CCIFG , биты 4 и 0 соответственно, обработка прерываний по захвату/сравнению.
    CCI и OUT , биты 3 и 2 соответственно, вход и выход захвата/сравнения.
    COV , бит 1, переполнение захвата. Устанавливается в 1, если произошел второй захват, а первый еще не был считан. Должен сбрасываться программно.
    TAIV – Taimer_A Interrupt Vector Register, регистр вектора прерывания Таймера А. Так как прерывание таймера, может быть вызвано по нескольким разным причинам, содержимое этого регистра, указывает на причину вызова прерывания.
    TAIVx , биты 1-3, содержат тип случившегося прерывания, что позволяет нам, совершить разные действия для обработки разных причин прерываний.

    Итак, если вам необходимо просто поморгать светодиодом, или переключать реле, или тому подобное, через определённые промежутки времени, то сконфигурировать таймер в таком случае очень просто.

    TACCR0 = 62499; // период 62,500 циклов TACCTL0 = CCIE; // Разрешаем прерывание таймера по достижению значения CCR0. TACTL = TASSEL_2 + ID_3 + MC_1 + TACLR; //Настройка режима работы Timer_A: // TASSEL_2 - источник так тов SMCLK (SubMainCLocK), // по умолчанию настроенны х на работу от DCO // ID_3 - делитель частоты на 8, от 1MHz это будет 125kHz // MC_1 - режим прямого счёта (до TACCR0) // TACLR - начальное обнуление таймера

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

    Чтобы было понятнее, на рисунке ниже приведены сигналы ШИМ для коэффициента заполнения: а – 25%; б – 50 %; в – 75 %; г – 100 %.

    t0– время импульса;
    T – период;
    Изменяя t0, можно получать различные средние значения напряжения на нагрузке, таким образом, изменяя яркость свечения светодиода.
    Период широтно-импульсной модуляции должен быть постоянным и больше чем время импульса. Поэтому запишем TACCR0 = 600; // период 600 циклов . Это число я подобрал экспериментальным путём, чтобы работу таймера было видно невооруженным глазом.
    Так как светодиод должен медленно начать светиться, то начальное время импульса должно быть очень маленьким например TACCR1 = 10; // время импульса 10 циклов . TACCR1 – это второй регистр захвата\сравнения Таймера А. Итак, получается, что при достижении значения TACCR0 = 600 таймер будет генерировать прерывание, в котором мы будем устанавливать выход микроконтроллера в единицу, что будет началом импульса. При это таймер обнуляется и начинает считать заново с нуля пока не дойдёт до TACCR1 = 10, тогда будет сгенерировано прерывание по второму регистру захвата\сравнения, в котором мы установим выход микроконтроллера в ноль. Таким образом длительность импульса t0 будет длиться 5 тактов.
    Для того, чтобы светодиод увеличивал интенсивность свечения, необходимо увеличивать время импульса. Для этого после каждого прерывания от TACCR0 будем прибавлять к текущему значению TACCR1 какое-то значение. При этом максимально значения, которое может иметь TACCR1 будет равно периоду, то есть 600. В зависимости о значения, которое мы будем прибавлять, будет зависеть плавность и скорость изменения интенсивности свечения.

    TACCR0 = 600; // Период TACCR1 = 5; //Время импульса TACCTL0 = CCIE; // Разрешаем прерывание таймера по достижению CCR0. TACCTL1 = CCIE; // Разрешаем прерывание таймера по достижению CCR1. TACTL = TASSEL_2 + ID_3 + MC_1 + TACLR; //Настройка режима работы Timer_A

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

    #pragma vector = TIMER0_A0_VECTOR __interrupt void CCR0_ISR(void) //вектор прерывания для TACCR0, Таймера А { P1OUT |= BIT0; // устанавливаем нулевой бит в еденицу if (TACCR1><=5) // если TACCR1 меньше минимального значения, то увеличиваем TACCR1 { indeX=2; } if (indeX == 1) { --TACCR1; // уменьшаем TACCR1 на 1 } if ((indeX == 2) || (indeX == 0)) { ++TACCR1; // увеличиваем TACCR1 на 1 } }

    Прерывание для TACCR1 будет выглядеть следующим образом.

    #pragma vector=TIMER0_A1_VECTOR __interrupt void CCR1_ISR(void) //вектор прерывания для TACCR1, Таймера А { P1OUT &= ~BIT0; // устанавливаем нулевой бит порта Р1 в ноль. TAIV &= ~TA0IV_TACCR1; //сбрасываем флаг прерывания. }

    Обратите внимание, что в первом случае, в обработчике прерывания от TACCR0 я не сбрасывал флаг прерывания, а для TACCR1 сделал это. Все потому, что флаг прерывания для TACCR0 сбрасывается автоматически при вызове такого прерывания, а для TACCR1 его нужно сбрасывать вручную.

    Теперь можно составить полный текст программы.

    #include /* Глобальные переменные */ unsigned int indeX = 0; /* Объявление функций */ void main(void) { WDTCTL = WDTPW + WDTHOLD; // отключаем сторожевой таймер P1DIR |= 0x01;// настраиваем нулевой бит порта Р1 на выход P1OUT = 0x01; // устанавливаем нулевой бит порта Р1 в единицу BCSCTL1 = CALBC1_1MHZ; // Устанавливаем частоту DCO DCOCTL = CALDCO_1MHZ; TACCR0 = 600; // Период TACCR1 = 10; // Время импульса TACCTL0 = CCIE; // Разрешаем прерывание таймера по достижению CCR0 TACCTL1 = CCIE; // Разрешаем прерывание таймера по достижению CCR1 TACTL = TASSEL_2 + ID_3 + MC_1 + TACLR; //Настройка режима работы Timer_A: // TASSEL_2 - источник так тов SMCLK (SubMainCLocK), // по умолчанию настроенны х на работу от DCO // ID_3 - делитель частоты на 8, от 1MHz это будет 125kHz // MC_1 - режим прямого счёта (до TACCR0) // TACLR - начальное обнуление таймера _enable_interrupt(); // разрешаем все прерывания } #pragma vector = TIMER0_A0_VECTOR __interrupt void CCR0_ISR(void) //вектор прерывания для TACCR0, Таймера А { P1OUT |= BIT0; // устанавливаем нулевой бит в еденицу if (TACCR1>=(TACCR0-5)) // если TACCR1 больше TACCR0, то уменьшаем TACCR1 { indeX=1; } if (TACCR1<=5) // если TACCR1 меньше минимального значения, то увеличиваем TACCR1 { indeX=2; } if (indeX == 1) { --TACCR1; // уменьшаем TACCR1 на 1 } if ((indeX == 2) || (indeX == 0)) { ++TACCR1; // увеличиваем TACCR1 на 1 } } #pragma vector=TIMER0_A1_VECTOR __interrupt void CCR1_ISR(void) //вектор прерывания для TACCR1, Таймера А { P1OUT &= ~BIT0; // устанавливаем нулевой бит порта Р1 в ноль. TAIV &= ~TA0IV_TACCR1; //сбрасываем флаг прерывания. }

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



    Эта статья также доступна на следующих языках: Тайский

    • Next

      Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

      • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

        • Next

          В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

    • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
      https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png